DeskHop 0.63 (Bugfixes, small features)
- add gaming mode (use left shift + right shift + G to toggle) - rework HID queue, smoother operation of rotary dials (no packets lost) - fix dragging across multiple screens on the same output - improve read reliability for UI - move default keyboard hotkey for output switching to LCtrl + Caps Lock - change default X/Y speed to match 16:9 geometry
This commit is contained in:
		
							parent
							
								
									1fd0049039
								
							
						
					
					
						commit
						a249aa50f1
					
				|  | @ -1,7 +1,7 @@ | |||
| cmake_minimum_required(VERSION 3.6) | ||||
| 
 | ||||
| set(VERSION_MAJOR 00) | ||||
| set(VERSION_MINOR 147) | ||||
| set(VERSION_MINOR 156) | ||||
| 
 | ||||
| set(PICO_SDK_FETCH_FROM_GIT off) | ||||
| set(PICO_BOARD=pico) | ||||
|  |  | |||
|  | @ -42,7 +42,7 @@ The actual switch happens at the very moment when one arrow stops moving and the | |||
| 
 | ||||
| ## Keyboard | ||||
| 
 | ||||
| Acting as a USB Host and querying your keyboard periodically, it looks for a preconfigured hotkey in the hid report (usually Caps Lock for me). When found, it will forward all subsequent characters to the other output. | ||||
| Acting as a USB Host and querying your keyboard periodically, it looks for a preconfigured hotkey in the hid report (usually Ctrl + Caps Lock for me). When found, it will forward all subsequent characters to the other output. | ||||
| 
 | ||||
| To have a visual indication which output you are using at any given moment, you can repurpose keyboard LEDs and have them provide the necessary feedback.  | ||||
| 
 | ||||
|  | @ -101,6 +101,10 @@ This will make sure you won't accidentally leave your current screen. To turn of | |||
| You can lock both computers at once by using ```RIGHT CTRL + L```.  | ||||
| To make use of this feature, first set up the OS for each output in config (since the shortcuts are different). | ||||
| 
 | ||||
| ### Gaming Mode | ||||
| 
 | ||||
| If you're gaming, there is a chance your game might not work properly with absolute mouse mode. To address that issue, a **gaming mode** is introduced, toggled by ```LEFT SHIFT + RIGHT SHIFT + G```. When in gaming mode, you are locked to the current screen and your mouse behaves like a standard relative mouse. This should also fix various virtual machine issues, currently unsupported operating systems etc. | ||||
| 
 | ||||
| ### Screensaver | ||||
| 
 | ||||
| Supposedly built in to prevent computer from entering standby, but truth be told - it is just fun to watch. **Off by default**, will make your mouse pointer bounce around the screen like a Pong ball. When enabled, it activates after a period of inactivity defined in user config header and automatically switches off as soon as you send any output towards that screen.  | ||||
|  | @ -216,7 +220,8 @@ _Usage_ | |||
| - ```Right CTRL + Right ALT``` - Toggle slower mouse mode | ||||
| - ```Right CTRL + K``` - Lock/Unlock mouse desktop switching | ||||
| - ```Right CTRL + L``` - Lock both outputs at once (set output OS before, see shortcuts below) | ||||
| - ```Caps Lock``` - Switch between outputs | ||||
| - ```Left Shift + Right Shift + G``` - Toggle gaming mode (lock to screen, act as standard mouse) | ||||
| - ```Left CTRL + Caps Lock``` - Switch between outputs | ||||
| 
 | ||||
| ### Switch cursor height calibration | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								disk/disk.img
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								disk/disk.img
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -67,6 +67,12 @@ void switchlock_hotkey_handler(device_t *state, hid_keyboard_report_t *report) { | |||
|     send_value(state->switch_lock, SWITCH_LOCK_MSG); | ||||
| } | ||||
| 
 | ||||
| /* This key combo toggles gaming mode */ | ||||
| void toggle_relative_mode_handler(device_t *state, hid_keyboard_report_t *report) { | ||||
|     state->relative_mouse ^= 1; | ||||
|     send_value(state->relative_mouse, RELATIVE_MODE_MSG); | ||||
| }; | ||||
| 
 | ||||
| /* This key combo locks both outputs simultaneously */ | ||||
| void screenlock_hotkey_handler(device_t *state, hid_keyboard_report_t *report) { | ||||
|     hid_keyboard_report_t lock_report = {0}, release_keys = {0}; | ||||
|  | @ -199,9 +205,9 @@ void handle_wipe_config_msg(uart_packet_t *packet, device_t *state) { | |||
|     load_config(state); | ||||
| } | ||||
| 
 | ||||
| /* Process consumer control message, TODO: use queue instead of sending directly */ | ||||
| /* Process consumer control message */ | ||||
| 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); | ||||
|     queue_cc_packet(packet->data, state); | ||||
| } | ||||
| 
 | ||||
| /* Process request to store config to flash */ | ||||
|  | @ -219,6 +225,11 @@ void handle_proxy_msg(uart_packet_t *packet, device_t *state) { | |||
|     queue_packet(&packet->data[1], (enum packet_type_e)packet->data[0], PACKET_DATA_LENGTH - 1); | ||||
| } | ||||
| 
 | ||||
| /* Process request to reboot the board */ | ||||
| void handle_toggle_relative_msg(uart_packet_t *packet, device_t *state) { | ||||
|     state->relative_mouse = packet->data[0]; | ||||
| } | ||||
| 
 | ||||
| /* Process api communication messages */ | ||||
| void handle_api_msgs(uart_packet_t *packet, device_t *state) { | ||||
|     uint8_t value_idx = packet->data[0]; | ||||
|  | @ -239,15 +250,24 @@ void handle_api_msgs(uart_packet_t *packet, device_t *state) { | |||
|         memcpy(ptr, &packet->data[1], map->len); | ||||
|     } | ||||
|     else if (packet->type == GET_VAL_MSG) { | ||||
|         uart_packet_t response = {.type=GET_VAL_MSG, .data={0}}; | ||||
|         memcpy(response.data, ptr, map->len); | ||||
|         queue_try_add(&state->cfg_queue_out, &response); | ||||
| 	uart_packet_t response = {.type=GET_VAL_MSG, .data={[0] = value_idx}}; | ||||
| 	memcpy(&response.data[1], ptr, map->len); | ||||
| 	queue_cfg_packet(&response, state); | ||||
|     } | ||||
| 
 | ||||
|     /* With each GET/SET message, we reset the configuration mode timeout */ | ||||
|     reset_config_timer(state); | ||||
| } | ||||
| 
 | ||||
