Features and fixes

- Auto switch-lock when dragging or resizing windows.
     * This was super annoying for me, resizing a window and you
       get too close to the left border, making it shrink suddenly.
       Now if you drag while holding a mouse button pressed, it will
       not switch.

- Added Mac OS multiple desktop workaround
     * you need to tweak defaults.c and set screen_count to e.g. 2 if
       you have 2 desktops on a single mac output, and set OUTPUT_A_OS
       (or OUTPUT_B_OS) to MACOS in user_config.h, then rebuild
     * This will be keyboard-configurable in the future without rebuilding
     * Support still experimental

- HID report protocol default for port B
- Added support for swapping output order
     * defaults.c, output has "pos" you can set to either LEFT or RIGHT
     * this will be hotkey-configurable

- Bugfixes
- Added debugging mode (serial CDC device for printf things)
- Refactored screensaver a bit
This commit is contained in:
Hrvoje Cavrak 2024-03-22 19:48:49 +01:00
parent f0b36569c9
commit 9c69dc3cd6
14 changed files with 371 additions and 338 deletions

View File

@ -14,8 +14,16 @@ const config_t default_config = {
.bottom = MAX_SCREEN_COORD, .bottom = MAX_SCREEN_COORD,
}, },
.screen_count = 1, .screen_count = 1,
.screen_index = 0, .screen_index = 1,
}, .os = OUTPUT_A_OS,
.pos = LEFT,
.screensaver = {
.enabled = SCREENSAVER_A_ENABLED,
.only_if_inactive = SCREENSAVER_A_ONLY_IF_INACTIVE,
.idle_time_us = SCREENSAVER_A_IDLE_TIME_SEC * 1000000,
.max_time_us = SCREENSAVER_A_MAX_TIME_SEC * 1000000,
}
},
.output[OUTPUT_B] = .output[OUTPUT_B] =
{ {
.number = OUTPUT_B, .number = OUTPUT_B,
@ -26,20 +34,14 @@ const config_t default_config = {
.bottom = MAX_SCREEN_COORD, .bottom = MAX_SCREEN_COORD,
}, },
.screen_count = 1, .screen_count = 1,
.screen_index = 0, .screen_index = 1,
.os = OUTPUT_B_OS,
.pos = RIGHT,
.screensaver = {
.enabled = SCREENSAVER_B_ENABLED,
.only_if_inactive = SCREENSAVER_B_ONLY_IF_INACTIVE,
.idle_time_us = SCREENSAVER_B_IDLE_TIME_SEC * 1000000,
.max_time_us = SCREENSAVER_B_MAX_TIME_SEC * 1000000,
}
}, },
.screensaver[OUTPUT_A] = };
{
.enabled = SCREENSAVER_A_ENABLED,
.only_if_inactive = SCREENSAVER_A_ONLY_IF_INACTIVE,
.idle_time_us = SCREENSAVER_A_IDLE_TIME_SEC * 1000000,
.max_time_us = SCREENSAVER_A_MAX_TIME_SEC * 1000000,
},
.screensaver[OUTPUT_B] =
{
.enabled = SCREENSAVER_B_ENABLED,
.only_if_inactive = SCREENSAVER_B_ONLY_IF_INACTIVE,
.idle_time_us = SCREENSAVER_B_IDLE_TIME_SEC * 1000000,
.max_time_us = SCREENSAVER_B_MAX_TIME_SEC * 1000000,
},
};

View File

