Updates and bugfixes

- Make MacOS multi-desktop workaround more reliable
  - Partial media keys support
This commit is contained in:
Hrvoje Cavrak 2024-05-13 13:53:38 +02:00
parent 35002c90eb
commit 2d93c01421
12 changed files with 119 additions and 11 deletions

23
.clang-format Normal file
View File

@ -0,0 +1,23 @@
BasedOnStyle: LLVM
BinPackParameters: 'false'
BinPackArguments: 'false'
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: 'true'
AlignConsecutiveAssignments: 'true'
AlignConsecutiveDeclarations: 'false'
AlignEscapedNewlines: Left
AlignOperands: 'true'
AlignTrailingComments: 'true'
AllowAllArgumentsOnNextLine: 'true'
AllowShortFunctionsOnASingleLine: 'false'
BreakBeforeBinaryOperators: 'All'
ColumnLimit: '110'
IndentWidth: '4'
IndentCaseLabels: 'true'
IndentWrappedFunctionNames: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'true'
MaxEmptyLinesToKeep: '2'
PointerAlignment: Right
ReflowComments: 'true'
Standard: Cpp11
UseTab: Never

Binary file not shown.

Binary file not shown.

View File

@ -231,6 +231,11 @@ void handle_output_config_msg(uart_packet_t *packet, device_t *state) {
save_config(state); save_config(state);
} }
/* Process consumer control keyboard message. Send immediately, w/o queing */
void handle_consumer_control_msg(uart_packet_t *packet, device_t *state) {
tud_hid_n_report(0, REPORT_ID_CONSUMER, &packet->data[0], CONSUMER_CONTROL_LENGTH);
}
/**==================================================== * /**==================================================== *
* ============== Output Switch Routines ============ * * ============== Output Switch Routines ============ *
* ==================================================== */ * ==================================================== */

View File

@ -74,6 +74,14 @@ typedef struct {
bool uses_report_id; bool uses_report_id;
} mouse_t; } mouse_t;
/* Defines information about HID report format for the keyboard. */
typedef struct {
uint8_t keyboard_report_id;
uint8_t consumer_report_id;
uint8_t system_report_id;
uint8_t protocol;
} keyboard_t;
/* For each element type we're interested in there is an entry /* For each element type we're interested in there is an entry
in an array of these, defining its usage and in case matched, where to in an array of these, defining its usage and in case matched, where to
store the data. */ store the data. */

View File

@ -181,6 +181,16 @@ void send_key(hid_keyboard_report_t *report, device_t *state) {
} }
} }
/* Decide if consumer control reports go local or to the other board */
void send_consumer_control(uint8_t *raw_report, device_t *state) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
tud_hid_n_report(0, REPORT_ID_CONSUMER, raw_report, CONSUMER_CONTROL_LENGTH);
state->last_activity[BOARD_ROLE] = time_us_64();
} else {
send_packet((uint8_t *)raw_report, CONSUMER_CONTROL_MSG, CONSUMER_CONTROL_LENGTH);
}
}
/* ==================================================== * /* ==================================================== *
* Parse and interpret the keys pressed on the keyboard * Parse and interpret the keys pressed on the keyboard
* ==================================================== */ * ==================================================== */
@ -218,3 +228,20 @@ void process_keyboard_report(uint8_t *raw_report, int length, device_t *state) {
/* This method will decide if the key gets queued locally or sent through UART */ /* This method will decide if the key gets queued locally or sent through UART */
send_key(keyboard_report, state); send_key(keyboard_report, state);
} }
void process_consumer_report(uint8_t *raw_report, int length, device_t *state) {
uint8_t new_report[CONSUMER_CONTROL_LENGTH] = {0};
/* We expect length not to be zero or bail out */
if (!length)
return;
/* Consumer control report ID rewrite and forward */
if (raw_report[0] && raw_report[0] == global_state.kbd_dev.consumer_report_id) {
for (int i = 0; i < length - 1 || i < CONSUMER_CONTROL_LENGTH; i++) {
new_report[i] = raw_report[i + 1];
}
send_consumer_control(new_report, &global_state);
}
}

View File

