Media keys working again

This commit is contained in:
2026-10-18 03:50:33 +02:00
parent 726a7ae92d
commit 798708b3e7
20 changed files with 590 additions and 112 deletions

View File

@@ -14,6 +14,14 @@ namespace gkeymapper::daemon {
// A raw HID report from the vendor interface.
using Report = std::vector<uint8_t>;
// 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
};
struct DeviceProfile {
const char* name = "";
uint16_t vendor = 0;
@@ -22,9 +30,18 @@ struct DeviceProfile {
int interfaceNumber = 1;
int altSetting = 0;
// gKeyReports[i] is the press report for G(i+1).
// 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).
std::vector<Report> gKeyReports;
Report releaseReport;
// Some models must be told to stop mapping G-keys to F-keys first.
bool disableBinding = false;
@@ -36,11 +53,24 @@ const std::vector<DeviceProfile>& profiles();
// Matching profile for vendor/product, or nullptr.
const DeviceProfile* find_profile(uint16_t vendor, uint16_t product);
// Index of the G key (0..N-1) for a report, or -1 if it is not a known press.
int match_gkey(const DeviceProfile& profile, const uint8_t* data, int length);
// 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);
// True if the report is the "all macro keys released" report.
bool is_release_report(const DeviceProfile& profile, const uint8_t* data, int length);
// 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);
} // namespace gkeymapper::daemon