diff --git a/src/defaults.c b/src/defaults.c index b0db359..54f25ff 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -15,7 +15,7 @@ const config_t default_config = { }, .screen_count = 1, .screen_index = 0, - }, + }, .output[OUTPUT_B] = { .number = OUTPUT_B, @@ -28,5 +28,18 @@ const config_t default_config = { .screen_count = 1, .screen_index = 0, }, - .screensaver_enabled = SCREENSAVER_ENABLED, -}; \ No newline at end of file + .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, + }, +}; diff --git a/src/handlers.c b/src/handlers.c index 9cd8ad4..95f9163 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -69,8 +69,8 @@ void wipe_config_hotkey_handler(device_t *state) { } void screensaver_hotkey_handler(device_t *state) { - state->config.screensaver_enabled ^= 1; - send_value(state->config.screensaver_enabled, SCREENSAVER_MSG); + state->config.screensaver[BOARD_ROLE].enabled ^= 1; + send_value(state->config.screensaver[BOARD_ROLE].enabled, SCREENSAVER_MSG); } /* When pressed, toggles the current mouse zoom mode state */ @@ -87,6 +87,7 @@ void mouse_zoom_hotkey_handler(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); 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 */ @@ -98,6 +99,7 @@ void handle_mouse_abs_uart_msg(uart_packet_t *packet, device_t *state) { state->mouse_y = mouse_report->y; state->last_activity[BOARD_ROLE] = time_us_64(); + state->screensaver_max_time_reached[BOARD_ROLE] = false; } /* Function handles request to switch output */ @@ -149,7 +151,7 @@ void handle_wipe_config_msg(uart_packet_t *packet, device_t *state) { } void handle_screensaver_msg(uart_packet_t *packet, device_t *state) { - state->config.screensaver_enabled = packet->data[0]; + state->config.screensaver[BOARD_ROLE].enabled = packet->data[0]; } /**==================================================== * diff --git a/src/keyboard.c b/src/keyboard.c index 1bf42a3..4a02531 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -162,6 +162,7 @@ void send_key(hid_keyboard_report_t *report, device_t *state) { if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { queue_kbd_report(report, state); state->last_activity[BOARD_ROLE] = time_us_64(); + state->screensaver_max_time_reached[BOARD_ROLE] = false; } else { send_packet((uint8_t *)report, KEYBOARD_REPORT_MSG, KBD_REPORT_LENGTH); } diff --git a/src/main.h b/src/main.h index 7bff444..5395639 100644 --- a/src/main.h +++ b/src/main.h @@ -146,7 +146,7 @@ typedef struct { /********* Configuration storage definitions **********/ -#define CURRENT_CONFIG_VERSION 2 +#define CURRENT_CONFIG_VERSION 3 typedef struct { int top; // When jumping from a smaller to a bigger screen, go to THIS top height @@ -164,13 +164,21 @@ typedef struct { border_size_t border; // Screen border size/offset to keep cursor at same height when switching } output_t; +/* Define screensaver parameters */ +typedef struct { + uint8_t enabled; + uint8_t only_if_inactive; + uint64_t idle_time_us; + uint64_t max_time_us; +} screensaver_t; + /* Data structure defining how configuration is stored */ typedef struct { uint32_t magic_header; uint32_t version; uint8_t force_mouse_boot_mode; output_t output[NUM_SCREENS]; - uint8_t screensaver_enabled; + screensaver_t screensaver[NUM_SCREENS]; // Keep checksum at the end of the struct uint32_t checksum; } config_t; @@ -214,6 +222,7 @@ typedef struct { 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 (-||-) + 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 uint64_t core1_last_loop_pass; // Timestamp of last core1 loop execution uint8_t active_output; // Currently selected output (0 = A, 1 = B) diff --git a/src/mouse.c b/src/mouse.c index 9438f13..c2f2f3e 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -53,6 +53,7 @@ void output_mouse_report(mouse_abs_report_t *report, device_t *state) { if (CURRENT_BOARD_IS_ACTIVE_OUTPUT) { queue_mouse_report(report, state); state->last_activity[BOARD_ROLE] = time_us_64(); + state->screensaver_max_time_reached[BOARD_ROLE] = false; } else { send_packet((uint8_t *)report, MOUSE_REPORT_MSG, MOUSE_REPORT_LENGTH); } diff --git a/src/user_config.h b/src/user_config.h index 36e3872..da424fe 100644 --- a/src/user_config.h +++ b/src/user_config.h @@ -63,17 +63,77 @@ /**================================================== * * ============== Screensaver Config ============== * - * ================================================== + * ================================================== * * - * Defines how long does an output need to be idle for screensaver to kick in. - * With this function, after being left idle for a certain amount of time (defined below), - * mouse cursor starts moving around like a bouncy-ball in pong. No clicking, of course. - * Move mouse on that active output to stop. + * While this feature is called 'screensaver', it's not actually a + * screensaver :) Really it's a way to ensure that some sort of mouse + * activity will be sent to one (or both) outputs when the user has + * not interacted with that output. This can be used to stop a + * screensaver or screenlock from activating on the attached computer, + * or to just watch the mouse pointer bouncing around! * - * SCREENSAVER_ENABLED: [0 or 1] 0 means screensaver is disabled, 1 means it is enabled. - * SCREENSAVER_TIME_SEC: time in seconds + * When the mode is active on an output, the pointer will jump around + * the screen like a bouncing-ball in a Pong game (however no click + * events will be generated, of course). * - * */ + * This mode is activated by 'idle time' on a per-output basis; if the + * mode is enabled for output B, and output B doesn't have any + * activity for (at least) the specified idle time, then the mode will + * be activated and will continue until the inactivity time reaches + * the maximum (if one has been specified). This allows you to stop a + * screensaver/screenlock from activating while you are still at your + * desk (but just interacting with the other computer attached to + * Deskhop), but let it activate if you leave your desk for an + * extended period of time. + * + * Additionally, this mode can be automatically disabled if the output + * is the currently-active output. + * + * If you only set the ENABLED options below, and leave the rest of + * the defaults in place, then the screensaver mode will activate + * after 4 minutes (240 seconds) of inactivity, will continue forever, + * but will only activate on an output that is not currently + * active. + * + **/ -#define SCREENSAVER_ENABLED 0 -#define SCREENSAVER_TIME_SEC 240 +/**================================================== * + * + * SCREENSAVER_{A|B}_ENABLED: [0 or 1] 0 means screensaver is + * disabled, 1 means it is enabled. + * + **/ + +#define SCREENSAVER_A_ENABLED 1 +#define SCREENSAVER_B_ENABLED 0 + +/**================================================== * + * + * SCREENSAVER_{A|B}_IDLE_TIME_SEC: Number of seconds that an output + * must be inactive before the screensaver mode will be activated. + * + **/ + +#define SCREENSAVER_A_IDLE_TIME_SEC 60 +#define SCREENSAVER_B_IDLE_TIME_SEC 240 + +/**================================================== * + * + * SCREENSAVER_{A|B}_MAX_TIME_SEC: Number of seconds that an output + * can be inactive before the screensaver mode will be deactivated. If + * zero, the screensaver will run indefinitely. + * + **/ + +#define SCREENSAVER_A_MAX_TIME_SEC 120 +#define SCREENSAVER_B_MAX_TIME_SEC 0 + +/**================================================== * + * + * SCREENSAVER_{A|B}_ONLY_IF_INACTIVE: [0 or 1] 1 means the + * screensaver will activate only if the output is inactive. + * + **/ + +#define SCREENSAVER_A_ONLY_IF_INACTIVE 1 +#define SCREENSAVER_B_ONLY_IF_INACTIVE 1 diff --git a/src/utils.c b/src/utils.c index 5d75766..52cccea 100644 --- a/src/utils.c +++ b/src/utils.c @@ -106,23 +106,54 @@ void save_config(device_t *state) { /* Have something fun and entertaining when idle */ void screensaver_task(device_t *state) { - const uint64_t idle_timeout_us = SCREENSAVER_TIME_SEC * 1000000; const int mouse_move_delay = 5000; static mouse_abs_report_t report = {.x = 0, .y = 0}; static int last_pointer_move = 0; + uint64_t current_time = time_us_64(); + static uint64_t last_activation_time = 0; + /* "Randomly" chosen initial values */ static int dx = 20; 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 (!state->config.screensaver_enabled) + if (!state->config.screensaver[BOARD_ROLE].enabled) { + last_activation_time = 0; + return; + } + + /* If we're not the selected output and that is required, nothing to do here. */ + if (state->config.screensaver[BOARD_ROLE].only_if_inactive && + CURRENT_BOARD_IS_ACTIVE_OUTPUT) { + last_activation_time = 0; return; + } /* We are enabled, but idle time still too small to activate. */ - if (time_us_64() - state->last_activity[BOARD_ROLE] < idle_timeout_us) + 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. */ if ((time_us_32()) - last_pointer_move < mouse_move_delay)