Ever since I was looking at the G13, I was really looking for an extra screen to put on/near the keyboard. I wanted a way to show updates from various things running the background, especially around my workflow at the time, which consisted of multiple (Linux desktop) workspaces each in charge of a single task. Based on which workspace I was in, I would be working on a different problem, but would want to know about updates from other workspaces, for when builds or tests finished, or the workspace was otherwise ready or needed my attention. In pursuit of that, I was looking at the fancier options like the Optimus line, but wasn't impressed enough to shell out thousands of dollars. Around that time though, I ran into the Razer line of products, which at the time included a keyboard and a laptop that had an LCD screen to the right of the keyboard with 10 buttons and a touchpad laid on top. It was much cheaper, only a couple hundred dollars, but it was out of production. I signed up for their email list and went about my day. Recently, they sent out an email advertising their keyboards, including the Deathstalker Ultimate, which has the "Synapse 2.0" UI, sometimes referred to as the Switchblade UI, that I was looking for. I ordered it and contacted them for the USB protocol, promising to build something for Linux users to take advantage of, but they have still not responded. In that email, I also offered to sign any NDAs they sent my way, but they haven't responded so I haven't signed any NDAs, and I'm now revoking that offer. In fact, I've reverse-engineered much of the protocol (it's very simple), so here it is: This is based on the Deathstalker Ultimate keyboard, but it should apply to the Blade laptop and anything else marked with "Synapse 2.0" or "Switchblade". There are 4 interfaces when you plug in the keyboard, 3 are HID interfaces and the last is the custom LCD interface:
- HID Mouse
- HID Keyboard -- Non-boot subclass
- HID Keyboard -- Boot subclass
HID Mouse -- Interface 0 -- Endpoint 0x81
This is for the touchpad. It's a HID with boot subclass, which means it can be used by any modern BIOS or operating system (OS) as a mouse without any drivers. By default, when plugged in, it'll act like a mouse, even without the Synapse 2.0 configurator installed or running. If you wrote a program that wanted to intercept the points from this touchpad without it going to the OS, you would have to detach the kernel driver, then start reading HID mouse data from the single interrupt endpoint on this interface. It has an alternate setting, which reports the events in a more verbose way. The same events send more data, but I haven't looked at it much though.
HID non-boot Keyboard -- Endpoint 0x82
This reports presses of the macro keys, the three special buttons at the top (the red M, gaming mode, and the razer-logo icon), and the fn-key. This isn't for any of the fn-key-toggled keys, those go through the normal keyboard in the next interface. Sometimes this reports presses of the 10 screen-backed buttons, but I can't figure out how to get that to happen consistently. This is how they are reported in Windows via the Synapse program, but I can't always get them via libusb.
HID boot-ready Keyboard -- Endpoint 0x83
This is for the rest of the keyboard, the normal keys and the fn-key-toggled keys (print screen and friends on the top row.
Vendor specific -- Endpoint 0x01 and 0x02
These are the two endpoints for writing to the two screens behind the right side of the keyboard. Endpoint 0x01 is under the touchpad, 0x02 is under the buttons on the top. The protocol is to send two URBs, the first is a header and the second is the actual data.
The header is 6 16-bit words:
- command: 1 for "draw rectangle". I never saw any other commands.
- rectangle left x, 0-indexed.
- rectangle top y
- rectangle right x
- rectangle bottom y
- checksum (XOR of previous 5 bytes, weak checksum)
The data is rather straight forward. It's a 5-6-5 format RGB data block, its length should be width * height * 2 bytes, where width = right x - left x + 1 and height = bottom y - top y + 1. Here is the reasoning for that '+ 1', since it bit me a few times:
Left x is the first pixel to draw to, and right x is the last pixel to draw to. For a 1-width rectangle, that would make left x = right x, but the width is 1, so right x - left x = 0, showing the off-by-one error there.
That's it! You can send commands to the keyboard to write to the LCD using that data. In the next post, I'll write about my implementation of it. I'm using cairo (through python-cairoffi) and python-libusb1 to draw and send images to the keyboard, similar to how I implemented the G13's interface. I plan on combining them.