From 9030165550e26483335f6b109834edecbd6f21eb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 28 Dec 2022 04:15:56 +0800 Subject: [PATCH] Implement Airplane Radio Button To toggle the system's "Airplane mode" via HID (disable WiFi and Bluetooth). Add Keycode `KC_AIRPLANE_MODE` with alias `KC_AIRP`. Needs to be enabled with `EXTRAKEY_ENABLE = yes`. Signed-off-by: Daniel Schaefer --- data/constants/keycodes/keycodes_0.0.2.hjson | 96 +++++++++++++++++++ .../keycodes/keycodes_0.0.2_basic.hjson | 12 +++ docs/keycodes.md | 1 + docs/keycodes_basic.md | 1 + quantum/action.c | 3 + quantum/action_code.h | 3 + quantum/keycodes.h | 4 +- quantum/keymap_common.c | 3 + tmk_core/protocol/chibios/usb_main.c | 24 +++++ tmk_core/protocol/host.c | 15 +++ tmk_core/protocol/host.h | 1 + tmk_core/protocol/host_driver.h | 1 + tmk_core/protocol/lufa/lufa.c | 6 ++ tmk_core/protocol/report.h | 6 ++ tmk_core/protocol/usb_descriptor.c | 14 +++ tmk_core/protocol/vusb/vusb.c | 22 +++++ 16 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 data/constants/keycodes/keycodes_0.0.2.hjson create mode 100644 data/constants/keycodes/keycodes_0.0.2_basic.hjson diff --git a/data/constants/keycodes/keycodes_0.0.2.hjson b/data/constants/keycodes/keycodes_0.0.2.hjson new file mode 100644 index 0000000000..7ba1ecf201 --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.2.hjson @@ -0,0 +1,96 @@ +{ + "ranges": { + "0x0000/0x00FF": { + "define": "QK_BASIC" + }, + "0x0100/0x1EFF": { + "define": "QK_MODS" + }, + "0x2000/0x1FFF": { + "define": "QK_MOD_TAP" + }, + "0x4000/0x0FFF": { + "define": "QK_LAYER_TAP" + }, + "0x5000/0x01FF": { + "define": "QK_LAYER_MOD" + }, + "0x5200/0x001F": { + "define": "QK_TO" + }, + "0x5220/0x001F": { + "define": "QK_MOMENTARY" + }, + "0x5240/0x001F": { + "define": "QK_DEF_LAYER" + }, + "0x5260/0x001F": { + "define": "QK_TOGGLE_LAYER" + }, + "0x5280/0x001F": { + "define": "QK_ONE_SHOT_LAYER" + }, + "0x52A0/0x001F": { + "define": "QK_ONE_SHOT_MOD" + }, + "0x52C0/0x001F": { + "define": "QK_LAYER_TAP_TOGGLE" + }, + // 0x52E0/0x001F - UNUSED + // 0x5300/0x02FF - UNUSED + "0x5600/0x00FF": { + "define": "QK_SWAP_HANDS" + }, + "0x5700/0x00FF": { + "define": "QK_TAP_DANCE" + }, + // 0x5800/0x17FF - UNUSED + "0x7000/0x00FF": { + "define": "QK_MAGIC" + }, + "0x7100/0x00FF": { + "define": "QK_MIDI" + }, + "0x7200/0x01FF": { + "define": "QK_SEQUENCER" + }, + "0x7400/0x003F": { + "define": "QK_JOYSTICK" + }, + "0x7440/0x003F": { + "define": "QK_PROGRAMMABLE_BUTTON" + }, + "0x7480/0x003F": { + "define": "QK_AUDIO" + }, + "0x74C0/0x003F": { + "define": "QK_STENO" + }, + // 0x7500/0x01FF - UNUSED + "0x7700/0x007F": { + "define": "QK_MACRO" + }, + // 0x7780/0x007F - UNUSED + "0x7800/0x00FF": { + "define": "QK_LIGHTING" + }, + // 0x7900/0x02FF - UNUSED + "0x7C00/0x01FF": { + "define": "QK_QUANTUM" + }, + "0x7E00/0x00FF": { + "define": "QK_KB" + }, + "0x7F00/0x00FF": { + "define": "QK_USER" + }, + "0x8000/0x7FFF": { + "define": "QK_UNICODE" + } + }, + "keycodes": { + "0x7E00": { + "key": "SAFE_RANGE" + } + } +} \ No newline at end of file diff --git a/data/constants/keycodes/keycodes_0.0.2_basic.hjson b/data/constants/keycodes/keycodes_0.0.2_basic.hjson new file mode 100644 index 0000000000..b5b038d83b --- /dev/null +++ b/data/constants/keycodes/keycodes_0.0.2_basic.hjson @@ -0,0 +1,12 @@ +{ + "keycodes": { + "0x00E8": { + "group": "media", + "key": "KC_AIRPLANE_MODE", + "label": "Toggle Airplane Mode", + "aliases": [ + "KC_AIRP" + ] + } + } +} \ No newline at end of file diff --git a/docs/keycodes.md b/docs/keycodes.md index f660c7f526..7be03748ae 100644 --- a/docs/keycodes.md +++ b/docs/keycodes.md @@ -209,6 +209,7 @@ See also: [Basic Keycodes](keycodes_basic.md) |`KC_BRIGHTNESS_DOWN` |`KC_BRID` |Brightness Down |✔ |✔ |✔ | |`KC_CONTROL_PANEL` |`KC_CPNL` |Open Control Panel |✔ | | | |`KC_ASSISTANT` |`KC_ASST` |Launch Context-Aware Assistant |✔ | | | +|`KC_AIRPLANE_MODE` |`KC_AIRP` |Toggle Airplane Mode |✔ | |✔ | 1. The Linux kernel HID driver recognizes [nearly all keycodes](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-input.c), but the default bindings depend on the DE/WM.
2. Treated as F13-F15.
diff --git a/docs/keycodes_basic.md b/docs/keycodes_basic.md index d2a49100d1..9f8cea5829 100644 --- a/docs/keycodes_basic.md +++ b/docs/keycodes_basic.md @@ -223,6 +223,7 @@ These keycodes are not part of the Keyboard/Keypad usage page. The `SYSTEM_` key |`KC_BRIGHTNESS_DOWN` |`KC_BRID`|Brightness Down | |`KC_CONTROL_PANEL` |`KC_CPNL`|Open Control Panel | |`KC_ASSISTANT` |`KC_ASST`|Launch Assistant | +|`KC_AIRPLANE_MODE` |`KC_AIRP`|Toggle Airplane Mode| ## Number Pad diff --git a/quantum/action.c b/quantum/action.c index abf9834d2f..3ea58daf8b 100644 --- a/quantum/action.c +++ b/quantum/action.c @@ -532,6 +532,9 @@ void process_action(keyrecord_t *record, action_t action) { case PAGE_CONSUMER: host_consumer_send(event.pressed ? action.usage.code : 0); break; + case PAGE_RADIO: + host_radio_send(event.pressed); + break; } break; #endif diff --git a/quantum/action_code.h b/quantum/action_code.h index 58d929016d..7b9230bfa1 100644 --- a/quantum/action_code.h +++ b/quantum/action_code.h @@ -198,11 +198,14 @@ enum mods_codes { enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER, + PAGE_RADIO, }; #define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id)) #define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id)) #define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key) +/* Not setting code, defaulting to action.usage.code = 0 */ +#define ACTION_USAGE_RADIO ACTION(ACT_USAGE, PAGE_RADIO << 10) /** \brief Layer Actions */ diff --git a/quantum/keycodes.h b/quantum/keycodes.h index c013858e78..577917c24f 100644 --- a/quantum/keycodes.h +++ b/quantum/keycodes.h @@ -304,6 +304,7 @@ enum qk_keycode_defines { KC_RIGHT_SHIFT = 0x00E5, KC_RIGHT_ALT = 0x00E6, KC_RIGHT_GUI = 0x00E7, + KC_AIRPLANE_MODE = 0x00E8, SH_TG = 0x56F0, SH_TT = 0x56F1, SH_MON = 0x56F2, @@ -863,6 +864,7 @@ enum qk_keycode_defines { KC_RGUI = KC_RIGHT_GUI, KC_RCMD = KC_RIGHT_GUI, KC_RWIN = KC_RIGHT_GUI, + KC_AIRP = KC_AIRPLANE_MODE, CL_SWAP = MAGIC_SWAP_CONTROL_CAPSLOCK, CL_NORM = MAGIC_UNSWAP_CONTROL_CAPSLOCK, CL_TOGG = MAGIC_TOGGLE_CONTROL_CAPSLOCK, @@ -1306,7 +1308,7 @@ enum qk_keycode_defines { #define IS_INTERNAL_KEYCODE(code) ((code) >= KC_NO && (code) <= KC_TRANSPARENT) #define IS_BASIC_KEYCODE(code) ((code) >= KC_A && (code) <= KC_EXSEL) #define IS_SYSTEM_KEYCODE(code) ((code) >= KC_SYSTEM_POWER && (code) <= KC_SYSTEM_WAKE) -#define IS_MEDIA_KEYCODE(code) ((code) >= KC_AUDIO_MUTE && (code) <= KC_ASSISTANT) +#define IS_MEDIA_KEYCODE(code) ((code) >= KC_AUDIO_MUTE && (code) <= KC_AIRPLANE_MODE) #define IS_MOUSE_KEYCODE(code) ((code) >= KC_MS_UP && (code) <= KC_MS_ACCEL2) #define IS_MODIFIERS_KEYCODE(code) ((code) >= KC_LEFT_CTRL && (code) <= KC_RIGHT_GUI) #define IS_SWAP_HANDS_KEYCODE(code) ((code) >= SH_TG && (code) <= SH_OS) diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c index c4336440f9..3111402ee4 100644 --- a/quantum/keymap_common.c +++ b/quantum/keymap_common.c @@ -64,6 +64,9 @@ action_t action_for_keycode(uint16_t keycode) { case KC_AUDIO_MUTE ... KC_ASSISTANT: action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode)); break; + case KC_AIRPLANE_MODE: + action.code = ACTION_USAGE_RADIO; + break; #endif case KC_MS_UP ... KC_MS_ACCEL2: action.code = ACTION_MOUSEKEY(keycode); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 62a11faff7..4804b6825f 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -959,6 +959,30 @@ void send_extra(report_extra_t *report) { #endif } +void send_radio(report_radio_t *report) { +#ifdef EXTRAKEY_ENABLE + osalSysLock(); + if (usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + if (usbGetTransmitStatusI(&USB_DRIVER, SHARED_IN_EPNUM)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + if (osalThreadSuspendTimeoutS(&(&USB_DRIVER)->epc[SHARED_IN_EPNUM]->in_state->thread, TIME_MS2I(10)) == MSG_TIMEOUT) { + osalSysUnlock(); + return; + } + } + + usbStartTransmitI(&USB_DRIVER, SHARED_IN_EPNUM, (uint8_t *)report, sizeof(report_radio_t)); + osalSysUnlock(); +#endif +} + void send_programmable_button(report_programmable_button_t *report) { #ifdef PROGRAMMABLE_BUTTON_ENABLE osalSysLock(); diff --git a/tmk_core/protocol/host.c b/tmk_core/protocol/host.c index 2c6654e9a6..6d12e56812 100644 --- a/tmk_core/protocol/host.c +++ b/tmk_core/protocol/host.c @@ -159,6 +159,21 @@ void host_consumer_send(uint16_t usage) { (*driver->send_extra)(&report); } +void host_radio_send(bool state) { + if (!driver) return; + + /* It's a toggle button, state==true means to toggle it. + * !state means there's no change. */ + if (!state) return; + + report_radio_t report = { + .report_id = REPORT_ID_RADIO, + .state = (uint8_t)state, + }; + send_radio(&report); +} +__attribute__((weak)) void send_radio(report_radio_t *report) {} + #ifdef JOYSTICK_ENABLE void host_joystick_send(joystick_t *joystick) { if (!driver) return; diff --git a/tmk_core/protocol/host.h b/tmk_core/protocol/host.h index dfa86cd7b5..16f1844d59 100644 --- a/tmk_core/protocol/host.h +++ b/tmk_core/protocol/host.h @@ -47,6 +47,7 @@ void host_keyboard_send(report_keyboard_t *report); void host_mouse_send(report_mouse_t *report); void host_system_send(uint16_t usage); void host_consumer_send(uint16_t usage); +void host_radio_send(bool usage); void host_programmable_button_send(uint32_t data); uint16_t host_last_system_usage(void); diff --git a/tmk_core/protocol/host_driver.h b/tmk_core/protocol/host_driver.h index 7dc6c3d810..9c22bff3fa 100644 --- a/tmk_core/protocol/host_driver.h +++ b/tmk_core/protocol/host_driver.h @@ -33,3 +33,4 @@ typedef struct { void send_joystick(report_joystick_t *report); void send_digitizer(report_digitizer_t *report); void send_programmable_button(report_programmable_button_t *report); +void send_radio(report_radio_t *report); diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 8f36e02b9a..cdcdea9d64 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -612,6 +612,12 @@ void send_programmable_button(report_programmable_button_t *report) { #endif } +void send_radio(report_radio_t *report) { +#ifdef EXTRAKEY_ENABLE + send_report(SHARED_IN_EPNUM, report, sizeof(report_radio_t)); +#endif +} + void send_digitizer(report_digitizer_t *report) { #ifdef DIGITIZER_ENABLE send_report(DIGITIZER_IN_EPNUM, report, sizeof(report_digitizer_t)); diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index e4526e4ee6..54be534f7b 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -29,6 +29,7 @@ enum hid_report_ids { REPORT_ID_MOUSE, REPORT_ID_SYSTEM, REPORT_ID_CONSUMER, + REPORT_ID_RADIO, REPORT_ID_PROGRAMMABLE_BUTTON, REPORT_ID_NKRO, REPORT_ID_JOYSTICK, @@ -196,6 +197,11 @@ typedef struct { uint16_t usage; } __attribute__((packed)) report_extra_t; +typedef struct { + uint8_t report_id; + uint8_t state; +} __attribute__((packed)) report_radio_t; + typedef struct { uint8_t report_id; uint32_t usage; diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 02f1c089d3..b0135dea0a 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -320,6 +320,20 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { HID_RI_REPORT_SIZE(8, 16), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_RI_END_COLLECTION(0), + + HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop + HID_RI_USAGE(8, 0x0C), // Wireless Radio Controls + HID_RI_COLLECTION(8, 0x01), // Application + HID_RI_REPORT_ID(8, REPORT_ID_RADIO), + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_USAGE(8, 0xC6), // Wireless Radio Button + HID_RI_REPORT_COUNT(8, 1), + HID_RI_REPORT_SIZE(8, 1), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_RI_REPORT_SIZE(8, 7), + HID_RI_INPUT(8, HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_END_COLLECTION(0), #endif #ifdef PROGRAMMABLE_BUTTON_ENABLE diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 8816ff44c4..96b43e1440 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -303,6 +303,14 @@ void send_programmable_button(report_programmable_button_t *report) { #endif } +void send_radio(report_radio_t *report) { +#ifdef EXTRAKEY_ENABLE + if (usbInterruptIsReadyShared()) { + usbSetInterruptShared((void *)report, sizeof(report_radio_t)); + } +#endif +} + /*------------------------------------------------------------------* * Request from host * *------------------------------------------------------------------*/ @@ -536,6 +544,20 @@ const PROGMEM uchar shared_hid_report[] = { 0x75, 0x10, // Report Size (16) 0x81, 0x00, // Input (Data, Array, Absolute) 0xC0, // End Collection + + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x0C, // USAGE (Wireless Radio Controls) + 0xA1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_RADIO, // Report ID (Radio) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x09, 0xC6, // Usage (Wireless Radio Button) + 0x95, 0x01, // Report Count (1) + 0x75, 0x01, // Report Size (1) + 0x81, 0x06, // Input (Data, Variable, Relative) + 0x75, 0x07, // Report Size (7) + 0x81, 0x03, // INPUT (Constant, Variable, Absolute) + 0xC0, // End Collection #endif #ifdef JOYSTICK_ENABLE