Screensaver features:

* Per-output enablement
* Maximum time
* Enable only when output is inactive

Closes #66.
This commit is contained in:
Kevin P. Fleming 2024-03-18 15:06:19 -04:00
parent 5d6fcc4975
commit 68a4ca6be2
7 changed files with 138 additions and 21 deletions

View File

@ -28,5 +28,18 @@ const config_t default_config = {
.screen_count = 1,
.screen_index = 0,
},
.screensaver_enabled = SCREENSAVER_ENABLED,
.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

@ -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];
}
/**==================================================== *

View File

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

View File

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

View File

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

View File

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

View File

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