@ -109,6 +109,7 @@ enum packet_type_e {
SWAP_OUTPUTS_MSG = 12, SWAP_OUTPUTS_MSG = 12,
HEARTBEAT_MSG = 13, HEARTBEAT_MSG = 13,
OUTPUT_CONFIG_MSG = 14, OUTPUT_CONFIG_MSG = 14,
CONSUMER_CONTROL_MSG = 15,
}; };
/* /*
@ -142,9 +143,10 @@ typedef struct {
#define KBD_QUEUE_LENGTH 128 #define KBD_QUEUE_LENGTH 128
#define MOUSE_QUEUE_LENGTH 2048 #define MOUSE_QUEUE_LENGTH 2048
#define KEYS_IN_USB_REPORT 6 #define KEYS_IN_USB_REPORT 6
#define KBD_REPORT_LENGTH 8 #define KBD_REPORT_LENGTH 8
#define MOUSE_REPORT_LENGTH 7 #define MOUSE_REPORT_LENGTH 7
#define CONSUMER_CONTROL_LENGTH 4
/********* Screen **********/ /********* Screen **********/
#define MIN_SCREEN_COORD 0 #define MIN_SCREEN_COORD 0
@ -260,6 +262,7 @@ typedef struct {
config_t config; // Device configuration, loaded from flash or defaults used config_t config; // Device configuration, loaded from flash or defaults used
mouse_t mouse_dev; // Mouse device specifics, e.g. stores locations for keys in report mouse_t mouse_dev; // Mouse device specifics, e.g. stores locations for keys in report
keyboard_t kbd_dev; // Keyboard device specifics, like report IDs
queue_t kbd_queue; // Queue that stores keyboard reports queue_t kbd_queue; // Queue that stores keyboard reports
queue_t mouse_queue; // Queue that stores mouse reports queue_t mouse_queue; // Queue that stores mouse reports
@ -288,11 +291,13 @@ void core1_main(void);
/********* Keyboard **********/ /********* Keyboard **********/
bool check_specific_hotkey(hotkey_combo_t, const hid_keyboard_report_t *); bool check_specific_hotkey(hotkey_combo_t, const hid_keyboard_report_t *);
void process_keyboard_report(uint8_t *, int, device_t *); void process_keyboard_report(uint8_t *, int, device_t *);
void process_consumer_report(uint8_t *, int, device_t *);
void release_all_keys(device_t *); void release_all_keys(device_t *);
void queue_kbd_report(hid_keyboard_report_t *, device_t *); void queue_kbd_report(hid_keyboard_report_t *, device_t *);
void process_kbd_queue_task(device_t *); void process_kbd_queue_task(device_t *);
void send_key(hid_keyboard_report_t *, device_t *); void send_key(hid_keyboard_report_t *, device_t *);
bool key_in_report(uint8_t, const hid_keyboard_report_t *); bool key_in_report(uint8_t, const hid_keyboard_report_t *);
void send_consumer_control(uint8_t *, device_t *);
/********* Mouse **********/ /********* Mouse **********/
bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel); bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel);
@ -355,6 +360,7 @@ void handle_flash_led_msg(uart_packet_t *, device_t *);
void handle_fw_upgrade_msg(uart_packet_t *, device_t *); void handle_fw_upgrade_msg(uart_packet_t *, device_t *);
void handle_wipe_config_msg(uart_packet_t *, device_t *); void handle_wipe_config_msg(uart_packet_t *, device_t *);
void handle_screensaver_msg(uart_packet_t *, device_t *); void handle_screensaver_msg(uart_packet_t *, device_t *);
void handle_consumer_control_msg(uart_packet_t *, device_t *);
void switch_output(device_t *, uint8_t); void switch_output(device_t *, uint8_t);

View File