| /* Handle the "read all" message by calling our "read one" handler for each type */ | ||||
| void handle_api_read_all_msg(uart_packet_t *packet, device_t *state) { | ||||
|     uart_packet_t result = {.type=GET_VAL_MSG}; | ||||
| 
 | ||||
|     for (int i = 0; i < get_field_map_length(); i++) { | ||||
|         result.data[0] = get_field_map_index(i)->idx; | ||||
|         handle_api_msgs(&result, state); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Process request packet and create a response */ | ||||
| void handle_request_byte_msg(uart_packet_t *packet, device_t *state) { | ||||
|  |  | |||
|  | @ -122,12 +122,14 @@ enum packet_type_e { | |||
|     FLASH_LED_MSG        = 9, | ||||
|     WIPE_CONFIG_MSG      = 10, | ||||
|     HEARTBEAT_MSG        = 12, | ||||
|     RELATIVE_MODE_MSG    = 13, | ||||
|     CONSUMER_CONTROL_MSG = 14, | ||||
|     SYSTEM_CONTROL_MSG   = 15, | ||||
|     SAVE_CONFIG_MSG      = 18, | ||||
|     REBOOT_MSG           = 19, | ||||
|     GET_VAL_MSG          = 20, | ||||
|     SET_VAL_MSG          = 21, | ||||
|     GET_ALL_VALS_MSG     = 22, | ||||
|     PROXY_PACKET_MSG     = 23, | ||||
|     REQUEST_BYTE_MSG     = 24, | ||||
|     RESPONSE_BYTE_MSG    = 25, | ||||
|  | @ -166,7 +168,7 @@ typedef struct { | |||
| #define RAW_PACKET_LENGTH (START_LENGTH + PACKET_LENGTH) | ||||
| 
 | ||||
| #define UART_QUEUE_LENGTH  256 | ||||
| #define CFG_QUEUE_LENGTH   128 | ||||
| #define HID_QUEUE_LENGTH   128 | ||||
| #define KBD_QUEUE_LENGTH   128 | ||||
| #define MOUSE_QUEUE_LENGTH 512 | ||||
| 
 | ||||
|  | @ -345,6 +347,25 @@ typedef struct TU_ATTR_PACKED { | |||
|     uint8_t mode; | ||||
| } mouse_report_t; | ||||
| 
 | ||||
| /* Used to work around OS issues with absolute coordinates on
 | ||||
|    multiple desktops (Windows/MacOS) */ | ||||
| typedef struct { | ||||
|     uint8_t tip_pressure; | ||||
|     uint8_t buttons; // Buttons
 | ||||
|     uint16_t x;      // X coordinate (0-32767)
 | ||||
|     uint16_t y;      // Y coordinate (0-32767)
 | ||||
| } touch_report_t; | ||||
| 
 | ||||
| /* This stores various packets other than kbd/mouse to go out
 | ||||
|    (configuration, consumer control, system...) */ | ||||
| typedef struct { | ||||
|     uint8_t instance; | ||||
|     uint8_t report_id; | ||||
|     uint8_t type; | ||||
|     uint8_t len; | ||||
|     uint8_t data[RAW_PACKET_LENGTH]; | ||||
| } hid_generic_pkt_t; | ||||
| 
 | ||||
| typedef enum { IDLE, READING_PACKET, PROCESSING_PACKET } receiver_state_t; | ||||
| 
 | ||||
| typedef struct { | ||||
|  | @ -370,7 +391,7 @@ typedef struct { | |||
|     int16_t mouse_buttons; // Store and update the state of mouse buttons
 | ||||
| 
 | ||||
|     config_t config;       // Device configuration, loaded from flash or defaults used
 | ||||
|     queue_t cfg_queue_out; // Queue that stores outgoing vendor config messages
 | ||||
|     queue_t hid_queue_out; // Queue that stores outgoing hid messages
 | ||||
|     queue_t kbd_queue;     // Queue that stores keyboard reports
 | ||||
|     queue_t mouse_queue;   // Queue that stores mouse reports
 | ||||
|     queue_t uart_tx_queue; // Queue that stores outgoing packets
 | ||||
|  | @ -427,6 +448,8 @@ void process_consumer_report(uint8_t *, int, uint8_t, hid_interface_t *); | |||
| void process_system_report(uint8_t *, int, uint8_t, hid_interface_t *); | ||||
| void release_all_keys(device_t *); | ||||
| void queue_kbd_report(hid_keyboard_report_t *, device_t *); | ||||
| void queue_cc_packet(uint8_t *, device_t *); | ||||
| void queue_system_packet(uint8_t *, device_t *); | ||||
| void send_key(hid_keyboard_report_t *, device_t *); | ||||
| void send_consumer_control(uint8_t *, device_t *); | ||||
| bool key_in_report(uint8_t, const hid_keyboard_report_t *); | ||||
|  | @ -474,7 +497,7 @@ void reboot(void); | |||
| /*********  Tasks  **********/ | ||||
| void process_uart_tx_task(device_t *); | ||||
| void process_mouse_queue_task(device_t *); | ||||
| void process_cfg_queue_task(device_t *); | ||||
| void process_hid_queue_task(device_t *); | ||||
| void process_kbd_queue_task(device_t *); | ||||
| void usb_device_task(device_t *); | ||||
| void kick_watchdog_task(device_t *); | ||||
|  | @ -495,7 +518,10 @@ void reset_config_timer(device_t *); | |||
| 
 | ||||
| extern const field_map_t api_field_map[]; | ||||
| const field_map_t* get_field_map_entry(uint32_t); | ||||
| const field_map_t* get_field_map_index(uint32_t); | ||||
| size_t get_field_map_length(void); | ||||
| bool validate_packet(uart_packet_t *); | ||||
| void queue_cfg_packet(uart_packet_t *, device_t *); | ||||
| 
 | ||||
| /*********  Handlers  **********/ | ||||
| void output_toggle_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
|  | @ -505,6 +531,7 @@ void fw_upgrade_hotkey_handler_B(device_t *, hid_keyboard_report_t *); | |||
| void mouse_zoom_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
| void all_keys_released_handler(device_t *); | ||||
| void switchlock_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
| void toggle_relative_mode_handler(device_t *, hid_keyboard_report_t *); | ||||
| void screenlock_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
| void output_config_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
| void wipe_config_hotkey_handler(device_t *, hid_keyboard_report_t *); | ||||
|  | @ -530,6 +557,8 @@ void handle_response_byte_msg(uart_packet_t *, device_t *); | |||
| void handle_heartbeat_msg(uart_packet_t *, device_t *); | ||||
| void handle_proxy_msg(uart_packet_t *, device_t *); | ||||
| void handle_api_msgs(uart_packet_t *, device_t *); | ||||
| void handle_api_read_all_msg(uart_packet_t *, device_t *); | ||||
| void handle_toggle_relative_msg(uart_packet_t *, device_t *); | ||||
| 
 | ||||
| void switch_output(device_t *, uint8_t); | ||||
| 
 | ||||
|  |  | |||
|  | @ -151,4 +151,48 @@ HID_COLLECTION_END \ | |||
|     HID_OUTPUT       ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\ | ||||
|   HID_COLLECTION_END \ | ||||
| 
 | ||||
| #define TUD_HID_REPORT_DESC_MOUSEHELP(...) \ | ||||
|   HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP      )                   ,\ | ||||
|   HID_USAGE      ( HID_USAGE_DESKTOP_MOUSE     )                   ,\ | ||||
|   HID_COLLECTION ( HID_COLLECTION_APPLICATION  )                   ,\ | ||||
|     /* Report ID if any */\ | ||||
|     __VA_ARGS__ \ | ||||
|     HID_USAGE      ( HID_USAGE_DESKTOP_POINTER )                   ,\ | ||||
|     HID_COLLECTION ( HID_COLLECTION_PHYSICAL   )                   ,\ | ||||
|       HID_USAGE_PAGE  ( HID_USAGE_PAGE_BUTTON  )                   ,\ | ||||
|         HID_USAGE_MIN   ( 1                                      ) ,\ | ||||
|         HID_USAGE_MAX   ( 5                                      ) ,\ | ||||
|         HID_LOGICAL_MIN ( 0                                      ) ,\ | ||||
|         HID_LOGICAL_MAX ( 1                                      ) ,\ | ||||
|         /* Left, Right, Middle, Backward, Forward buttons */ \ | ||||
|         HID_REPORT_COUNT( 5                                      ) ,\ | ||||
|         HID_REPORT_SIZE ( 1                                      ) ,\ | ||||
|         HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\ | ||||
|         /* 3 bit padding */ \ | ||||
|         HID_REPORT_COUNT( 1                                      ) ,\ | ||||
|         HID_REPORT_SIZE ( 3                                      ) ,\ | ||||
|         HID_INPUT       ( HID_CONSTANT                           ) ,\ | ||||
|       HID_USAGE_PAGE  ( HID_USAGE_PAGE_DESKTOP )                   ,\ | ||||
|         /* X, Y position [-127, 127] */ \ | ||||
|         HID_USAGE       ( HID_USAGE_DESKTOP_X                    ) ,\ | ||||
|         HID_USAGE       ( HID_USAGE_DESKTOP_Y                    ) ,\ | ||||
|         HID_LOGICAL_MIN_N ( 0x8000, 2                            ) ,\ | ||||
|         HID_LOGICAL_MAX_N ( 0x7fff, 2                            ) ,\ | ||||
|         HID_REPORT_SIZE ( 16                                     ) ,\ | ||||
|         HID_REPORT_COUNT( 2                                      ) ,\ | ||||
|         HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_RELATIVE ) ,\ | ||||
|         /* Vertical wheel scroll [-127, 127] */ \ | ||||
|         HID_USAGE       ( HID_USAGE_DESKTOP_WHEEL                )  ,\ | ||||
|         HID_LOGICAL_MIN ( 0x81                                   )  ,\ | ||||
|         HID_LOGICAL_MAX ( 0x7f                                   )  ,\ | ||||
|         HID_REPORT_COUNT( 1                                      )  ,\ | ||||
|         HID_REPORT_SIZE ( 8                                      )  ,\ | ||||
|         HID_INPUT       ( HID_DATA | HID_VARIABLE | HID_RELATIVE )  ,\ | ||||
|         /* Mouse mode (0 = absolute, 1 = relative) */ \ | ||||
|         HID_REPORT_COUNT( 1                                      ), \ | ||||
|         HID_REPORT_SIZE ( 8                                      ), \ | ||||
|         HID_INPUT       ( HID_CONSTANT                           ), \ | ||||
|     HID_COLLECTION_END                                            , \ | ||||
|   HID_COLLECTION_END \ | ||||
| 
 | ||||
| #endif /* USB_DESCRIPTORS_H_ */ | ||||
|  |  | |||
|  | @ -23,6 +23,10 @@ | |||
|  * | ||||
|  * defined as HID_KEY_<something> | ||||
|  * | ||||
|  * In addition, keyboard.c defines right ctrl as a modifier key required to | ||||
|  * activate this. So, the current shortcut is RIGHT CTRL + whatever is defined | ||||
|  * here. | ||||
|  * | ||||
|  * If you do not want to use a key for switching outputs, you may be tempted | ||||
|  * to select HID_KEY_NONE here; don't do that! That code appears in many HID | ||||
|  * messages and the result will be a non-functional keyboard. Instead, choose | ||||
|  | @ -32,7 +36,7 @@ | |||
|  * | ||||
|  * */ | ||||
| 
 | ||||
| #define HOTKEY_TOGGLE HID_KEY_F24 | ||||
| #define HOTKEY_TOGGLE HID_KEY_CAPS_LOCK | ||||
| 
 | ||||
| /**================================================== *
 | ||||
|  * ==============  Mouse Speed Factor  ============== * | ||||
|  | @ -53,13 +57,13 @@ | |||
|  * | ||||
|  * */ | ||||
| 
 | ||||
| /* Output A values */ | ||||
| /* Output A values, default is for the most common ~ 16:9 ratio screen */ | ||||
| #define MOUSE_SPEED_A_FACTOR_X 16 | ||||
| #define MOUSE_SPEED_A_FACTOR_Y 16 | ||||
| #define MOUSE_SPEED_A_FACTOR_Y 28 | ||||
| 
 | ||||
| /* Output B values  */ | ||||
| /* Output B values, default is for the most common ~ 16:9 ratio screen */ | ||||
| #define MOUSE_SPEED_B_FACTOR_X 16 | ||||
| #define MOUSE_SPEED_B_FACTOR_Y 16 | ||||
| #define MOUSE_SPEED_B_FACTOR_Y 28 | ||||
| 
 | ||||
| #define JUMP_THRESHOLD 0 | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ | |||
| 
 | ||||
| hotkey_combo_t hotkeys[] = { | ||||
|     /* Main keyboard switching hotkey */ | ||||
|     {.modifier       = 0, | ||||
|     {.modifier       = KEYBOARD_MODIFIER_LEFTCTRL, | ||||
|      .keys           = {HOTKEY_TOGGLE}, | ||||
|      .key_count      = 1, | ||||
|      .pass_to_os     = false, | ||||
|  | @ -51,6 +51,13 @@ hotkey_combo_t hotkeys[] = { | |||
|      .acknowledge    = true, | ||||
|      .action_handler = &screenlock_hotkey_handler}, | ||||
| 
 | ||||
|     /* Toggle gaming mode */ | ||||
|     {.modifier       = KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT, | ||||
|      .keys           = {HID_KEY_G}, | ||||
|      .key_count      = 1, | ||||
|      .acknowledge    = true, | ||||
|      .action_handler = &toggle_relative_mode_handler}, | ||||
| 
 | ||||
|     /* Erase stored config */ | ||||
|     {.modifier       = KEYBOARD_MODIFIER_RIGHTSHIFT, | ||||
|      .keys           = {HID_KEY_F12, HID_KEY_D}, | ||||
|  | @ -185,7 +192,7 @@ 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); | ||||
|         queue_cc_packet(raw_report, state); | ||||
|         state->last_activity[BOARD_ROLE] = time_us_64(); | ||||
|     } else { | ||||
|         queue_packet((uint8_t *)raw_report, CONSUMER_CONTROL_MSG, CONSUMER_CONTROL_LENGTH); | ||||
|  | @ -195,7 +202,7 @@ void send_consumer_control(uint8_t *raw_report, device_t *state) { | |||
| /* Decide if consumer control reports go local or to the other board */ | ||||
| void send_system_control(uint8_t *raw_report, device_t *state) { | ||||
|     if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { | ||||
|         tud_hid_n_report(0, REPORT_ID_SYSTEM, raw_report, SYSTEM_CONTROL_LENGTH); | ||||
|         queue_system_packet(raw_report, state); | ||||
|         state->last_activity[BOARD_ROLE] = time_us_64(); | ||||
|     } else { | ||||
|         queue_packet((uint8_t *)raw_report, SYSTEM_CONTROL_MSG, SYSTEM_CONTROL_LENGTH); | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ int main(void) { | |||
|         [1] = {.exec = &kick_watchdog_task,       .frequency = _HZ(30)},     // | Verify core1 is still running and if so, reset watchdog timer
 | ||||
|         [2] = {.exec = &process_kbd_queue_task,   .frequency = _HZ(2000)},   // | Check if there were any keypresses and send them
 | ||||
|         [3] = {.exec = &process_mouse_queue_task, .frequency = _HZ(2000)},   // | Check if there were any mouse movements and send them
 | ||||
|         [4] = {.exec = &process_cfg_queue_task,   .frequency = _HZ(1000)},   // | Check if there are any packets to send over vendor link
 | ||||
|         [4] = {.exec = &process_hid_queue_task,   .frequency = _HZ(1000)},   // | Check if there are any packets to send over vendor link
 | ||||
|         [5] = {.exec = &process_uart_tx_task,     .frequency = _TOP()},      // | Check if there are any packets to send over UART
 | ||||
|     };                                                                       // `----- then go back and repeat forever
 | ||||
|     const int NUM_TASKS = ARRAY_SIZE(tasks_core0); | ||||
|  |  | |||
							
								
								
									
										30
									
								
								src/mouse.c
								
								
								
								
							
							
						
						
									
										30
									
								
								src/mouse.c
								
								
								
								
							|  | @ -64,6 +64,10 @@ void update_mouse_position(device_t *state, mouse_values_t *values) { | |||
|     output_t *current    = &state->config.output[state->active_output]; | ||||
|     uint8_t reduce_speed = 0; | ||||
| 
 | ||||
|     /* If relative mouse mode is active, just pass mouse movements and update nothing */ | ||||
|     if (state->relative_mouse) | ||||
|         return; | ||||
| 
 | ||||
|     /* Check if we are configured to move slowly */ | ||||
|     if (state->mouse_zoom) | ||||
|         reduce_speed = MOUSE_ZOOM_SCALING_FACTOR; | ||||
|  | @ -141,13 +145,13 @@ void switch_desktop(device_t *state, output_t *output, int new_index, int direct | |||
|     /* Fix for MACOS: Send relative mouse movement here, 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) ? SCREEN_MIDPOINT - 2 : SCREEN_MIDPOINT + 2, .mode = RELATIVE}; | ||||
|         = {.x = (direction == LEFT) ? -5 : 5, .mode = RELATIVE}; | ||||
| 
 | ||||
|     switch (output->os) { | ||||
|         case MACOS: | ||||
|             /* Once doesn't seem reliable enough, do it twice */ | ||||
|             output_mouse_report(&move_relative_one, state); | ||||
|             output_mouse_report(&move_relative_one, state); | ||||
|             /* Once doesn't seem reliable enough, do it a few times */ | ||||
|             for (int i = 0; i < 5; i++) | ||||
|                 output_mouse_report(&move_relative_one, state); | ||||
|             break; | ||||
| 
 | ||||
|         case WINDOWS: | ||||
|  | @ -184,8 +188,8 @@ void check_screen_switch(const mouse_values_t *values, device_t *state) { | |||
| 
 | ||||
|     int direction = jump_left ? LEFT : RIGHT; | ||||
| 
 | ||||
|     /* No switching allowed if explicitly disabled or mouse button is held */ | ||||
|     if (state->switch_lock || state->mouse_buttons) | ||||
|     /* No switching allowed if explicitly disabled */ | ||||
|     if (state->switch_lock) | ||||
|         return; | ||||
| 
 | ||||
|     /* No jump condition met == nothing to do, return */ | ||||
|  | @ -194,9 +198,13 @@ void check_screen_switch(const mouse_values_t *values, device_t *state) { | |||
| 
 | ||||
|     /* 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 (output->screen_index == 1) { /* We are at the border -> switch outputs */ | ||||
|             /* No switching allowed if mouse button is held. Should only apply to the border! */ | ||||
|             if (state->mouse_buttons) | ||||
|                 return; | ||||
| 
 | ||||
|             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); | ||||
|  | @ -240,11 +248,9 @@ mouse_report_t create_mouse_report(device_t *state, mouse_values_t *values) { | |||
| 
 | ||||
|     /* Workaround for Windows multiple desktops */ | ||||
|     if (state->relative_mouse) { | ||||
|         mouse_report.x = SCREEN_MIDPOINT + values->move_x; | ||||
|         mouse_report.y = SCREEN_MIDPOINT + values->move_y; | ||||
|         mouse_report.x = values->move_x; | ||||
|         mouse_report.y = values->move_y; | ||||
|         mouse_report.mode = RELATIVE; | ||||
|         mouse_report.buttons = values->buttons; | ||||
|         mouse_report.wheel = values->wheel; | ||||
|     } | ||||
| 
 | ||||
|     return mouse_report; | ||||
|  |  | |||
|  | @ -66,4 +66,39 @@ const field_map_t* get_field_map_entry(uint32_t index) { | |||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| const field_map_t* get_field_map_index(uint32_t index) { | ||||
|     return &api_field_map[index]; | ||||
| } | ||||
| 
 | ||||
| size_t get_field_map_length(void) { | ||||
|     return ARRAY_SIZE(api_field_map); | ||||
| } | ||||
| 
 | ||||
| void _queue_packet(uint8_t *payload, device_t *state, uint8_t type, uint8_t len, uint8_t id, uint8_t inst) { | ||||
|     hid_generic_pkt_t generic_packet = { | ||||
|         .instance = inst, | ||||
|         .report_id = id, | ||||
|         .type = type, | ||||
|         .len = len, | ||||
|     }; | ||||
| 
 | ||||
|     memcpy(generic_packet.data, payload, len); | ||||
|     queue_try_add(&state->hid_queue_out, &generic_packet); | ||||
| } | ||||
| 
 | ||||
| void queue_cfg_packet(uart_packet_t *packet, device_t *state) { | ||||
|     uint8_t raw_packet[RAW_PACKET_LENGTH]; | ||||
|     write_raw_packet(raw_packet, packet); | ||||
|     _queue_packet(raw_packet, state, 0, RAW_PACKET_LENGTH, REPORT_ID_VENDOR, ITF_NUM_HID_VENDOR); | ||||
| } | ||||
| 
 | ||||
| void queue_cc_packet(uint8_t *payload, device_t *state) { | ||||
|     _queue_packet(payload, state, 1, CONSUMER_CONTROL_LENGTH, REPORT_ID_CONSUMER, ITF_NUM_HID); | ||||
| } | ||||
| 
 | ||||
| void queue_system_packet(uint8_t *payload, device_t *state) { | ||||
|     _queue_packet(payload, state, 2, SYSTEM_CONTROL_LENGTH, REPORT_ID_SYSTEM, ITF_NUM_HID); | ||||
| } | ||||
|  |  | |||
|  | @ -237,8 +237,8 @@ void initial_setup(device_t *state) { | |||
|     queue_init(&state->kbd_queue, sizeof(hid_keyboard_report_t), KBD_QUEUE_LENGTH); | ||||
|     queue_init(&state->mouse_queue, sizeof(mouse_report_t), MOUSE_QUEUE_LENGTH); | ||||
| 
 | ||||
|     /* Initialize vendor config protocol queue */ | ||||
|     queue_init(&state->cfg_queue_out, sizeof(uart_packet_t), CFG_QUEUE_LENGTH); | ||||
|     /* Initialize generic HID packet queue */ | ||||
|     queue_init(&state->hid_queue_out, sizeof(hid_generic_pkt_t), HID_QUEUE_LENGTH); | ||||
| 
 | ||||
|     /* Initialize UART queue */ | ||||
|     queue_init(&state->uart_tx_queue, sizeof(uart_packet_t), UART_QUEUE_LENGTH); | ||||
|  | @ -267,4 +267,4 @@ void initial_setup(device_t *state) { | |||
|     watchdog_enable(WATCHDOG_TIMEOUT, WATCHDOG_PAUSE_ON_DEBUG); | ||||
| } | ||||
| 
 | ||||
| /* ==========  End of Initial Board Setup  ========== */ | ||||
| /* ==========  End of Initial Board Setup  ========== */ | ||||
|  |  | |||
							
								
								
									
										20
									
								
								src/tasks.c
								
								
								
								
							
							
						
						
									
										20
									
								
								src/tasks.c
								
								
								
								
							|  | @ -148,25 +148,23 @@ void heartbeat_output_task(device_t *state) { | |||
|     queue_try_add(&global_state.uart_tx_queue, &packet); | ||||
| } | ||||
| 
 | ||||
| /* Process outgoing config report messages. */ | ||||
| void process_cfg_queue_task(device_t *state) { | ||||
|     uint8_t raw_packet[RAW_PACKET_LENGTH] = {[0] = START1, [1] = START2, [11] = 0}; | ||||
|     uart_packet_t packet; | ||||
| 
 | ||||
|     if (!queue_try_peek(&state->cfg_queue_out, &packet)) | ||||
| /* Process other outgoing hid report messages. */ | ||||
| void process_hid_queue_task(device_t *state) { | ||||
|     hid_generic_pkt_t packet; | ||||
| 
 | ||||
|     if (!queue_try_peek(&state->hid_queue_out, &packet)) | ||||
|         return; | ||||
| 
 | ||||
|     if (!tud_hid_n_ready(ITF_NUM_HID_VENDOR)) | ||||
|     if (!tud_hid_n_ready(packet.instance)) | ||||
|         return; | ||||
| 
 | ||||
|     write_raw_packet(raw_packet, &packet); | ||||
| 
 | ||||
|     /* ... try sending it to the host, if it's successful */ | ||||
|     bool succeeded = tud_hid_n_report(ITF_NUM_HID_VENDOR, REPORT_ID_VENDOR, raw_packet, RAW_PACKET_LENGTH); | ||||
|     bool succeeded = tud_hid_n_report(packet.instance, packet.report_id, packet.data, packet.len); | ||||
| 
 | ||||
|     /* ... then we can remove it from the queue. Race conditions shouldn't happen [tm] */ | ||||
|     if (succeeded) | ||||
|         queue_try_remove(&state->cfg_queue_out, &packet); | ||||
| 	queue_try_remove(&state->hid_queue_out, &packet); | ||||
| } | ||||
| 
 | ||||
| /* Task that handles copying firmware from the other device to ours */ | ||||
|  | @ -221,4 +219,4 @@ void packet_receiver_task(device_t *state) { | |||
|         state->dma_ptr = NEXT_RING_IDX(state->dma_ptr); | ||||
|         delta--; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -76,6 +76,7 @@ const uart_handler_t uart_handler[] = { | |||
|     {.type = SWITCH_LOCK_MSG, .handler = handle_switch_lock_msg}, | ||||
|     {.type = SYNC_BORDERS_MSG, .handler = handle_sync_borders_msg}, | ||||
|     {.type = FLASH_LED_MSG, .handler = handle_flash_led_msg}, | ||||
|     {.type = RELATIVE_MODE_MSG, .handler = handle_toggle_relative_msg}, | ||||
|     {.type = CONSUMER_CONTROL_MSG, .handler = handle_consumer_control_msg}, | ||||
| 
 | ||||
|     /* Config */ | ||||
|  | @ -83,6 +84,7 @@ const uart_handler_t uart_handler[] = { | |||
|     {.type = SAVE_CONFIG_MSG, .handler = handle_save_config_msg}, | ||||
|     {.type = REBOOT_MSG, .handler = handle_reboot_msg}, | ||||
|     {.type = GET_VAL_MSG, .handler = handle_api_msgs}, | ||||
|     {.type = GET_ALL_VALS_MSG, .handler = handle_api_read_all_msg}, | ||||
|     {.type = SET_VAL_MSG, .handler = handle_api_msgs}, | ||||
| 
 | ||||
|     /* Firmware */ | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ uint8_t const desc_hid_report[] = {TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(RE | |||
|                                    TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(REPORT_ID_SYSTEM)) | ||||
|                                    }; | ||||
| 
 | ||||
| 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_MOUSEHELP(HID_REPORT_ID(REPORT_ID_RELMOUSE))}; | ||||
| 
 | ||||
| uint8_t const desc_hid_report_vendor[] = {TUD_HID_REPORT_DESC_VENDOR_CTRL(HID_REPORT_ID(REPORT_ID_VENDOR))}; | ||||
| 
 | ||||
|  | @ -82,15 +82,16 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) { | |||
| } | ||||
| 
 | ||||
| bool tud_mouse_report(uint8_t mode, uint8_t buttons, int16_t x, int16_t y, int8_t wheel) { | ||||
|     mouse_report_t report = {.buttons = buttons, .wheel = wheel, .x = x, .y = y, .mode = mode}; | ||||
|     uint8_t instance = ITF_NUM_HID; | ||||
|     uint8_t report_id = REPORT_ID_MOUSE; | ||||
| 
 | ||||
|     if (mode == ABSOLUTE) { | ||||
|         mouse_report_t report = {.buttons = buttons, .x = x, .y = y, .wheel = wheel}; | ||||
|         return tud_hid_n_report(ITF_NUM_HID, REPORT_ID_MOUSE, &report, sizeof(report)); | ||||
|     } else { | ||||
|         hid_mouse_report_t report | ||||
|             = {.buttons = buttons, .x = x - SCREEN_MIDPOINT, .y = y - SCREEN_MIDPOINT, .wheel = wheel, .pan = 0}; | ||||
|         return tud_hid_n_report(ITF_NUM_HID_REL_M, REPORT_ID_RELMOUSE, &report, sizeof(report)); | ||||
|     if (mode == RELATIVE) { | ||||
|         instance = ITF_NUM_HID_REL_M; | ||||
|         report_id = REPORT_ID_RELMOUSE; | ||||
|     } | ||||
| 
 | ||||
|     return tud_hid_n_report(instance, report_id, &report, sizeof(report)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -195,6 +195,7 @@ bool validate_packet(uart_packet_t *packet) { | |||
|     const enum packet_type_e ALLOWED_PACKETS[] = { | ||||
|         FLASH_LED_MSG, | ||||
|         GET_VAL_MSG, | ||||
| 	GET_ALL_VALS_MSG, | ||||
|         SET_VAL_MSG, | ||||
|         WIPE_CONFIG_MSG, | ||||
|         SAVE_CONFIG_MSG, | ||||
|  |  | |||
|  | @ -1,2 +1,2 @@ | |||
| render: | ||||
| 	python3 render.py | ||||
| 	python3 -B render.py | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ | |||
| :root { | ||||
|   --highlight-color: #384955; | ||||
|   --font-color: #384955; | ||||
|   --highlight-color2: #5e9f41;   | ||||
|   --highlight-color2: #5e9f41; | ||||
| } | ||||
| 
 | ||||
| html { | ||||
|  | @ -492,7 +492,7 @@ input[type='number'].input-inline { | |||
| @media (min-width: 40rem) { | ||||
|   .row { | ||||
|     flex-direction: row; | ||||
|      | ||||
| 
 | ||||
|     width: calc(100% + 2.0rem); | ||||
|   } | ||||
|   .row .column { | ||||
|  | @ -681,14 +681,14 @@ img { | |||
| 
 | ||||
|   <main class="wrapper"> | ||||
| 
 | ||||
|     <section class="container">     | ||||
|     <section class="container"> | ||||
| 
 | ||||
|       <div class="row" id="warning" style="display: none;">  | ||||
|       <div class="row" id="warning" style="display: none;"> | ||||
|         <blockquote> | ||||
|           <h3> Oh, no! </h3> | ||||
|           Unfortunately, your browser does not support WebHID or the Permissions Policy is blocking its usage. Please try Chromium <br /> | ||||
|           (or Chrome if you have to). Apologies for the inconvenience, hopefully a cross-browser solution happens soon. | ||||
|         </blockquote>  | ||||
|         </blockquote> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="row"> | ||||
|  | @ -716,7 +716,7 @@ img { | |||
|               <path d="m 104.01516,99.341366 a 1.8639801,1.8639801 0 0 0 -1.86329,1.863284 1.8639801,1.8639801 0 0 0 1.86329,1.86328 h 27.4375 a 1.8639801,1.8639801 0 0 0 1.86718,-1.86328 1.8639801,1.8639801 0 0 0 -1.86718,-1.863284 z" /> | ||||
|             </g> | ||||
|           </svg> | ||||
|          | ||||
| 
 | ||||
|           <div id="menu-buttons"> | ||||
|             <button data-handler="connectHandler" class="button button-clear button-shifted">Connect</button><br /> | ||||
|             <button data-handler="readHandler" class="button button-clear button-shifted online">Read</button><br /> | ||||
|  | @ -727,8 +727,8 @@ img { | |||
|             <button data-handler="blinkHandler" class="button button-clear button-shifted online">Blink</button><br /> | ||||
|             <button data-handler="blinkBothHandler" class="button button-clear button-shifted online">Blink both</button><br /> | ||||
|             <button data-handler="enterBootloaderHandler" class="button button-clear button-shifted online">Bootloader</button><br /> | ||||
|             <button data-handler="wipeConfigHandler" class="button button-clear button-shifted online">Wipe Config</button><br />                        | ||||
|              | ||||
|             <button data-handler="wipeConfigHandler" class="button button-clear button-shifted online">Wipe Config</button><br /> | ||||
| 
 | ||||
|           </div> | ||||
| 
 | ||||
|         </div> | ||||
|  | @ -757,20 +757,20 @@ img { | |||
|      | ||||
| <label class=""> Screen Count</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint32" data-key="11" required>                  | ||||
|     <select class="api" data-type="uint32" data-key="11" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">1</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">2</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">3</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -789,22 +789,22 @@ img { | |||
| 
 | ||||
| 
 | ||||
|        | ||||
| <input class="input-inline" type="number" name="aInput12" data-type="int32" data-key="12"  | ||||
| <input class="input-inline" type="number" name="aInput12" data-type="int32" data-key="12" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         readonly oninput="this.form.aRange12.value=this.value" /> | ||||
| 
 | ||||
|        | ||||
|        | ||||
| <input class="range api" type="range" name="aRange12" data-type="int32" data-key="12"  | ||||
| <input class="range api" type="range" name="aRange12" data-type="int32" data-key="12" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         min="" max="" oninput="this.form.aInput12.value=this.value" />                | ||||
|         min="" max="" oninput="this.form.aInput12.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -823,22 +823,22 @@ img { | |||
| 
 | ||||
| 
 | ||||
|        | ||||
| <input class="input-inline" type="number" name="aInput13" data-type="int32" data-key="13"  | ||||
| <input class="input-inline" type="number" name="aInput13" data-type="int32" data-key="13" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         readonly oninput="this.form.aRange13.value=this.value" /> | ||||
| 
 | ||||
|        | ||||
|        | ||||
| <input class="range api" type="range" name="aRange13" data-type="int32" data-key="13"  | ||||
| <input class="range api" type="range" name="aRange13" data-type="int32" data-key="13" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         min="" max="" oninput="this.form.aInput13.value=this.value" />                | ||||
|         min="" max="" oninput="this.form.aInput13.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -854,12 +854,12 @@ img { | |||
| <label class=""> Border Top</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name14" data-type="int32" data-key="14"  | ||||
| <input class="api" type="text" name="name14" data-type="int32" data-key="14" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -875,12 +875,12 @@ img { | |||
| <label class=""> Border Bottom</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name15" data-type="int32" data-key="15"  | ||||
| <input class="api" type="text" name="name15" data-type="int32" data-key="15" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -895,24 +895,24 @@ img { | |||
|      | ||||
| <label class=""> Operating System</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="16" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="16" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">Linux</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">MacOS</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">Windows</option> | ||||
|          | ||||
|      | ||||
|     <option value="4">Android</option> | ||||
|          | ||||
|      | ||||
|     <option value="255">Other</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -927,18 +927,18 @@ img { | |||
|      | ||||
| <label class=""> Screen Position</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="17" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="17" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">Left</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">Right</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -953,20 +953,20 @@ img { | |||
|      | ||||
| <label class=""> Cursor Park Position</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="18" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="18" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="0">Top</option> | ||||
|          | ||||
|      | ||||
|     <option value="1">Bottom</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">Previous</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -983,7 +983,7 @@ img { | |||
| 
 | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -998,20 +998,20 @@ img { | |||
|      | ||||
| <label class=""> Mode</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="19" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="19" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="0">Disabled</option> | ||||
|          | ||||
|      | ||||
|     <option value="1">Pong</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">Jitter</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1026,16 +1026,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Only If Inactive</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name20" data-type="uint8" data-key="20"  | ||||
| <input class="api" type="checkbox" name="name20" data-type="uint8" data-key="20" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1051,12 +1051,12 @@ img { | |||
| <label class=""> Idle Time (μs)</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name21" data-type="uint64" data-key="21"  | ||||
| <input class="api" type="text" name="name21" data-type="uint64" data-key="21" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1072,12 +1072,12 @@ img { | |||
| <label class=""> Max Time (μs)</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name22" data-type="uint64" data-key="22"  | ||||
| <input class="api" type="text" name="name22" data-type="uint64" data-key="22" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
| 
 | ||||
|         </div> | ||||
|  | @ -1088,7 +1088,7 @@ img { | |||
|             <line x1="50" y1="90" x2="50" y2="75" stroke="black" stroke-width="2" /> | ||||
|             <rect x="30" y="90" width="40" height="3" stroke="black" stroke-width="2" fill="#d7e5f0" rx="5" ry="5" /> | ||||
|           </svg> | ||||
|            | ||||
| 
 | ||||
|             <h3>Output B</h3> | ||||
| 
 | ||||
|              | ||||
|  | @ -1105,20 +1105,20 @@ img { | |||
|      | ||||
| <label class=""> Screen Count</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint32" data-key="41" required>                  | ||||
|     <select class="api" data-type="uint32" data-key="41" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">1</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">2</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">3</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1137,22 +1137,22 @@ img { | |||
| 
 | ||||
| 
 | ||||
|        | ||||
| <input class="input-inline" type="number" name="aInput42" data-type="int32" data-key="42"  | ||||
| <input class="input-inline" type="number" name="aInput42" data-type="int32" data-key="42" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         readonly oninput="this.form.aRange42.value=this.value" /> | ||||
| 
 | ||||
|        | ||||
|        | ||||
| <input class="range api" type="range" name="aRange42" data-type="int32" data-key="42"  | ||||
| <input class="range api" type="range" name="aRange42" data-type="int32" data-key="42" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         min="" max="" oninput="this.form.aInput42.value=this.value" />                | ||||
|         min="" max="" oninput="this.form.aInput42.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1171,22 +1171,22 @@ img { | |||
| 
 | ||||
| 
 | ||||
|        | ||||
| <input class="input-inline" type="number" name="aInput43" data-type="int32" data-key="43"  | ||||
| <input class="input-inline" type="number" name="aInput43" data-type="int32" data-key="43" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         readonly oninput="this.form.aRange43.value=this.value" /> | ||||
| 
 | ||||
|        | ||||
|        | ||||
| <input class="range api" type="range" name="aRange43" data-type="int32" data-key="43"  | ||||
| <input class="range api" type="range" name="aRange43" data-type="int32" data-key="43" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         min="" max="" oninput="this.form.aInput43.value=this.value" />                | ||||
|         min="" max="" oninput="this.form.aInput43.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1202,12 +1202,12 @@ img { | |||
| <label class=""> Border Top</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name44" data-type="int32" data-key="44"  | ||||
| <input class="api" type="text" name="name44" data-type="int32" data-key="44" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1223,12 +1223,12 @@ img { | |||
| <label class=""> Border Bottom</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name45" data-type="int32" data-key="45"  | ||||
| <input class="api" type="text" name="name45" data-type="int32" data-key="45" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1243,24 +1243,24 @@ img { | |||
|      | ||||
| <label class=""> Operating System</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="46" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="46" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">Linux</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">MacOS</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">Windows</option> | ||||
|          | ||||
|      | ||||
|     <option value="4">Android</option> | ||||
|          | ||||
|      | ||||
|     <option value="255">Other</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1275,18 +1275,18 @@ img { | |||
|      | ||||
| <label class=""> Screen Position</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="47" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="47" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="1">Left</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">Right</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1301,20 +1301,20 @@ img { | |||
|      | ||||
| <label class=""> Cursor Park Position</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="48" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="48" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="0">Top</option> | ||||
|          | ||||
|      | ||||
|     <option value="1">Bottom</option> | ||||
|          | ||||
|      | ||||
|     <option value="3">Previous</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1331,7 +1331,7 @@ img { | |||
| 
 | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1346,20 +1346,20 @@ img { | |||
|      | ||||
| <label class=""> Mode</label> | ||||
| 
 | ||||
|     <select class="api" data-type="uint8" data-key="49" required>                  | ||||
|     <select class="api" data-type="uint8" data-key="49" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|          | ||||
|      | ||||
|     <option value="0">Disabled</option> | ||||
|          | ||||
|      | ||||
|     <option value="1">Pong</option> | ||||
|          | ||||
|      | ||||
|     <option value="2">Jitter</option> | ||||
|      | ||||
|     </select><br /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1374,16 +1374,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Only If Inactive</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name50" data-type="uint8" data-key="50"  | ||||
| <input class="api" type="checkbox" name="name50" data-type="uint8" data-key="50" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1399,12 +1399,12 @@ img { | |||
| <label class=""> Idle Time (μs)</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name51" data-type="uint64" data-key="51"  | ||||
| <input class="api" type="text" name="name51" data-type="uint64" data-key="51" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1420,14 +1420,14 @@ img { | |||
| <label class=""> Max Time (μs)</label> | ||||
| 
 | ||||
|        | ||||
| <input class="api" type="text" name="name52" data-type="uint64" data-key="52"  | ||||
| <input class="api" type="text" name="name52" data-type="uint64" data-key="52" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|    | ||||
|                             | ||||
| 
 | ||||
|              | ||||
|            | ||||
| 
 | ||||
|         </div> | ||||
| 
 | ||||
|       </div> | ||||
|  | @ -1459,7 +1459,7 @@ img { | |||
| 
 | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1474,16 +1474,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Force Mouse Boot Mode</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name71" data-type="uint8" data-key="71"  | ||||
| <input class="api" type="checkbox" name="name71" data-type="uint8" data-key="71" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1498,16 +1498,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Enable Acceleration</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name75" data-type="uint8" data-key="75"  | ||||
| <input class="api" type="checkbox" name="name75" data-type="uint8" data-key="75" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1526,22 +1526,22 @@ img { | |||
| 
 | ||||
| 
 | ||||
|        | ||||
| <input class="input-inline" type="number" name="aInput77" data-type="uint16" data-key="77"  | ||||
| <input class="input-inline" type="number" name="aInput77" data-type="uint16" data-key="77" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         readonly oninput="this.form.aRange77.value=this.value" /> | ||||
| 
 | ||||
|        | ||||
|        | ||||
| <input class="range api" type="range" name="aRange77" data-type="uint16" data-key="77"  | ||||
| <input class="range api" type="range" name="aRange77" data-type="uint16" data-key="77" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| 
 | ||||
|         min="" max="" oninput="this.form.aInput77.value=this.value" />                | ||||
|         min="" max="" oninput="this.form.aInput77.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1558,7 +1558,7 @@ img { | |||
| 
 | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1573,16 +1573,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Force KBD Boot Protocol</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name72" data-type="uint8" data-key="72"  | ||||
| <input class="api" type="checkbox" name="name72" data-type="uint8" data-key="72" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1597,16 +1597,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> KBD LED as Indicator</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name73" data-type="uint8" data-key="73"  | ||||
| <input class="api" type="checkbox" name="name73" data-type="uint8" data-key="73" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
|                | ||||
| 
 | ||||
|  | @ -1621,16 +1621,16 @@ img { | |||
|   <div class="clearfix"> | ||||
|      | ||||
| <label class="label-inline"> Enforce Ports</label> | ||||
| 
 | ||||
|      | ||||
|      | ||||
| <input class="api" type="checkbox" name="name76" data-type="uint8" data-key="76"  | ||||
| <input class="api" type="checkbox" name="name76" data-type="uint8" data-key="76" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|   /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|              | ||||
| 
 | ||||
|           <input type="submit" value="Save" id="submitButton"> | ||||
|  | @ -1654,12 +1654,12 @@ img { | |||
| <label class="label-inline"> Running FW version:</label> | ||||
| 
 | ||||
|      | ||||
| <input class="content api" type="text" name="name78" data-type="uint16" data-key="78"  | ||||
| <input class="content api" type="text" name="name78" data-type="uint16" data-key="78" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|  data-hex readonly /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|            | ||||
|              | ||||
| 
 | ||||
|  | @ -1675,12 +1675,12 @@ img { | |||
| <label class="label-inline"> Running FW checksum:</label> | ||||
| 
 | ||||
|      | ||||
| <input class="content api" type="text" name="name79" data-type="uint32" data-key="79"  | ||||
| <input class="content api" type="text" name="name79" data-type="uint32" data-key="79" | ||||
|   onchange="valueChangedHandler(this)" | ||||
|  data-hex readonly /> | ||||
| 
 | ||||
|    | ||||
|               | ||||
| 
 | ||||
|            | ||||
| 
 | ||||
|         </div> | ||||
|  | @ -1697,7 +1697,7 @@ var device; | |||
| const packetType = { | ||||
|   keyboardReportMsg: 1, mouseReportMsg: 2, outputSelectMsg: 3, firmwareUpgradeMsg: 4, switchLockMsg: 7, | ||||
|   syncBordersMsg: 8, flashLedMsg: 9, wipeConfigMsg: 10, readConfigMsg: 16, writeConfigMsg: 17, saveConfigMsg: 18, | ||||
|   rebootMsg: 19, getValMsg: 20, setValMsg: 21, proxyPacketMsg: 23 | ||||
|   rebootMsg: 19, getValMsg: 20, setValMsg: 21, getValAllMsg: 22, proxyPacketMsg: 23 | ||||
| }; | ||||
| 
 | ||||
| function calcChecksum(report) { | ||||
|  | @ -1710,26 +1710,26 @@ function calcChecksum(report) { | |||
| 
 | ||||
| async function sendReport(type, payload = [], sendBoth = false) { | ||||
|   if (!device || !device.opened) | ||||
|     return;   | ||||
|    | ||||
|     return; | ||||
| 
 | ||||
|   /* First send this one, if the first one gets e.g. rebooted */ | ||||
|   if (sendBoth) {       | ||||
|   if (sendBoth) { | ||||
|     var reportProxy = makeReport(type, payload, true); | ||||
|     await device.sendReport(mgmtReportId, reportProxy); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     var report = makeReport(type, payload, false); | ||||
|     await device.sendReport(mgmtReportId, report);    | ||||
|     await device.sendReport(mgmtReportId, report); | ||||
| } | ||||
| 
 | ||||
| function makeReport(type, payload, proxy=false) { | ||||
|   var dataOffset = proxy ? 4 : 3; | ||||
|   report = new Uint8Array([0xaa, 0x55, type, ...new Array(9).fill(0)]);    | ||||
|   report = new Uint8Array([0xaa, 0x55, type, ...new Array(9).fill(0)]); | ||||
| 
 | ||||
|   if (proxy)  | ||||
|     report = new Uint8Array([0xaa, 0x55, packetType.proxyPacketMsg, type, ...new Array(7).fill(0), type]);   | ||||
|        | ||||
|   if (payload) {     | ||||
|   if (proxy) | ||||
|     report = new Uint8Array([0xaa, 0x55, packetType.proxyPacketMsg, type, ...new Array(7).fill(0), type]); | ||||
| 
 | ||||
|   if (payload) { | ||||
|     report.set([...payload], dataOffset); | ||||
|     report[report.length - 1] = calcChecksum(report); | ||||
|   } | ||||
|  | @ -1784,7 +1784,8 @@ async function connectHandler() { | |||
|   }); | ||||
| 
 | ||||
|   device = devices[0]; | ||||
|   device.open().then(async () => {      | ||||
|   device.open().then(async () => { | ||||
|     device.addEventListener('inputreport', handleInputReport); | ||||
|     document.querySelectorAll('.online').forEach(element => { element.style.opacity = 1.0; }); | ||||
|     await readHandler(); | ||||
|   }); | ||||
|  | @ -1816,8 +1817,12 @@ function setValue(element, value) { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| function updateElement(element, event, dataType) { | ||||
|   var dataOffset = 3; | ||||
| function updateElement(key, event) { | ||||
|   var dataOffset = 4; | ||||
|   var element = document.querySelector(`[data-key="${key}"]`); | ||||
| 
 | ||||
|   if (!element) | ||||
|     return; | ||||
| 
 | ||||
|   const methods = { | ||||
|     "uint32": event.data.getUint32, | ||||
|  | @ -1829,6 +1834,8 @@ function updateElement(element, event, dataType) { | |||
|     "int8": event.data.getInt8 | ||||
|   }; | ||||
| 
 | ||||
|   dataType = element.getAttribute('data-type'); | ||||
| 
 | ||||
|   if (dataType in methods) { | ||||
|     var value = methods[dataType].call(event.data, dataOffset, true); | ||||
|     setValue(element, value); | ||||
|  | @ -1842,24 +1849,14 @@ async function readHandler() { | |||
|   if (!device || !device.opened) | ||||
|     await connectHandler(); | ||||
| 
 | ||||
|   const elements = document.querySelectorAll('.api'); | ||||
|   await sendReport(packetType.getValAllMsg); | ||||
| } | ||||
| 
 | ||||
|   for (const element of elements) { | ||||
|     var key = element.getAttribute('data-key'); | ||||
|     var dataType = element.getAttribute('data-type'); | ||||
| async function handleInputReport(event) { | ||||
|   var data = new Uint8Array(event.data.buffer); | ||||
|   var key = data[3]; | ||||
| 
 | ||||
|     await sendReport(packetType.getValMsg, [key]); | ||||
| 
 | ||||
|     let incomingReport = await new Promise((resolve, reject) => { | ||||
|       const handleInputReport = (event) => { | ||||
|         updateElement(element, event, dataType); | ||||
| 
 | ||||
|         device.removeEventListener('inputreport', handleInputReport); | ||||
|         resolve(); | ||||
|       } | ||||
|       device.addEventListener('inputreport', handleInputReport); | ||||
|     }); | ||||
|   } | ||||
|   updateElement(key, event); | ||||
| } | ||||
| 
 | ||||
| async function rebootHandler() { | ||||
|  | @ -1879,7 +1876,7 @@ async function valueChangedHandler(element) { | |||
| 
 | ||||
|   if (origValue != newValue) { | ||||
|     uintBuffer = packValue(element, key, dataType); | ||||
|      | ||||
| 
 | ||||
|     /* Send to both devices */ | ||||
|     await sendReport(packetType.setValMsg, uintBuffer, true); | ||||
| 
 | ||||
|  | @ -1901,7 +1898,7 @@ async function saveHandler() { | |||
|       continue; | ||||
| 
 | ||||
|     if (origValue != getValue(element)) | ||||
|       await valueChangedHandler(element);     | ||||
|       await valueChangedHandler(element); | ||||
|   } | ||||
|   await sendReport(packetType.saveConfigMsg, [], true); | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -12,12 +12,12 @@ class FormField: | |||
|     elem: str | None = None | ||||
| 
 | ||||
| SHORTCUTS = { | ||||
|     0x73: "None",  | ||||
|     0x73: "None", | ||||
|     0x2A: "Backspace", | ||||
|     0x39: "Caps Lock",  | ||||
|     0x2B: "Tab",  | ||||
|     0x39: "Caps Lock", | ||||
|     0x2B: "Tab", | ||||
|     0x46: "Print Screen", | ||||
|     0x47: "Scroll Lock",     | ||||
|     0x47: "Scroll Lock", | ||||
|     0x53: "Num Lock", | ||||
|     } | ||||
| 
 | ||||
|  | @ -26,18 +26,18 @@ STATUS_ = [ | |||
|     FormField(79, "Running FW checksum", None, {}, "uint32", elem="hex_info"), | ||||
| ] | ||||
| 
 | ||||
| CONFIG_ = [    | ||||
| CONFIG_ = [ | ||||
|     FormField(1001, "Mouse", elem="label"), | ||||
|     FormField(71, "Force Mouse Boot Mode", None, {}, "uint8", "checkbox"), | ||||
|     FormField(75, "Enable Acceleration", None, {}, "uint8", "checkbox"), | ||||
|     FormField(77, "Jump Treshold", 0, {"min": 0, "max": 1024}, "uint16", "range"), | ||||
|      | ||||
| 
 | ||||
|     FormField(1002, "Keyboard", elem="label"), | ||||
|     FormField(72, "Force KBD Boot Protocol", None, {}, "uint8", "checkbox"), | ||||
|     FormField(73, "KBD LED as Indicator", None, {}, "uint8", "checkbox"), | ||||
| 
 | ||||
|     FormField(76, "Enforce Ports", None, {}, "uint8", "checkbox"), | ||||
| ]    | ||||
| ] | ||||
| 
 | ||||
| OUTPUT_ = [ | ||||
|     FormField(1, "Screen Count", 1, {1: "1", 2: "2", 3: "3"}, "uint32"), | ||||
|  | @ -48,7 +48,7 @@ OUTPUT_ = [ | |||
|     FormField(6, "Operating System", 1, {1: "Linux", 2: "MacOS", 3: "Windows", 4: "Android", 255: "Other"}, "uint8"), | ||||
|     FormField(7, "Screen Position", 1, {1: "Left", 2: "Right"}, "uint8"), | ||||
|     FormField(8, "Cursor Park Position", 0, {0: "Top", 1: "Bottom", 3: "Previous"}, "uint8"), | ||||
|     FormField(1003, "Screensaver", elem="label"),   | ||||
|     FormField(1003, "Screensaver", elem="label"), | ||||
|     FormField(9, "Mode", 0, {0: "Disabled", 1: "Pong", 2: "Jitter"}, "uint8"), | ||||
|     FormField(10, "Only If Inactive", None, {}, "uint8", "checkbox"), | ||||
|     FormField(11, "Idle Time (μs)", None, {}, "uint64"), | ||||
|  |  | |||
|  | @ -37,12 +37,12 @@ def encode_file(payload): | |||
|     return base64_compressed_data | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__":     | ||||
| if __name__ == "__main__": | ||||
|     # Read main template contents | ||||
|     webpage = render( | ||||
|         INPUT_FILENAME,  | ||||
|         screen_A=output_A(),  | ||||
|         screen_B=output_B(),  | ||||
|         INPUT_FILENAME, | ||||
|         screen_A=output_A(), | ||||
|         screen_B=output_B(), | ||||
|         status=output_status(), | ||||
|         config=output_config(), | ||||
|     ) | ||||
|  | @ -56,6 +56,6 @@ if __name__ == "__main__": | |||
| 
 | ||||
|     # Write data to output filename | ||||
|     write_file(self_extracting_webpage) | ||||
|      | ||||
| 
 | ||||
|     # Write unpacked webpage | ||||
|     write_file(webpage, OUTPUT_UNPACKED) | ||||
|  | @ -3,7 +3,7 @@ | |||
| {% set key = item.key %} | ||||
| 
 | ||||
| {% macro input(item, type='text', class='api', name='name') %} | ||||
| <input class="{{ class }}" type="{{ type }}" name="{{ name }}{{ key }}" data-type="{{ item.type }}" data-key="{{ key }}"  | ||||
| <input class="{{ class }}" type="{{ type }}" name="{{ name }}{{ key }}" data-type="{{ item.type }}" data-key="{{ key }}" | ||||
|   onchange="valueChangedHandler(this)" | ||||
| {% endmacro %} | ||||
| 
 | ||||
|  | @ -26,26 +26,26 @@ | |||
| 
 | ||||
|       {{ input(item, class='input-inline', type='number', name='aInput') }} | ||||
|         readonly oninput="this.form.aRange{{ key }}.value=this.value" /> | ||||
|        | ||||
| 
 | ||||
|       {{ input(item, class='range api', type='range', name='aRange') }} | ||||
|         min="{{ item.values.min }}" max="{{ item.values.max }}" oninput="this.form.aInput{{key}}.value=this.value" />                | ||||
|         min="{{ item.values.min }}" max="{{ item.values.max }}" oninput="this.form.aInput{{key}}.value=this.value" /> | ||||
|     </form> | ||||
|      | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|   {% elif item.get("elem") == "checkbox" %} | ||||
|   <div class="clearfix"> | ||||
|     {{ label(item) }}     | ||||
|     {{ label(item) }} | ||||
|     {{ input(item, type="checkbox") }}  /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
|   {% elif item["values"] %} | ||||
|     {{ label(item, class='') }} | ||||
|     <select class="api" data-type="{{ item.type }}" data-key="{{ key }}" required>                  | ||||
|     <select class="api" data-type="{{ item.type }}" data-key="{{ key }}" required> | ||||
|     <option disabled selected value></option> | ||||
| 
 | ||||
|     {% for k, v in item["values"].items() %}     | ||||
|     {% for k, v in item["values"].items() %} | ||||
|     <option value="{{ k }}">{{ v }}</option> | ||||
|     {% endfor %} | ||||
|     </select><br /> | ||||
|  |  | |||
|  | @ -17,14 +17,14 @@ | |||
| 
 | ||||
|   <main class="wrapper"> | ||||
| 
 | ||||
|     <section class="container">     | ||||
|     <section class="container"> | ||||
| 
 | ||||
|       <div class="row" id="warning" style="display: none;">  | ||||
|       <div class="row" id="warning" style="display: none;"> | ||||
|         <blockquote> | ||||
|           <h3> Oh, no! </h3> | ||||
|           Unfortunately, your browser does not support WebHID or the Permissions Policy is blocking its usage. Please try Chromium <br /> | ||||
|           (or Chrome if you have to). Apologies for the inconvenience, hopefully a cross-browser solution happens soon. | ||||
|         </blockquote>  | ||||
|         </blockquote> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="row"> | ||||
|  | @ -52,7 +52,7 @@ | |||
|               <path d="m 104.01516,99.341366 a 1.8639801,1.8639801 0 0 0 -1.86329,1.863284 1.8639801,1.8639801 0 0 0 1.86329,1.86328 h 27.4375 a 1.8639801,1.8639801 0 0 0 1.86718,-1.86328 1.8639801,1.8639801 0 0 0 -1.86718,-1.863284 z" /> | ||||
|             </g> | ||||
|           </svg> | ||||
|          | ||||
| 
 | ||||
|           <div id="menu-buttons"> | ||||
|             <button data-handler="connectHandler" class="button button-clear button-shifted">Connect</button><br /> | ||||
|             <button data-handler="readHandler" class="button button-clear button-shifted online">Read</button><br /> | ||||
|  | @ -63,8 +63,8 @@ | |||
|             <button data-handler="blinkHandler" class="button button-clear button-shifted online">Blink</button><br /> | ||||
|             <button data-handler="blinkBothHandler" class="button button-clear button-shifted online">Blink both</button><br /> | ||||
|             <button data-handler="enterBootloaderHandler" class="button button-clear button-shifted online">Bootloader</button><br /> | ||||
|             <button data-handler="wipeConfigHandler" class="button button-clear button-shifted online">Wipe Config</button><br />                        | ||||
|              | ||||
|             <button data-handler="wipeConfigHandler" class="button button-clear button-shifted online">Wipe Config</button><br /> | ||||
| 
 | ||||
|           </div> | ||||
| 
 | ||||
|         </div> | ||||
|  | @ -80,7 +80,7 @@ | |||
|             <h3>Output A</h3> | ||||
| 
 | ||||
|             {% for item in screen_A %} | ||||
|               {% include "form.html" with context %}              | ||||
|               {% include "form.html" with context %} | ||||
|             {% endfor %} | ||||
| 
 | ||||
|         </div> | ||||
|  | @ -91,13 +91,13 @@ | |||
|             <line x1="50" y1="90" x2="50" y2="75" stroke="black" stroke-width="2" /> | ||||
|             <rect x="30" y="90" width="40" height="3" stroke="black" stroke-width="2" fill="#d7e5f0" rx="5" ry="5" /> | ||||
|           </svg> | ||||
|            | ||||
| 
 | ||||
|             <h3>Output B</h3> | ||||
| 
 | ||||
|             {% for item in screen_B %} | ||||
|               {% include "form.html" with context %}                            | ||||
|               {% include "form.html" with context %} | ||||
|             {% endfor %} | ||||
|            | ||||
| 
 | ||||
|         </div> | ||||
| 
 | ||||
|       </div> | ||||
|  | @ -114,7 +114,7 @@ | |||
|           <h3>Common Config</h3> | ||||
| 
 | ||||
|             {% for item in config %} | ||||
|               {% include "form.html" with context %}              | ||||
|               {% include "form.html" with context %} | ||||
|             {% endfor %} | ||||
| 
 | ||||
|           <input type="submit" value="Save" id="submitButton"> | ||||
|  | @ -124,7 +124,7 @@ | |||
|           <h3>Device Status</h3> | ||||
| 
 | ||||
|           {% for item in status %} | ||||
|             {% include "form.html" with context %}              | ||||
|             {% include "form.html" with context %} | ||||
|           {% endfor %} | ||||
| 
 | ||||
|         </div> | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ var device; | |||
| const packetType = { | ||||
|   keyboardReportMsg: 1, mouseReportMsg: 2, outputSelectMsg: 3, firmwareUpgradeMsg: 4, switchLockMsg: 7, | ||||
|   syncBordersMsg: 8, flashLedMsg: 9, wipeConfigMsg: 10, readConfigMsg: 16, writeConfigMsg: 17, saveConfigMsg: 18, | ||||
|   rebootMsg: 19, getValMsg: 20, setValMsg: 21, proxyPacketMsg: 23 | ||||
|   rebootMsg: 19, getValMsg: 20, setValMsg: 21, getValAllMsg: 22, proxyPacketMsg: 23 | ||||
| }; | ||||
| 
 | ||||
| function calcChecksum(report) { | ||||
|  | @ -17,26 +17,26 @@ function calcChecksum(report) { | |||
| 
 | ||||
| async function sendReport(type, payload = [], sendBoth = false) { | ||||
|   if (!device || !device.opened) | ||||
|     return;   | ||||
|    | ||||
|     return; | ||||
| 
 | ||||
|   /* First send this one, if the first one gets e.g. rebooted */ | ||||
|   if (sendBoth) {       | ||||
|   if (sendBoth) { | ||||
|     var reportProxy = makeReport(type, payload, true); | ||||
|     await device.sendReport(mgmtReportId, reportProxy); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     var report = makeReport(type, payload, false); | ||||
|     await device.sendReport(mgmtReportId, report);    | ||||
|     await device.sendReport(mgmtReportId, report); | ||||
| } | ||||
| 
 | ||||
| function makeReport(type, payload, proxy=false) { | ||||
|   var dataOffset = proxy ? 4 : 3; | ||||
|   report = new Uint8Array([0xaa, 0x55, type, ...new Array(9).fill(0)]);    | ||||
|   report = new Uint8Array([0xaa, 0x55, type, ...new Array(9).fill(0)]); | ||||
| 
 | ||||
|   if (proxy)  | ||||
|     report = new Uint8Array([0xaa, 0x55, packetType.proxyPacketMsg, type, ...new Array(7).fill(0), type]);   | ||||
|        | ||||
|   if (payload) {     | ||||
|   if (proxy) | ||||
|     report = new Uint8Array([0xaa, 0x55, packetType.proxyPacketMsg, type, ...new Array(7).fill(0), type]); | ||||
| 
 | ||||
|   if (payload) { | ||||
|     report.set([...payload], dataOffset); | ||||
|     report[report.length - 1] = calcChecksum(report); | ||||
|   } | ||||
|  | @ -91,7 +91,8 @@ async function connectHandler() { | |||
|   }); | ||||
| 
 | ||||
|   device = devices[0]; | ||||
|   device.open().then(async () => {      | ||||
|   device.open().then(async () => { | ||||
|     device.addEventListener('inputreport', handleInputReport); | ||||
|     document.querySelectorAll('.online').forEach(element => { element.style.opacity = 1.0; }); | ||||
|     await readHandler(); | ||||
|   }); | ||||
|  | @ -123,8 +124,12 @@ function setValue(element, value) { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| function updateElement(element, event, dataType) { | ||||
|   var dataOffset = 3; | ||||
| function updateElement(key, event) { | ||||
|   var dataOffset = 4; | ||||
|   var element = document.querySelector(`[data-key="${key}"]`); | ||||
| 
 | ||||
|   if (!element) | ||||
|     return; | ||||
| 
 | ||||
|   const methods = { | ||||
|     "uint32": event.data.getUint32, | ||||
|  | @ -136,6 +141,8 @@ function updateElement(element, event, dataType) { | |||
|     "int8": event.data.getInt8 | ||||
|   }; | ||||
| 
 | ||||
|   dataType = element.getAttribute('data-type'); | ||||
| 
 | ||||
|   if (dataType in methods) { | ||||
|     var value = methods[dataType].call(event.data, dataOffset, true); | ||||
|     setValue(element, value); | ||||
|  | @ -149,24 +156,14 @@ async function readHandler() { | |||
|   if (!device || !device.opened) | ||||
|     await connectHandler(); | ||||
| 
 | ||||
|   const elements = document.querySelectorAll('.api'); | ||||
|   await sendReport(packetType.getValAllMsg); | ||||
| } | ||||
| 
 | ||||
|   for (const element of elements) { | ||||
|     var key = element.getAttribute('data-key'); | ||||
|     var dataType = element.getAttribute('data-type'); | ||||
| async function handleInputReport(event) { | ||||
|   var data = new Uint8Array(event.data.buffer); | ||||
|   var key = data[3]; | ||||
| 
 | ||||
|     await sendReport(packetType.getValMsg, [key]); | ||||
| 
 | ||||
|     let incomingReport = await new Promise((resolve, reject) => { | ||||
|       const handleInputReport = (event) => { | ||||
|         updateElement(element, event, dataType); | ||||
| 
 | ||||
|         device.removeEventListener('inputreport', handleInputReport); | ||||
|         resolve(); | ||||
|       } | ||||
|       device.addEventListener('inputreport', handleInputReport); | ||||
|     }); | ||||
|   } | ||||
|   updateElement(key, event); | ||||
| } | ||||
| 
 | ||||
| async function rebootHandler() { | ||||
|  | @ -186,7 +183,7 @@ async function valueChangedHandler(element) { | |||
| 
 | ||||
|   if (origValue != newValue) { | ||||
|     uintBuffer = packValue(element, key, dataType); | ||||
|      | ||||
| 
 | ||||
|     /* Send to both devices */ | ||||
|     await sendReport(packetType.setValMsg, uintBuffer, true); | ||||
| 
 | ||||
|  | @ -208,7 +205,7 @@ async function saveHandler() { | |||
|       continue; | ||||
| 
 | ||||
|     if (origValue != getValue(element)) | ||||
|       await valueChangedHandler(element);     | ||||
|       await valueChangedHandler(element); | ||||
|   } | ||||
|   await sendReport(packetType.saveConfigMsg, [], true); | ||||
| } | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| :root { | ||||
|   --highlight-color: #384955; | ||||
|   --font-color: #384955; | ||||
|   --highlight-color2: #5e9f41;   | ||||
|   --highlight-color2: #5e9f41; | ||||
| } | ||||
| 
 | ||||
| html { | ||||
|  | @ -483,7 +483,7 @@ input[type='number'].input-inline { | |||
| @media (min-width: 40rem) { | ||||
|   .row { | ||||
|     flex-direction: row; | ||||
|      | ||||
| 
 | ||||
|     width: calc(100% + 2.0rem); | ||||
|   } | ||||
|   .row .column { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue