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 <git@danielschaefer.me>
This commit is contained in:
Daniel Schaefer 2022-12-28 04:15:56 +08:00 committed by Daniel Schaefer
parent b1098f2be8
commit 9030165550
16 changed files with 211 additions and 1 deletions

View File

@ -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"
}
}
}

View File

@ -0,0 +1,12 @@
{
"keycodes": {
"0x00E8": {
"group": "media",
"key": "KC_AIRPLANE_MODE",
"label": "Toggle Airplane Mode",
"aliases": [
"KC_AIRP"
]
}
}
}

View File

@ -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 |✔ | |✔ |
<sup>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.</sup><br/>
<sup>2. Treated as F13-F15.</sup><br/>

View File

@ -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

View File

@ -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

View File

@ -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
*/

View File

@ -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)

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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));

View File

@ -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;

View File

@ -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

View File

@ -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