@ -139,9 +139,9 @@ void switch_desktop(device_t *state, output_t *output, int new_index, int direct
switch (output->os) { switch (output->os) {
case MACOS: case MACOS:
/* Once doesn't seem reliable enough, do it twice */ /* Once isn't reliable enough, but repeating it does the trick */
output_mouse_report(&move_relative_one, state); for (int move_cnt=0; move_cnt<5; move_cnt++)
output_mouse_report(&move_relative_one, state); output_mouse_report(&move_relative_one, state);
break; break;
case WINDOWS: case WINDOWS:

View File

@ -57,6 +57,7 @@ const uart_handler_t uart_handler[] = {
{.type = SCREENSAVER_MSG, .handler = handle_screensaver_msg}, {.type = SCREENSAVER_MSG, .handler = handle_screensaver_msg},
{.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg}, {.type = WIPE_CONFIG_MSG, .handler = handle_wipe_config_msg},
{.type = OUTPUT_CONFIG_MSG, .handler = handle_output_config_msg}, {.type = OUTPUT_CONFIG_MSG, .handler = handle_output_config_msg},
{.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg},
}; };
void process_packet(uart_packet_t *packet, device_t *state) { void process_packet(uart_packet_t *packet, device_t *state) {

View File

@ -87,6 +87,7 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
switch (itf_protocol) { switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD: case HID_ITF_PROTOCOL_KEYBOARD:
global_state.keyboard_connected = false; global_state.keyboard_connected = false;
memset(&global_state.kbd_dev, 0, sizeof(global_state.kbd_dev));
break; break;
case HID_ITF_PROTOCOL_MOUSE: case HID_ITF_PROTOCOL_MOUSE:
@ -99,7 +100,9 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
} }
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) { void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
tuh_hid_report_info_t report_info[MAX_REPORT_ITEMS] = {0};
tuh_hid_report_info_t *info;
switch (itf_protocol) { switch (itf_protocol) {
case HID_ITF_PROTOCOL_KEYBOARD: case HID_ITF_PROTOCOL_KEYBOARD:
@ -120,13 +123,25 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
at least we get all the information we need (looking at you, mouse wheel) */ at least we get all the information we need (looking at you, mouse wheel) */
if (tuh_hid_get_protocol(dev_addr, instance) == HID_PROTOCOL_BOOT) { if (tuh_hid_get_protocol(dev_addr, instance) == HID_PROTOCOL_BOOT) {
tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT); tuh_hid_set_protocol(dev_addr, instance, HID_PROTOCOL_REPORT);
} }
global_state.mouse_dev.protocol = tuh_hid_get_protocol(dev_addr, instance); global_state.mouse_dev.protocol = tuh_hid_get_protocol(dev_addr, instance);
parse_report_descriptor(&global_state.mouse_dev, MAX_REPORTS, desc_report, desc_len); parse_report_descriptor(&global_state.mouse_dev, MAX_REPORTS, desc_report, desc_len);
global_state.mouse_connected = true; global_state.mouse_connected = true;
break; break;
case HID_ITF_PROTOCOL_NONE:
uint8_t num_parsed
= tuh_hid_parse_report_descriptor(&report_info[0], MAX_REPORT_ITEMS, desc_report, desc_len);
for (int report_num = 0; report_num < num_parsed; report_num++) {
info = &report_info[report_num];
if (info->usage == HID_USAGE_CONSUMER_CONTROL && info->usage_page == HID_USAGE_PAGE_CONSUMER)
global_state.kbd_dev.consumer_report_id = info->report_id;
}
break;
} }
/* Flash local led to indicate a device was connected */ /* Flash local led to indicate a device was connected */
blink_led(&global_state); blink_led(&global_state);
@ -134,7 +149,7 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
/* Also signal the other board to flash LED, to enable easy verification if serial works */ /* Also signal the other board to flash LED, to enable easy verification if serial works */
send_value(ENABLE, FLASH_LED_MSG); send_value(ENABLE, FLASH_LED_MSG);
/* Kick off the report querying */ /* Kick off the report querying */
tuh_hid_receive_report(dev_addr, instance); tuh_hid_receive_report(dev_addr, instance);
} }
@ -150,6 +165,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
case HID_ITF_PROTOCOL_MOUSE: case HID_ITF_PROTOCOL_MOUSE:
process_mouse_report((uint8_t *)report, len, &global_state); process_mouse_report((uint8_t *)report, len, &global_state);
break; break;
case HID_ITF_PROTOCOL_NONE:
process_consumer_report((uint8_t *)report, len, &global_state);
break;
} }
/* Continue requesting reports */ /* Continue requesting reports */

View File

@ -62,7 +62,9 @@ uint8_t const *tud_descriptor_device_cb(void) {
// Relative mouse is used to overcome limitations of multiple desktops on MacOS and Windows // Relative mouse is used to overcome limitations of multiple desktops on MacOS and Windows
uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)), uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))}; TUD_HID_REPORT_DESC_ABSMOUSE(HID_REPORT_ID(REPORT_ID_MOUSE)),
TUD_HID_REPORT_DESC_CONSUMER_CTRL(HID_REPORT_ID(REPORT_ID_CONSUMER))
};
uint8_t const desc_hid_report_relmouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_RELMOUSE))}; uint8_t const desc_hid_report_relmouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_RELMOUSE))};

View File

@ -29,7 +29,8 @@ enum
{ {
REPORT_ID_KEYBOARD = 1, REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE, REPORT_ID_MOUSE,
REPORT_ID_COUNT REPORT_ID_COUNT,
REPORT_ID_CONSUMER
}; };
enum enum
@ -86,4 +87,20 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_COLLECTION_END , \ HID_COLLECTION_END , \
HID_COLLECTION_END \ HID_COLLECTION_END \
// Consumer Control Report Descriptor Template
#define TUD_HID_REPORT_DESC_CONSUMER_CTRL(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ) ,\
HID_USAGE ( HID_USAGE_CONSUMER_CONTROL ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
HID_LOGICAL_MIN ( 0x01 ) ,\
HID_LOGICAL_MAX_N( 0x0FFF, 2 ) ,\
HID_USAGE_MIN ( 0x01 ) ,\
HID_USAGE_MAX_N ( 0x0FFF, 2 ) ,\
HID_REPORT_SIZE ( 16 ) ,\
HID_REPORT_COUNT ( 2 ) ,\
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
HID_COLLECTION_END \
#endif /* USB_DESCRIPTORS_H_ */ #endif /* USB_DESCRIPTORS_H_ */