I try to get the extra keys of the Steelseries APEX 300 working. The main keys and the standard special keys working correctly.

At the end I want to write a small app (C/C++ Qt) to assign some commands to these keys.

For that I do some research:

  1. look for the device:

    $ lsusb
    Bus 003 Device 004: ID 1038:1208 SteelSeries ApS 
  2. look into the kernel log:

    [ 1173.630363] usb 3-1.1: new full-speed USB device number 13 using xhci_hcd
    [ 1173.748310] usb 3-1.1: New USB device found, idVendor=1038, idProduct=1208
    [ 1173.748314] usb 3-1.1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
    [ 1173.748316] usb 3-1.1: Product: SteelSeries Apex 300 Gaming Keyboard
    [ 1173.748317] usb 3-1.1: Manufacturer: SteelSeries
    [ 1173.750546]  0003:1038:1208.000D: hiddev0,hidraw0: USB HID v1.11 Device [SteelSeries SteelSeries Apex 300 Gaming Keyboard] on usb-0000:00:14.0-1.1/input0
    [ 1173.751418] input: SteelSeries SteelSeries Apex 300 Gaming Keyboard as /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.1/3-1.1:1.1/0003:1038:1208.000E/input/input32
    [ 1173.810811] hid-generic 0003:1038:1208.000E: input,hidraw2: USB HID v1.11 Keyboard [SteelSeries SteelSeries Apex 300 Gaming Keyboard] on usb-0000:00:14.0-1.1/input1
    [ 1173.812405] input: SteelSeries SteelSeries Apex 300 Gaming Keyboard as /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1.1/3-1.1:1.2/0003:1038:1208.000F/input/input33
    [ 1173.870645] hid-generic 0003:1038:1208.000F: input,hidraw3: USB HID v1.11 Device [SteelSeries SteelSeries Apex 300 Gaming Keyboard] on usb-0000:00:14.0-1.1/input2
  3. I saw, that hidraw0, hidraw2 and hidraw3 there added. So I look at finally all hidraw* for their output and found out using sudo cat:

    • hidraw0 = ?
    • hidraw1 = mouse
    • hidraw2 = std keyboard keys
    • hidraw3 = special function keys (volume...)
    • hidraw4 = mouse
    • hidraw5 = ?

It may possible that hidraw0 is the key.

  1. In /dev/input/by-id/ I found two fitting entries regarding the APEX:

    $ls /dev/input/by-id/usb-SteelSeries_SteelSeries_Apex_300_*

    Here I try to use cat again:

    • *-event-if02: nothing
    • *-if01-event-kbd: standard keyboard inputs

I found one post (Recognise extra keyboard keys: Steelseries Apex) dealing with this keyboard but my Haskell knowledge is too poor. I only understand that the APEX need to receive a command to enable the keys.

Can you give me some hints where to start?


Just browsing through the linked Haskell code:

put (CmdEnableExtraKeys) = do
  put32 [0x02, 0x00, 0x02]

is the report you need to send (3 bytes),

withDevice 0x1038 [0x1200, 0x1202, 0x1208] $ \dev -> do
  withDeviceHandle dev $ \devHndl ->
  withDetachedKernelDriver devHndl 0 $
  withClaimedInterface devHndl 0 $ do ...

finds a device with vendor id 1038 and device ids 1200, 1202, 1208, detaches the kernel driver, and works on it.

apexCtl :: DeviceHandle -> ByteString -> IO ()
apexCtl devHndl d = do
  writeControlExact devHndl controlSetup d noTimeout

does the actual writing; writeControlExact is from System.USB, does a USB Control write, and therefore seems to bypass the HID layer. That is a guess as I am not familiar with this library.

So I'd either try to get the Haskell program running (instructions are on the git readme you linked), or perform that USB control write with whatever you are more familiar.

BTW, you can use evtest to better understand what happens on the input layer, and look at the HID reports to understand the HID layer with

mount -t debugfs none /sys/kernel/debug
cat /sys/kernel/debug/hid/YOUR_DEVICE/rdesc

Apex-Macros is another program to enable the extra keys. It's written in C and may be more clear if you're unused to Haskell.

I suspect this functionality should be added to the kernel driver as a device quirk.

What works with this keyboard at present is basic functionality of the extra keys. The ApexCtrl and Apex-Macros programs simply send the keyboard a command to report the keys like normal keys. Once this command is sent, the kernel receives scancodes for the L/M/MX keys and can be defined normally:

  • hwdb can override the mapping of scancodes to specific keycodes
  • XKB can override the mapping of keycodes to specific keysyms
  • applications can be configured to act on those keys

I use an old build of ApexCtl, though compiling failed last time I tried. The combination of basic key definitions in hwdb and custom XKB options allows me to set the extra keys to unused keysyms, which can be macrod to launch any desired program with tools like xbindkeys.

The hwdb that comes with ApexCtl is for an old udev format and needs to be updated. I think Apex-Macros is using the same methods, so ApexCtl's hwdb should be effective with that tool as well. See ApexCtl's documentation for which scancodes belong to which hardware keys, and then you can tweak the definitions accordingly.

# original -- ApexCtl/config/90-apex.hwdb

# new format + my tweaks

With those keycode definitions in hwdb, I use this XKB symbols file to set options as needed. Normally I use the meta-option apexf13 to pull in several options. The actual overrides are done in small options that handle one group of keys at a time, so they can be overridden without affecting the other key groups.