@ -31,18 +31,24 @@ void output_toggle_hotkey_handler(device_t *state) {
switch_output(state, state->active_output); switch_output(state, state->active_output);
}; };
/* This key combo records switch y top coordinate for different-size monitors */ void get_border_position(device_t *state, border_size_t *border) {
void screen_border_hotkey_handler(device_t *state) {
border_size_t *border = &state->config.output[state->active_output].border;
/* To avoid having 2 different keys, if we're above half, it's the top coord */ /* To avoid having 2 different keys, if we're above half, it's the top coord */
if (state->mouse_y > (MAX_SCREEN_COORD / 2)) if (state->mouse_y > (MAX_SCREEN_COORD / 2))
border->bottom = state->mouse_y; border->bottom = state->mouse_y;
else else
border->top = state->mouse_y; border->top = state->mouse_y;
}
/* This key combo records switch y top coordinate for different-size monitors */
void screen_border_hotkey_handler(device_t *state) {
border_size_t *border = &state->config.output[state->active_output].border;
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
get_border_position(state, border);
save_config(state);
}
send_packet((uint8_t *)border, SYNC_BORDERS_MSG, sizeof(border_size_t)); send_packet((uint8_t *)border, SYNC_BORDERS_MSG, sizeof(border_size_t));
save_config(state);
}; };
/* This key combo puts board A in firmware upgrade mode */ /* This key combo puts board A in firmware upgrade mode */
@ -69,8 +75,8 @@ void wipe_config_hotkey_handler(device_t *state) {
} }
void screensaver_hotkey_handler(device_t *state) { void screensaver_hotkey_handler(device_t *state) {
state->config.screensaver[BOARD_ROLE].enabled ^= 1; state->config.output[BOARD_ROLE].screensaver.enabled ^= 1;
send_value(state->config.screensaver[BOARD_ROLE].enabled, SCREENSAVER_MSG); send_value(state->config.output[BOARD_ROLE].screensaver.enabled, SCREENSAVER_MSG);
} }
/* When pressed, toggles the current mouse zoom mode state */ /* When pressed, toggles the current mouse zoom mode state */
@ -87,19 +93,18 @@ void mouse_zoom_hotkey_handler(device_t *state) {
void handle_keyboard_uart_msg(uart_packet_t *packet, device_t *state) { void handle_keyboard_uart_msg(uart_packet_t *packet, device_t *state) {
queue_kbd_report((hid_keyboard_report_t *)packet->data, state); queue_kbd_report((hid_keyboard_report_t *)packet->data, state);
state->last_activity[BOARD_ROLE] = time_us_64(); state->last_activity[BOARD_ROLE] = time_us_64();
state->screensaver_max_time_reached[BOARD_ROLE] = false;
} }
/* Function handles received mouse moves from the other board */ /* Function handles received mouse moves from the other board */
void handle_mouse_abs_uart_msg(uart_packet_t *packet, device_t *state) { void handle_mouse_abs_uart_msg(uart_packet_t *packet, device_t *state) {
mouse_abs_report_t *mouse_report = (mouse_abs_report_t *)packet->data; mouse_report_t *mouse_report = (mouse_report_t *)packet->data;
queue_mouse_report(mouse_report, state); queue_mouse_report(mouse_report, state);
state->mouse_x = mouse_report->x; state->mouse_x = mouse_report->x;
state->mouse_y = mouse_report->y; state->mouse_y = mouse_report->y;
state->mouse_buttons = mouse_report->buttons;
state->last_activity[BOARD_ROLE] = time_us_64(); state->last_activity[BOARD_ROLE] = time_us_64();
state->screensaver_max_time_reached[BOARD_ROLE] = false;
} }
/* Function handles request to switch output */ /* Function handles request to switch output */
@ -135,7 +140,14 @@ void handle_switch_lock_msg(uart_packet_t *packet, device_t *state) {
/* Handle border syncing message that lets the other device know about monitor height offset */ /* Handle border syncing message that lets the other device know about monitor height offset */
void handle_sync_borders_msg(uart_packet_t *packet, device_t *state) { void handle_sync_borders_msg(uart_packet_t *packet, device_t *state) {
border_size_t *border = &state->config.output[state->active_output].border; border_size_t *border = &state->config.output[state->active_output].border;
memcpy(border, packet->data, sizeof(border_size_t));
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
get_border_position(state, border);
send_packet((uint8_t *)border, SYNC_BORDERS_MSG, sizeof(border_size_t));
}
else
memcpy(border, packet->data, sizeof(border_size_t));
save_config(state); save_config(state);
} }
@ -151,7 +163,7 @@ void handle_wipe_config_msg(uart_packet_t *packet, device_t *state) {
} }
void handle_screensaver_msg(uart_packet_t *packet, device_t *state) { void handle_screensaver_msg(uart_packet_t *packet, device_t *state) {
state->config.screensaver[BOARD_ROLE].enabled = packet->data[0]; state->config.output[BOARD_ROLE].screensaver.enabled = packet->data[0];
} }
/**==================================================== * /**==================================================== *

View File

@ -21,7 +21,6 @@
#include "main.h" #include "main.h"
#define IS_BLOCK_END (collection.start == collection.end) #define IS_BLOCK_END (collection.start == collection.end)
#define MAX_BUTTONS 16
enum { SIZE_0_BIT = 0, SIZE_8_BIT = 1, SIZE_16_BIT = 2, SIZE_32_BIT = 3 }; enum { SIZE_0_BIT = 0, SIZE_8_BIT = 1, SIZE_16_BIT = 2, SIZE_32_BIT = 3 };

View File

@ -88,11 +88,11 @@ typedef struct {
uint8_t usage_count; uint8_t usage_count;
uint8_t global_usage; uint8_t global_usage;
uint32_t offset_in_bits; uint32_t offset_in_bits;
uint8_t usages[64]; uint8_t usages[256];
uint8_t *p_usage; uint8_t *p_usage;
collection_t collection; collection_t collection;
usage_map_t *map; usage_map_t *map;
globals_t globals[16]; /* as tag is 4 bits, there can be 16 different tags in global header type */ globals_t globals[16]; /* as tag is 4 bits, there can be 16 different tags in global header type */
} parser_state_t; } parser_state_t;

View File

@ -162,7 +162,6 @@ void send_key(hid_keyboard_report_t *report, device_t *state) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
queue_kbd_report(report, state); queue_kbd_report(report, state);
state->last_activity[BOARD_ROLE] = time_us_64(); state->last_activity[BOARD_ROLE] = time_us_64();
state->screensaver_max_time_reached[BOARD_ROLE] = false;
} else { } else {
send_packet((uint8_t *)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH); send_packet((uint8_t *)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH);
} }

View File

