2026-10-17 03:53:51 +02:00
|
|
|
// gKeyMapper daemon - per-model raw HID report tables.
|
|
|
|
|
//
|
|
|
|
|
// Ported from the byte tables of the Python reference project
|
|
|
|
|
// (g910_gkeys/data_mappers/supported_devices.py). Keys are matched against the
|
|
|
|
|
// exact bytes read from the keyboard's vendor interface.
|
|
|
|
|
#ifndef GKEYMAPPER_DAEMON_DEVICE_TABLES_H
|
|
|
|
|
#define GKEYMAPPER_DAEMON_DEVICE_TABLES_H
|
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace gkeymapper::daemon {
|
|
|
|
|
|
|
|
|
|
// A raw HID report from the vendor interface.
|
|
|
|
|
using Report = std::vector<uint8_t>;
|
|
|
|
|
|
2026-10-18 03:50:33 +02:00
|
|
|
// One media key: the bit it has in the media report and the Linux input code it
|
|
|
|
|
// is replayed as.
|
|
|
|
|
struct MediaKey {
|
|
|
|
|
uint8_t bit = 0;
|
|
|
|
|
uint16_t key = 0; // Linux KEY_* code
|
|
|
|
|
const char* name = ""; // for the log line
|
|
|
|
|
};
|
|
|
|
|
|
2026-10-17 03:53:51 +02:00
|
|
|
struct DeviceProfile {
|
|
|
|
|
const char* name = "";
|
|
|
|
|
uint16_t vendor = 0;
|
|
|
|
|
uint16_t product = 0;
|
|
|
|
|
int configuration = 0; // 0-based configuration index
|
|
|
|
|
int interfaceNumber = 1;
|
|
|
|
|
int altSetting = 0;
|
|
|
|
|
|
2026-10-18 03:50:33 +02:00
|
|
|
// Media keys (mute / volume / play) arrive as a short report: this prefix
|
|
|
|
|
// byte followed by one bit per key, 0 when they are all released. 0 means
|
|
|
|
|
// the model reports none. This is the only path the keyboard offers for
|
|
|
|
|
// those keys, so while the daemon holds the interface the kernel does not see
|
|
|
|
|
// them at all — the daemon replays them through its virtual keyboard.
|
|
|
|
|
uint8_t mediaPrefix = 0;
|
|
|
|
|
std::vector<MediaKey> mediaKeys;
|
|
|
|
|
|
|
|
|
|
// gKeyReports[i] is the report that has the bit of G(i+1) set. Both the
|
|
|
|
|
// shared header bytes and the bit of each key are derived from these, so a
|
|
|
|
|
// new model only needs its table (see decode_gkey_state).
|
2026-10-17 03:53:51 +02:00
|
|
|
std::vector<Report> gKeyReports;
|
|
|
|
|
|
|
|
|
|
// Some models must be told to stop mapping G-keys to F-keys first.
|
|
|
|
|
bool disableBinding = false;
|
|
|
|
|
std::vector<Report> disableCommands;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const std::vector<DeviceProfile>& profiles();
|
|
|
|
|
|
|
|
|
|
// Matching profile for vendor/product, or nullptr.
|
|
|
|
|
const DeviceProfile* find_profile(uint16_t vendor, uint16_t product);
|
|
|
|
|
|
2026-10-18 03:50:33 +02:00
|
|
|
// Decodes a macro-key report into the keys it reports as pressed: bit 0 = G1 ..
|
|
|
|
|
// bit 8 = G9. Returns false for every other report — the media keys have their
|
|
|
|
|
// own decoder below, and memory keys, LED and command acknowledgements are
|
|
|
|
|
// ignored on purpose.
|
|
|
|
|
//
|
|
|
|
|
// The report carries the state of all G keys, not a single key: one report with
|
|
|
|
|
// several bits set means a chord, and "no bit set" is the release. The caller
|
|
|
|
|
// compares consecutive states, which makes a held key (repeating report) fire
|
|
|
|
|
// once and a chord fire every key it contains.
|
|
|
|
|
bool decode_gkey_state(const DeviceProfile& profile, const uint8_t* data, int length,
|
|
|
|
|
uint32_t& pressed);
|
2026-10-17 03:53:51 +02:00
|
|
|
|
2026-10-18 03:50:33 +02:00
|
|
|
// Decodes a media-key report into its bits (bit set = key held), using the bit
|
|
|
|
|
// table of the profile. `bits` is 0 for the release report, which is a valid
|
|
|
|
|
// media report. Returns false for every other report, in particular for the
|
|
|
|
|
// macro-key reports.
|
|
|
|
|
bool decode_media_key(const DeviceProfile& profile, const uint8_t* data, int length,
|
|
|
|
|
uint32_t& bits);
|
2026-10-17 03:53:51 +02:00
|
|
|
|
|
|
|
|
} // namespace gkeymapper::daemon
|
|
|
|
|
|
|
|
|
|
#endif // GKEYMAPPER_DAEMON_DEVICE_TABLES_H
|