// place in /usr/share/X11/xkb/symbols/apex
// or a custom location for use with setxkbmap -I
// modify /usr/share/X11/xkb/rules/evdev{,.lst,.xml} to use systemwide

// These are intended to be loaded after inet(evdev) to override
// its generic ideas.

// for use with Apex300
// L1-L2 as layout toggles
partial alphanumeric_keys
xkb_symbols "lkeys_grp" {
    key <I156> { [ ISO_Next_Group, ISO_First_Group ] };
    key <I157> { [ ISO_Prev_Group, ISO_Last_Group  ] };
}; // end of "lkeys_grp"

// for use with Apex300
// L1-L2 as VolUp-VolDown
partial alphanumeric_keys
xkb_symbols "lkeys_vol" {
    key <I156> { [ XF86AudioRaiseVolume ] };
    key <I157> { [ XF86AudioLowerVolume ] };
}; // end of "lkeys_vol"

// for use with Apex300
// Combo arrows mute
partial alphanumeric_keys
xkb_symbols "arrows_mute" {
    key <I160> { [ XF86AudioMicMute      ] };
    key <I159> { [ XF86AudioMute         ] };
}; // end of "arrows_mute"

// for use with Apex300
// Combo arrows as browser Back/Forward, shifted Stop/Reload
partial alphanumeric_keys
xkb_symbols "arrows_www" {
    key <I160> { [ XF86Back,    XF86Stop   ] };
    key <I159> { [ XF86Forward, XF86Reload ] };
}; // end of "arrows_www"

// for use with Apex300
// MX1-MX5 as media keys
partial alphanumeric_keys
xkb_symbols "mxkeys_media" {
    key <I171> { [ XF86AudioNext                   ] };
    key <I172> { [ XF86AudioPlay,   XF86AudioPause ] };
    key <I173> { [ XF86AudioPrev                   ] };
    key <I174> { [ XF86AudioStop,   XF86Eject      ] };
    key <I175> { [ XF86AudioRecord                 ] };
}; // end of "mxkeys_media"

// for use with Apex300
// MX1-MX5 as Launch1-5
partial alphanumeric_keys
xkb_symbols "mxkeys_prog" {
    key <I171> { [ XF86Launch1 ] };
    key <I172> { [ XF86Launch2 ] };
    key <I173> { [ XF86Launch3 ] };
    key <I174> { [ XF86Launch4 ] };
    key <I175> { [ XF86Launch5 ] };
}; // end of "mxkeys_prog"

// for use with Apex300
// M1-M4 as F13-F16
partial alphanumeric_keys
xkb_symbols "mkeys1_f13" {
    key <FK13> { [ F13      ] };
    key <FK14> { [ F14      ] };
    key <FK15> { [ F15      ] };
    key <FK16> { [ F16      ] };
}; // end of "mkeys1_f13"

// for use with Apex300
// M5-M8 as F17-F20
partial alphanumeric_keys
xkb_symbols "mkeys5_f17" {
    key <FK17> { [ F17      ] };
    key <FK18> { [ F18      ] };
    key <FK19> { [ F19      ] };
    key <FK20> { [ F20      ] };
}; // end of "mkeys5_f17"

// for use with Apex300
// M9-M12 as F21-F24
partial alphanumeric_keys
xkb_symbols "mkeys9_f21" {
    key <FK21> { [ F21      ] };
    key <FK22> { [ F22      ] };
    key <FK23> { [ F23      ] };
    key <FK24> { [ F24      ] };
}; // end of "mkeys9_f21"

// for use with Apex300
// M1-M12 as F13-F24
partial alphanumeric_keys
xkb_symbols "mkeys_f13" {
    include "apex(mkeys1_f13)"
    include "apex(mkeys5_f17)"
    include "apex(mkeys9_f21)"
}; // end of "mkeys_f13"

// for use with Apex300
// M5-M8 as Launch5-8
partial alphanumeric_keys
xkb_symbols "mkeys5_prog5" {
    key <FK17> { [ XF86Launch5 ] };
    key <FK18> { [ XF86Launch6 ] };
    key <FK19> { [ XF86Launch7 ] };
    key <FK20> { [ XF86Launch8 ] };
}; // end of "mkeys5_prog5"

// for use with Apex300
partial alphanumeric_keys
xkb_symbols "apexf13" {
    include "apex(mkeys_f13)"
    include "apex(lkeys_volmute)"
    include "apex(arrows_mute)"
    include "apex(mxkeys_media)"
}; // end of "apexf13"

// define other options here

Once the modified hwdb and xkb symbols are loaded, you can use them in applications as you'd expect. The only key that isn't configurable is the SteelSeries logo key, which is essentially a hardwired Fn key (briefly documented in XKB's Apex300 geometry).

As an example, I use this config in i3wm to bind the media keys to playerctl:

#$mod+key activates playerctl thru dbus for MPRIS-capable players
bindsym $mod+XF86AudioRaiseVolume exec playerctl volume 0.05+
bindsym $mod+XF86AudioLowerVolume exec playerctl volume 0.05-
bindsym $mod+XF86AudioNext exec playerctl next
bindsym $mod+XF86AudioPrev exec playerctl previous
bindsym $mod+XF86AudioPlay exec playerctl play-pause
bindsym $mod+XF86AudioStop exec playerctl stop
  • (personal note: the geometry file's right. that spacebar is ridiculous.) – quixotic Jan 21 '18 at 22:00
  • Yes. I think this should be done within the Kernel module. Maybe here include/config/hid/steelseries.h and in the corresponding source... I will take a look. – Alex44 Jan 23 '18 at 19:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.