@ -47,6 +47,9 @@
#define ENABLE 1 #define ENABLE 1
#define DISABLE 0 #define DISABLE 0
#define ABSOLUTE 0
#define RELATIVE 1
#define MAX_REPORT_ITEMS 16 #define MAX_REPORT_ITEMS 16
#define MOUSE_BOOT_REPORT_LEN 4 #define MOUSE_BOOT_REPORT_LEN 4
@ -103,6 +106,9 @@ enum packet_type_e {
FLASH_LED_MSG = 9, FLASH_LED_MSG = 9,
SCREENSAVER_MSG = 10, SCREENSAVER_MSG = 10,
WIPE_CONFIG_MSG = 11, WIPE_CONFIG_MSG = 11,
REL_MOUSE_REPORT_MSG = 12,
SWAP_OUTPUTS_MSG = 13,
HEARTBEAT_MSG = 14,
}; };
/* /*
@ -146,7 +152,25 @@ typedef struct {
/********* Configuration storage definitions **********/ /********* Configuration storage definitions **********/
#define CURRENT_CONFIG_VERSION 3 #define CURRENT_CONFIG_VERSION 4
enum os_type_e {
LINUX = 1,
MACOS = 2,
WINDOWS = 3,
OTHER = 255,
};
enum screen_pos_e {
LEFT = 1,
RIGHT = 2,
MIDDLE = 3,
};
enum itf_num_e {
ITF_NUM_HID = 0,
ITF_NUM_HID_REL_M = 1,
};
typedef struct { typedef struct {
int top; // When jumping from a smaller to a bigger screen, go to THIS top height int top; // When jumping from a smaller to a bigger screen, go to THIS top height
@ -154,16 +178,6 @@ typedef struct {
// height // height
} border_size_t; } border_size_t;
/* Define output parameters */
typedef struct {
int number; // Number of this output (e.g. OUTPUT_A = 0 etc)
int screen_count; // How many monitors per output (e.g. Output A is Windows with 3 monitors)
int screen_index; // Current active screen
int speed_x; // Mouse speed per output, in direction X
int speed_y; // Mouse speed per output, in direction Y
border_size_t border; // Screen border size/offset to keep cursor at same height when switching
} output_t;
/* Define screensaver parameters */ /* Define screensaver parameters */
typedef struct { typedef struct {
uint8_t enabled; uint8_t enabled;
@ -172,13 +186,26 @@ typedef struct {
uint64_t max_time_us; uint64_t max_time_us;
} screensaver_t; } screensaver_t;
/* Define output parameters */
typedef struct {
int number; // Number of this output (e.g. OUTPUT_A = 0 etc)
int screen_count; // How many monitors per output (e.g. Output A is Windows with 3 monitors)
int screen_index; // Current active screen
int speed_x; // Mouse speed per output, in direction X
int speed_y; // Mouse speed per output, in direction Y
border_size_t border; // Screen border size/offset to keep cursor at same height when switching
enum os_type_e os; // Operating system on this output
enum screen_pos_e pos; // Screen position on this output
screensaver_t screensaver; // Screensaver parameters for this output
} output_t;
/* Data structure defining how configuration is stored */ /* Data structure defining how configuration is stored */
typedef struct { typedef struct {
uint32_t magic_header; uint32_t magic_header;
uint32_t version; uint32_t version;
uint8_t force_mouse_boot_mode; uint8_t force_mouse_boot_mode;
output_t output[NUM_SCREENS]; output_t output[NUM_SCREENS];
screensaver_t screensaver[NUM_SCREENS]; uint8_t screensaver_enabled;
// Keep checksum at the end of the struct // Keep checksum at the end of the struct
uint32_t checksum; uint32_t checksum;
} config_t; } config_t;
@ -211,8 +238,8 @@ typedef struct TU_ATTR_PACKED {
int16_t x; int16_t x;
int16_t y; int16_t y;
int8_t wheel; int8_t wheel;
int8_t pan; uint8_t mode;
} mouse_abs_report_t; } mouse_report_t;
typedef enum { IDLE, READING_PACKET, PROCESSING_PACKET } receiver_state_t; typedef enum { IDLE, READING_PACKET, PROCESSING_PACKET } receiver_state_t;
@ -222,13 +249,13 @@ typedef struct {
uint8_t keyboard_leds[NUM_SCREENS]; // State of keyboard LEDs (index 0 = A, index 1 = B) uint8_t keyboard_leds[NUM_SCREENS]; // State of keyboard LEDs (index 0 = A, index 1 = B)
uint64_t last_activity[NUM_SCREENS]; // Timestamp of the last input activity (-||-) uint64_t last_activity[NUM_SCREENS]; // Timestamp of the last input activity (-||-)
bool screensaver_max_time_reached[NUM_SCREENS]; // Screensaver maximum time has been reached (will be reset at the next input activity)
receiver_state_t receiver_state; // Storing the state for the simple receiver state machine receiver_state_t receiver_state; // Storing the state for the simple receiver state machine
uint64_t core1_last_loop_pass; // Timestamp of last core1 loop execution uint64_t core1_last_loop_pass; // Timestamp of last core1 loop execution
uint8_t active_output; // Currently selected output (0 = A, 1 = B) uint8_t active_output; // Currently selected output (0 = A, 1 = B)
int16_t mouse_x; // Store and update the location of our mouse pointer int16_t mouse_x; // Store and update the location of our mouse pointer
int16_t mouse_y; int16_t mouse_y;
int16_t mouse_buttons; // Store and update the state of mouse buttons
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
@ -241,9 +268,9 @@ typedef struct {
bool mouse_connected; // True when a mouse is connected locally bool mouse_connected; // True when a mouse is connected locally
/* Feature flags */ /* Feature flags */
bool mouse_zoom; // True when "mouse zoom" is enabled bool mouse_zoom; // True when "mouse zoom" is enabled
bool switch_lock; // True when device is prevented from switching bool switch_lock; // True when device is prevented from switching
bool onboard_led_state; // True when LED is ON bool onboard_led_state; // True when LED is ON
/* Onboard LED blinky (provide feedback when e.g. mouse connected) */ /* Onboard LED blinky (provide feedback when e.g. mouse connected) */
int32_t blinks_left; // How many blink transitions are left int32_t blinks_left; // How many blink transitions are left
@ -265,16 +292,15 @@ 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 *);
/********* Mouse **********/ /********* Mouse **********/
bool tud_hid_abs_mouse_report( bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel);
uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
void process_mouse_report(uint8_t *, int, device_t *); void process_mouse_report(uint8_t *, int, device_t *);
uint8_t uint8_t
parse_report_descriptor(mouse_t *mouse, uint8_t arr_count, uint8_t const *desc_report, uint16_t desc_len); parse_report_descriptor(mouse_t *mouse, uint8_t arr_count, uint8_t const *desc_report, uint16_t desc_len);
int32_t get_report_value(uint8_t *report, report_val_t *val); int32_t get_report_value(uint8_t *report, report_val_t *val);
void process_mouse_queue_task(device_t *); void process_mouse_queue_task(device_t *);
void queue_mouse_report(mouse_abs_report_t *, device_t *); void queue_mouse_report(mouse_report_t *, device_t *);
void output_mouse_report(mouse_abs_report_t *, device_t *); void output_mouse_report(mouse_report_t *, device_t *);
/********* UART **********/ /********* UART **********/
void receive_char(uart_packet_t *, device_t *); void receive_char(uart_packet_t *, device_t *);

View File

@ -46,14 +46,16 @@ void update_mouse_position(device_t *state, mouse_values_t *values) {
/* Update movement */ /* Update movement */
state->mouse_x = move_and_keep_on_screen(state->mouse_x, offset_x); state->mouse_x = move_and_keep_on_screen(state->mouse_x, offset_x);
state->mouse_y = move_and_keep_on_screen(state->mouse_y, offset_y); state->mouse_y = move_and_keep_on_screen(state->mouse_y, offset_y);
/* Update buttons state */
state->mouse_buttons = values->buttons;
} }
/* If we are active output, queue packet to mouse queue, else send them through UART */ /* If we are active output, queue packet to mouse queue, else send them through UART */
void output_mouse_report(mouse_abs_report_t *report, device_t *state) { void output_mouse_report(mouse_report_t *report, device_t *state) {
if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
queue_mouse_report(report, state); queue_mouse_report(report, state);
state->last_activity[BOARD_ROLE] = time_us_64(); state->last_activity[BOARD_ROLE] = time_us_64();
state->screensaver_max_time_reached[BOARD_ROLE] = false;
} else { } else {
send_packet((uint8_t *)report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH); send_packet((uint8_t *)report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH);
} }
@ -90,31 +92,81 @@ int16_t scale_y_coordinate(int screen_from, int screen_to, device_t *state) {
return ((state->mouse_y - from->border.top) * MAX_SCREEN_COORD) / size_from; return ((state->mouse_y - from->border.top) * MAX_SCREEN_COORD) / size_from;
} }
void switch_screen(device_t *state, int new_x, int output_from, int output_to) { void switch_screen(device_t *state, output_t *output, int new_x, int output_from, int output_to, int direction) {
mouse_abs_report_t hidden_pointer = {.y = MIN_SCREEN_COORD, .x = MAX_SCREEN_COORD}; mouse_report_t hidden_pointer = {.y = MIN_SCREEN_COORD, .x = MAX_SCREEN_COORD};
output_mouse_report(&hidden_pointer, state); output_mouse_report(&hidden_pointer, state);
switch_output(state, output_to); switch_output(state, output_to);
state->mouse_x = (output_to == OUTPUT_A) ? MIN_SCREEN_COORD : MAX_SCREEN_COORD; state->mouse_x = (direction == LEFT) ? MAX_SCREEN_COORD : MIN_SCREEN_COORD;
state->mouse_y = scale_y_coordinate(output_from, output_to, state); state->mouse_y = scale_y_coordinate(output->number, 1 - output->number, state);
} }
void check_screen_switch(const mouse_values_t *values, device_t *state) { void switch_desktop(device_t *state, output_t *output, int new_index, int direction) {
int new_x = state->mouse_x + values->move_x; switch (output->os) {
case MACOS:
/* Send relative mouse movement here as well, one or two pixels in the direction of
movement, BEFORE absolute report sets X to 0 */
mouse_report_t move_relative_one = {.x = (direction == LEFT) ? 16384-2 : 16384+2, .mode = RELATIVE};
/* No switching allowed if explicitly disabled */ /* Once doesn't seem reliable enough, do it twice */
if (state->switch_lock) output_mouse_report(&move_relative_one, state);
output_mouse_report(&move_relative_one, state);
break;
case WINDOWS:
/* TODO: Switch to relative-only if index > 1, but keep tabs to switch back */
break;
case LINUX:
case OTHER:
/* Linux should treat all desktops as a single virtual screen, so you should leave
screen_count at 1 and it should just work */
break;
}
state->mouse_x = (direction == RIGHT) ? MIN_SCREEN_COORD : MAX_SCREEN_COORD;
output->screen_index = new_index;
}
/* BORDER
|
.---------. .---------. | .---------. .---------. .---------.
|| B2 || || B1 || | || A1 || || A2 || || A3 || (output, index)
|| extra || || main || | || main || || extra || || extra || (main or extra)
'---------' '---------' | '---------' '---------' '---------'
)___( )___( | )___( )___( )___(
*/
void check_screen_switch(const mouse_values_t *values, device_t *state) {
int new_x = state->mouse_x + values->move_x;
output_t *output = &state->config.output[state->active_output];
bool jump_left = new_x < MIN_SCREEN_COORD - JUMP_THRESHOLD;
bool jump_right = new_x > MAX_SCREEN_COORD + JUMP_THRESHOLD;
int direction = jump_left ? LEFT : RIGHT;
/* No switching allowed if explicitly disabled or mouse button is held */
if (state->switch_lock || state->mouse_buttons)
return; return;
/* End of screen left switches screen A->B TODO: make configurable */ /* No jump condition met == nothing to do, return */
if (new_x < MIN_SCREEN_COORD - JUMP_THRESHOLD && state->active_output == OUTPUT_A) { if (!jump_left && !jump_right)
switch_screen(state, new_x, OUTPUT_A, OUTPUT_B); return;
/* We want to jump in the direction of the other computer */
if (output->pos != direction) {
if (output->screen_index == 1) /* We are at the border -> switch outputs */
switch_screen(state, output, new_x, state->active_output, 1 - state->active_output, direction);
/* If here, this output has multiple desktops and we are not on the main one */
else
switch_desktop(state, output, output->screen_index - 1, direction);
} }
/* End of screen right switches screen B->A TODO: make configurable */ /* We want to jump away from the other computer, only possible if there is another screen to jump to */
else if (new_x > MAX_SCREEN_COORD + JUMP_THRESHOLD && state->active_output == OUTPUT_B) { else if (output->screen_index < output->screen_count)
switch_screen(state, new_x, OUTPUT_B, OUTPUT_A); switch_desktop(state, output, output->screen_index + 1, direction);
}
} }
void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t *values) { void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t *values) {
@ -139,12 +191,13 @@ void extract_report_values(uint8_t *raw_report, device_t *state, mouse_values_t
values->buttons = get_report_value(raw_report, &state->mouse_dev.buttons); values->buttons = get_report_value(raw_report, &state->mouse_dev.buttons);
} }
mouse_abs_report_t create_mouse_report(device_t *state, mouse_values_t *values) { mouse_report_t create_mouse_report(device_t *state, mouse_values_t *values) {
mouse_abs_report_t abs_mouse_report = {.buttons = values->buttons, mouse_report_t abs_mouse_report = {.buttons = values->buttons,
.x = state->mouse_x, .x = state->mouse_x,
.y = state->mouse_y, .y = state->mouse_y,
.wheel = values->wheel, .wheel = values->wheel,
.pan = 0}; .mode = ABSOLUTE,
};
return abs_mouse_report; return abs_mouse_report;
} }
@ -158,7 +211,7 @@ void process_mouse_report(uint8_t *raw_report, int len, device_t *state) {
update_mouse_position(state, &values); update_mouse_position(state, &values);
/* Create the report for the output PC based on the updated values */ /* Create the report for the output PC based on the updated values */
mouse_abs_report_t report = create_mouse_report(state, &values); mouse_report_t report = create_mouse_report(state, &values);
/* Move the mouse, depending where the output is supposed to go */ /* Move the mouse, depending where the output is supposed to go */
output_mouse_report(&report, state); output_mouse_report(&report, state);
@ -172,7 +225,7 @@ void process_mouse_report(uint8_t *raw_report, int len, device_t *state) {
* ==================================================== */ * ==================================================== */
void process_mouse_queue_task(device_t *state) { void process_mouse_queue_task(device_t *state) {
mouse_abs_report_t report = {0}; mouse_report_t report = {0};
/* We need to be connected to the host to send messages */ /* We need to be connected to the host to send messages */
if (!state->tud_connected) if (!state->tud_connected)
@ -187,15 +240,15 @@ void process_mouse_queue_task(device_t *state) {
tud_remote_wakeup(); tud_remote_wakeup();
/* ... try sending it to the host, if it's successful */ /* ... try sending it to the host, if it's successful */
bool succeeded = tud_hid_abs_mouse_report( bool succeeded = tud_mouse_report(
REPORT_ID_MOUSE, report.buttons, report.x, report.y, report.wheel, report.pan); report.mode, report.buttons, report.x, report.y, report.wheel);
/* ... then we can remove it from the queue */ /* ... then we can remove it from the queue */
if (succeeded) if (succeeded)
queue_try_remove(&state->mouse_queue, &report); queue_try_remove(&state->mouse_queue, &report);
} }
void queue_mouse_report(mouse_abs_report_t *report, device_t *state) { void queue_mouse_report(mouse_report_t *report, device_t *state) {
/* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */ /* It wouldn't be fun to queue up a bunch of messages and then dump them all on host */
if (!state->tud_connected) if (!state->tud_connected)
return; return;

View File

@ -54,6 +54,12 @@ void pio_usb_host_config(void) {
/* tuh_configure() must be called before tuh_init() */ /* tuh_configure() must be called before tuh_init() */
static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG; static pio_usb_configuration_t config = PIO_USB_DEFAULT_CONFIG;
config.pin_dp = PIO_USB_DP_PIN_DEFAULT; config.pin_dp = PIO_USB_DP_PIN_DEFAULT;
/* Make HID protocol the default for port B as a fix for devices enumerating
themselves as both keyboards and mice, but having just a single common mode */
if(BOARD_ROLE == OUTPUT_B)
tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT);
tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &config); tuh_configure(BOARD_TUH_RHPORT, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &config);
/* Initialize and configure TinyUSB Host */ /* Initialize and configure TinyUSB Host */
@ -80,15 +86,15 @@ void initial_setup(device_t *state) {
/* Initialize keyboard and mouse queues */ /* Initialize keyboard and mouse queues */
queue_init(&state->kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH); queue_init(&state->kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH);
queue_init(&state->mouse_queue, sizeof(mouse_abs_report_t), MOUSE_QUEUE_LENGTH); queue_init(&state->mouse_queue, sizeof(mouse_report_t), MOUSE_QUEUE_LENGTH);
/* Setup RP2040 Core 1 */ /* Setup RP2040 Core 1 */
multicore_reset_core1(); multicore_reset_core1();
multicore_launch_core1(core1_main); multicore_launch_core1(core1_main);
/* Initialize and configure TinyUSB Device */ /* Initialize and configure TinyUSB Device */
tud_init(BOARD_TUD_RHPORT); tud_init(BOARD_TUD_RHPORT);
/* Initialize and configure TinyUSB Host */ /* Initialize and configure TinyUSB Host */
pio_usb_host_config(); pio_usb_host_config();

View File

@ -49,11 +49,6 @@ extern "C" {
#define CFG_TUH_ENABLED 1 #define CFG_TUH_ENABLED 1
#define CFG_TUH_RPI_PIO_USB 1 #define CFG_TUH_RPI_PIO_USB 1
// defined by board.mk
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
// RHPort number used for device can be defined by board.mk, default to port 0 // RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_DEVICE_RHPORT_NUM #ifndef BOARD_DEVICE_RHPORT_NUM
#define BOARD_DEVICE_RHPORT_NUM 0 #define BOARD_DEVICE_RHPORT_NUM 0
@ -62,22 +57,13 @@ extern "C" {
// RHPort max operational speed can defined by board.mk // RHPort max operational speed can defined by board.mk
// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed
#ifndef BOARD_DEVICE_RHPORT_SPEED #ifndef BOARD_DEVICE_RHPORT_SPEED
#if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX \
|| CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || CFG_TUSB_MCU == OPT_MCU_NUC505 \
|| CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X)
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED
#else
#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED
#endif #endif
#endif
// Device mode with rhport and speed defined by board.mk // Device mode with rhport and speed defined by board.mk
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | BOARD_DEVICE_RHPORT_SPEED) #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | BOARD_DEVICE_RHPORT_SPEED)
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put * Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section. * into those specific section.
@ -101,9 +87,25 @@ extern "C" {
#define CFG_TUD_ENDPOINT0_SIZE 64 #define CFG_TUD_ENDPOINT0_SIZE 64
#endif #endif
//------------- DEBUG -------------//
#ifdef DH_DEBUG
#define CFG_TUD_CDC 1
#define CFG_TUSB_DEBUG 2
#define CFG_TUD_LOG_LEVEL 3
#define CFG_TUSB_DEBUG_PRINTF dh_debug_printf
extern int dh_debug_printf(const char *__restrict __format, ...);
#define CFG_TUD_CDC_RX_BUFSIZE 64
#define CFG_TUD_CDC_TX_BUFSIZE 64
#define CFG_TUH_CDC_LINE_CODING_ON_ENUM \
{ 921600, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
#else
#define CFG_TUD_CDC 0
#endif
//------------- CLASS -------------// //------------- CLASS -------------//
#define CFG_TUD_HID 1 #define CFG_TUD_HID 2
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0 #define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0 #define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0 #define CFG_TUD_VENDOR 0
@ -116,7 +118,7 @@ extern "C" {
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// Size of buffer to hold descriptors and other data used for enumeration // Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 256 #define CFG_TUH_ENUMERATION_BUFSIZE 512
#define CFG_TUH_HUB 1 #define CFG_TUH_HUB 1
// max device support (excluding hub device) // max device support (excluding hub device)

View File

@ -114,7 +114,9 @@ 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);
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;

View File

@ -59,132 +59,66 @@ uint8_t const *tud_descriptor_device_cb(void) {
// HID Report Descriptor // HID Report Descriptor
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// 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))};
uint8_t const desc_hid_report_relmouse[] = {TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_RELMOUSE))};
// Invoked when received GET HID REPORT DESCRIPTOR // Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor // Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete // Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
(void)instance; if (instance == ITF_NUM_HID_REL_M) {
return desc_hid_report_relmouse;
}
/* Default */
return desc_hid_report; return desc_hid_report;
} }
bool tud_hid_n_abs_mouse_report(uint8_t instance, bool tud_mouse_report(uint8_t mode,
uint8_t report_id, uint8_t buttons,
uint8_t buttons, int16_t x,
int16_t x, int16_t y,
int16_t y, int8_t wheel) {
int8_t vertical,
int8_t horizontal) { if (mode == ABSOLUTE) {
mouse_abs_report_t report = {.buttons = buttons, .x = x, .y = y, .wheel = vertical, .pan = horizontal}; mouse_report_t report = {.buttons = buttons, .x = x, .y = y, .wheel = wheel};
return tud_hid_n_report(instance, report_id, &report, sizeof(report)); return tud_hid_n_report(ITF_NUM_HID, REPORT_ID_MOUSE, &report, sizeof(report));
}
else {
hid_mouse_report_t report = {.buttons = buttons, .x = x - 16384, .y = y - 16384, .wheel = wheel, .pan = 0};
return tud_hid_n_report(ITF_NUM_HID_REL_M, REPORT_ID_RELMOUSE, &report, sizeof(report));
}
} }
bool tud_hid_abs_mouse_report(
uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum { ITF_NUM_HID, ITF_NUM_TOTAL };
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN)
#define EPNUM_HID 0x81
uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500),
// Interface number, string index, protocol, report descriptor len, EP In address, size &
// polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID,
0,
HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report),
EPNUM_HID,
CFG_TUD_HID_EP_BUFSIZE,
1)};
#if TUD_OPT_HIGH_SPEED
// Per USB specs: high speed capable device must report device_qualifier and
// other_speed_configuration
// other speed configuration
uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
// device qualifier is mostly similar to device descriptor since we don't change configuration based
// on speed
tusb_desc_device_qualifier_t const desc_device_qualifier = {.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = USB_BCD,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0x00};
// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to
// complete. device_qualifier descriptor describes information about a high-speed capable device
// that would change if the device were operating at the other speed. If not highspeed capable stall
// this request.
uint8_t const *tud_descriptor_device_qualifier_cb(void) {
return (uint8_t const *)&desc_device_qualifier;
}
// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to
// complete Configuration descriptor in the other speed e.g if high speed then this is for full
// speed and vice versa
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// other speed config is basically configuration with type = OHER_SPEED_CONFIG
memcpy(desc_other_speed_config, desc_configuration, CONFIG_TOTAL_LEN);
desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
// this example use the same configuration for both high and full speed mode
return desc_other_speed_config;
}
#endif // highspeed
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
// This example use the same configuration for both high and full speed mode
return desc_configuration;
}
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// String Descriptors // String Descriptors
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
};
// array of pointer to string descriptors // array of pointer to string descriptors
char const *string_desc_arr[] = { char const *string_desc_arr[] = {
(const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"Hrvoje Cavrak", // 1: Manufacturer "Hrvoje Cavrak", // 1: Manufacturer
"DeskHop Switch", // 2: Product "DeskHop Switch", // 2: Product
"0", // 3: Serials, should use chip ID "0", // 3: Serials, should use chip ID
"MouseHelper", // 4: Relative mouse to work around OS issues
#ifdef DH_DEBUG
"Debug Interface", // 5: Debug Interface
#endif
};
// String Descriptor Index
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
STRID_MOUSE,
STRID_DEBUG,
}; };
static uint16_t _desc_str[32]; static uint16_t _desc_str[32];
@ -225,3 +159,60 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
return _desc_str; return _desc_str;
} }
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
#define EPNUM_HID 0x81
#define EPNUM_HID_REL_M 0x82
#ifndef DH_DEBUG
enum { ITF_NUM_TOTAL = 2 };
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_HID_DESC_LEN)
#else
enum { ITF_NUM_CDC = 2, ITF_NUM_TOTAL = 3 };
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_HID_DESC_LEN + TUD_CDC_DESC_LEN)
#define EPNUM_CDC_NOTIF 0x83
#define EPNUM_CDC_OUT 0x04
#define EPNUM_CDC_IN 0x84
#endif
uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 500),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID,
STRID_PRODUCT,
HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report),
EPNUM_HID,
CFG_TUD_HID_EP_BUFSIZE,
1),
TUD_HID_DESCRIPTOR(ITF_NUM_HID_REL_M,
STRID_MOUSE,
HID_ITF_PROTOCOL_NONE,
sizeof(desc_hid_report_relmouse),
EPNUM_HID_REL_M,
CFG_TUD_HID_EP_BUFSIZE,
20),
#ifdef DH_DEBUG
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(
ITF_NUM_CDC, STRID_DEBUG, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, CFG_TUD_CDC_EP_BUFSIZE),
#endif
};
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
(void)index; // for multiple configurations
return desc_configuration;
}

View File

@ -29,10 +29,14 @@ enum
{ {
REPORT_ID_KEYBOARD = 1, REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE, REPORT_ID_MOUSE,
REPORT_ID_CONSUMER_CONTROL,
REPORT_ID_COUNT REPORT_ID_COUNT
}; };
enum
{
REPORT_ID_RELMOUSE = 1,
};
#define TUD_HID_REPORT_DESC_ABSMOUSE(...) \ #define TUD_HID_REPORT_DESC_ABSMOUSE(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\ HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\ HID_USAGE ( HID_USAGE_DESKTOP_MOUSE ) ,\
@ -74,92 +78,12 @@ HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
HID_REPORT_COUNT( 1 ) ,\ HID_REPORT_COUNT( 1 ) ,\
HID_REPORT_SIZE ( 8 ) ,\ HID_REPORT_SIZE ( 8 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_CONSUMER ), \
\ \
/* Horizontal wheel scroll [-127, 127] */ \ /* Mouse mode (0 = absolute, 1 = relative) */ \
HID_USAGE_N ( HID_USAGE_CONSUMER_AC_PAN, 2 ), \
HID_LOGICAL_MIN ( 0x81 ), \
HID_LOGICAL_MAX ( 0x7f ), \
HID_REPORT_COUNT( 1 ), \ HID_REPORT_COUNT( 1 ), \
HID_REPORT_SIZE ( 8 ), \ HID_REPORT_SIZE ( 8 ), \
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE ), \ HID_INPUT ( HID_CONSTANT ), \
HID_COLLECTION_END , \ HID_COLLECTION_END , \
HID_COLLECTION_END \ HID_COLLECTION_END \
/* Generated report */
/* */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x06, Usage (Keyboard), */
/* 0xA1, 0x01, Collection (Application), */
/* 0x85, 0x01, Report ID (1), */
/* 0x05, 0x07, Usage Page (Keyboard), */
/* 0x19, 0xE0, Usage Minimum (KB Leftcontrol), */
/* 0x29, 0xE7, Usage Maximum (KB Right GUI), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x25, 0x01, Logical Maximum (1), */
/* 0x95, 0x08, Report Count (8), */
/* 0x75, 0x01, Report Size (1), */
/* 0x81, 0x02, Input (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x01, Input (Constant), */
/* 0x05, 0x08, Usage Page (LED), */
/* 0x19, 0x01, Usage Minimum (01h), */
/* 0x29, 0x05, Usage Maximum (05h), */
/* 0x95, 0x05, Report Count (5), */
/* 0x75, 0x01, Report Size (1), */
/* 0x91, 0x02, Output (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x03, Report Size (3), */
/* 0x91, 0x01, Output (Constant), */
/* 0x05, 0x07, Usage Page (Keyboard), */
/* 0x19, 0x00, Usage Minimum (None), */
/* 0x2A, 0xFF, 0x00, Usage Maximum (FFh), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x26, 0xFF, 0x00, Logical Maximum (255), */
/* 0x95, 0x06, Report Count (6), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x00, Input, */
/* 0xC0, End Collection, */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x02, Usage (Mouse), */
/* 0xA1, 0x01, Collection (Application), */
/* 0x85, 0x02, Report ID (2), */
/* 0x09, 0x01, Usage (Pointer), */
/* 0xA1, 0x00, Collection (Physical), */
/* 0x05, 0x09, Usage Page (Button), */
/* 0x19, 0x01, Usage Minimum (01h), */
/* 0x29, 0x05, Usage Maximum (05h), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x25, 0x01, Logical Maximum (1), */
/* 0x95, 0x05, Report Count (5), */
/* 0x75, 0x01, Report Size (1), */
/* 0x81, 0x02, Input (Variable), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x03, Report Size (3), */
/* 0x81, 0x01, Input (Constant), */
/* 0x05, 0x01, Usage Page (Desktop), */
/* 0x09, 0x30, Usage (X), */
/* 0x09, 0x31, Usage (Y), */
/* 0x15, 0x00, Logical Minimum (0), */
/* 0x26, 0xFF, 0x7F, Logical Maximum (32767), */
/* 0x75, 0x10, Report Size (16), */
/* 0x95, 0x02, Report Count (2), */
/* 0x81, 0x02, Input (Variable), */
/* 0x09, 0x38, Usage (Wheel), */
/* 0x15, 0x81, Logical Minimum (-127), */
/* 0x25, 0x7F, Logical Maximum (127), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x06, Input (Variable, Relative), */
/* 0x05, 0x0C, Usage Page (Consumer), */
/* 0x0A, 0x38, 0x02, Usage (AC Pan), */
/* 0x15, 0x81, Logical Minimum (-127), */
/* 0x25, 0x7F, Logical Maximum (127), */
/* 0x95, 0x01, Report Count (1), */
/* 0x75, 0x08, Report Size (8), */
/* 0x81, 0x06, Input (Variable, Relative), */
/* 0xC0, End Collection, */
/* 0xC0 End Collection */
#endif /* USB_DESCRIPTORS_H_ */ #endif /* USB_DESCRIPTORS_H_ */

View File

@ -29,6 +29,7 @@
* a key that is unlikely to ever appear on a keyboard that you will use. * a key that is unlikely to ever appear on a keyboard that you will use.
* HID_KEY_F24 is probably a good choice as keyboards with 24 function keys * HID_KEY_F24 is probably a good choice as keyboards with 24 function keys
* are rare. * are rare.
*
* */ * */
#define HOTKEY_TOGGLE HID_KEY_CAPS_LOCK #define HOTKEY_TOGGLE HID_KEY_CAPS_LOCK
@ -54,13 +55,12 @@
#define MOUSE_SPEED_A_FACTOR_X 16 #define MOUSE_SPEED_A_FACTOR_X 16
#define MOUSE_SPEED_A_FACTOR_Y 16 #define MOUSE_SPEED_A_FACTOR_Y 16
/* Output B values */ /* Output B values */
#define MOUSE_SPEED_B_FACTOR_X 16 #define MOUSE_SPEED_B_FACTOR_X 16
#define MOUSE_SPEED_B_FACTOR_Y 16 #define MOUSE_SPEED_B_FACTOR_Y 16
#define JUMP_THRESHOLD 0 #define JUMP_THRESHOLD 0
/**================================================== * /**================================================== *
* ============== Screensaver Config ============== * * ============== Screensaver Config ============== *
* ================================================== * * ================================================== *
@ -104,8 +104,8 @@
* *
**/ **/
#define SCREENSAVER_A_ENABLED 1 #define SCREENSAVER_A_ENABLED 0
#define SCREENSAVER_B_ENABLED 0 #define SCREENSAVER_B_ENABLED 0
/**================================================== * /**================================================== *
* *
@ -114,19 +114,18 @@
* *
**/ **/
#define SCREENSAVER_A_IDLE_TIME_SEC 60 #define SCREENSAVER_A_IDLE_TIME_SEC 240
#define SCREENSAVER_B_IDLE_TIME_SEC 240 #define SCREENSAVER_B_IDLE_TIME_SEC 240
/**================================================== * /**================================================== *
* *
* SCREENSAVER_{A|B}_MAX_TIME_SEC: Number of seconds that an output * SCREENSAVER_{A|B}_MAX_TIME_SEC: Number of seconds that the screensaver
* can be inactive before the screensaver mode will be deactivated. If * will run on an output before being deactivated. 0 for indefinitely.
* zero, the screensaver will run indefinitely.
* *
**/ **/
#define SCREENSAVER_A_MAX_TIME_SEC 120 #define SCREENSAVER_A_MAX_TIME_SEC 0
#define SCREENSAVER_B_MAX_TIME_SEC 0 #define SCREENSAVER_B_MAX_TIME_SEC 0
/**================================================== * /**================================================== *
* *
@ -135,5 +134,22 @@
* *
**/ **/
#define SCREENSAVER_A_ONLY_IF_INACTIVE 1 #define SCREENSAVER_A_ONLY_IF_INACTIVE 0
#define SCREENSAVER_B_ONLY_IF_INACTIVE 1 #define SCREENSAVER_B_ONLY_IF_INACTIVE 0
/**================================================== *
* ================ Output OS Config =============== *
* ==================================================
*
* Defines OS an output connects to. You will need to worry about this only if you have
* multiple desktops and one of your outputs is MacOS or Windows.
*
* Available options: LINUX, MACOS, WINDOWS, OTHER (check main.h for details)
*
* OUTPUT_A_OS: OS for output A
* OUTPUT_B_OS: OS for output B
*
* */
#define OUTPUT_A_OS LINUX
#define OUTPUT_B_OS LINUX

View File

@ -104,56 +104,37 @@ void save_config(device_t *state) {
restore_interrupts(ints); restore_interrupts(ints);
} }
/* Have something fun and entertaining when idle */ /* Have something fun and entertaining when idle. */
void screensaver_task(device_t *state) { void screensaver_task(device_t *state) {
const int mouse_move_delay = 5000; const int mouse_move_delay = 5000;
screensaver_t *screensaver = &state->config.output[BOARD_ROLE].screensaver;
static mouse_abs_report_t report = {.x = 0, .y = 0}; static mouse_report_t report = {.x = 0, .y = 0};
static int last_pointer_move = 0; static int last_pointer_move = 0;
uint64_t current_time = time_us_64(); uint64_t current_time = time_us_64();
static uint64_t last_activation_time = 0; uint64_t inactivity_period = current_time - state->last_activity[BOARD_ROLE];
/* "Randomly" chosen initial values */ /* "Randomly" chosen initial values */
static int dx = 20; static int dx = 20;
static int dy = 25; static int dy = 25;
/* If the maximum time has been reached, nothing to do here. */
if (state->screensaver_max_time_reached[BOARD_ROLE]) {
return;
}
/* If we're not enabled, nothing to do here. */ /* If we're not enabled, nothing to do here. */
if (!state->config.screensaver[BOARD_ROLE].enabled) { if (!screensaver->enabled)
last_activation_time = 0; return;
return;
} /* System is still not idle for long enough to activate or we've been running for too long */
if (inactivity_period < screensaver->idle_time_us)
return;
/* We exceeded the maximum permitted screensaver runtime */
if (screensaver->max_time_us
&& inactivity_period > (screensaver->max_time_us + screensaver->idle_time_us))
return;
/* If we're not the selected output and that is required, nothing to do here. */ /* If we're not the selected output and that is required, nothing to do here. */
if (state->config.screensaver[BOARD_ROLE].only_if_inactive && if (screensaver->only_if_inactive && CURRENT_BOARD_IS_ACTIVE_OUTPUT)
CURRENT_BOARD_IS_ACTIVE_OUTPUT) {
last_activation_time = 0;
return; return;
}
/* We are enabled, but idle time still too small to activate. */
if ((current_time - state->last_activity[BOARD_ROLE]) <
state->config.screensaver[BOARD_ROLE].idle_time_us) {
last_activation_time = 0;
return;
}
if (last_activation_time == 0) {
last_activation_time = current_time;
} else {
/* We are enabled, but max time has been reached. */
if ((current_time - last_activation_time) >
state->config.screensaver[BOARD_ROLE].max_time_us) {
state->screensaver_max_time_reached[BOARD_ROLE] = true;
last_activation_time = 0;
return;
}
}
/* We're active! Now check if it's time to move the cursor yet. */ /* We're active! Now check if it's time to move the cursor yet. */
if ((time_us_32()) - last_pointer_move < mouse_move_delay) if ((time_us_32()) - last_pointer_move < mouse_move_delay)
@ -175,3 +156,23 @@ void screensaver_task(device_t *state) {
/* Update timer of the last pointer move */ /* Update timer of the last pointer move */
last_pointer_move = time_us_32(); last_pointer_move = time_us_32();
} }
/* ================================================== *
* Debug functions
* ================================================== */
#ifdef DH_DEBUG
int dh_debug_printf(const char *format, ...) {
va_list args;
va_start(args, format);
char buffer[512];
int string_len = vsnprintf(buffer, 512, format, args);
tud_cdc_n_write(0, buffer, string_len);
tud_cdc_write_flush();
va_end(args);
}
#endif