From f07008468fb8617e7ffaf8b89c964eee669292d8 Mon Sep 17 00:00:00 2001 From: Luke Nuttall Date: Fri, 24 May 2024 01:08:37 +0930 Subject: [PATCH] Add always_switch optional config for Acer monitors that don't correctly report they are on the usb-c input --- LICENSE | 1 + README.md | 7 +++++- src/configuration.rs | 51 ++++++++++++++++++++++++++++++++++++++++++ src/display_control.rs | 11 ++++++--- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index e5f9c68..835146e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2020 Haim Gelfenbeyn +Copyright (c) 2024 Luke Nuttall Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 001ab1e..5b477c8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ Configuration file settings: ```ini usb_device = "1050:0407" on_usb_connect = "Hdmi1" - on_usb_disconnect = "Hdmi2" + on_usb_disconnect = "Hdmi2" + always_switch = true ``` `usb_device` is which USB device to watch (vendor id / device id in hex), and `on_usb_connect` is which monitor input @@ -42,6 +43,10 @@ The optional `on_usb_disconnect` settings allows to switch in the other directio Note that the preferred way is to have this app installed on both computers. Switching "away" is problematic: if the other computer has put the monitors to sleep, they will switch immediately back to the original input. +The optional `always_switch` setting can be used to attempt to switch even if the monitor reports that it currently +on the input we want to switch to. This is useful for Acer monitors like the XR343CRK, which pretend they are still +connected to `DisplayPort1` or `0xF`, when you switch to USB-C using `DisplayPort2` or `0x10`. + ### Different inputs on different monitors `display-switch` supports per-monitor configuration: add one or more monitor-specific configuration sections to set monitor-specific inputs. For example: diff --git a/src/configuration.rs b/src/configuration.rs index 68447f4..195d344 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,5 +1,6 @@ // // Copyright © 2020 Haim Gelfenbeyn +// Copyright © 2020 Luke Nuttall // This code is licensed under MIT license (see LICENSE.txt for details) // @@ -35,6 +36,9 @@ struct PerMonitorConfiguration { pub struct Configuration { #[serde(deserialize_with = "Configuration::deserialize_usb_device")] pub usb_device: String, + #[serde(default)] + #[serde(deserialize_with = "Configuration::deserialize_always_switch")] + pub always_switch: bool, #[serde(flatten)] pub default_input_sources: InputSources, monitor1: Option, @@ -108,6 +112,14 @@ impl Configuration { Ok(s.to_lowercase()) } + fn deserialize_always_switch<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let b: bool = Deserialize::deserialize(deserializer)?; + Ok(b) + } + pub fn config_file_name() -> Result { let config_dir = if cfg!(target_os = "macos") { dirs::preference_dir().ok_or_else(|| anyhow!("Config directory not found"))? @@ -198,6 +210,45 @@ mod tests { assert_eq!(config.usb_device, "dead:beef") } + #[test] + fn test_always_switch_is_true_deserialization() { + let config = load_test_config( + r#" + usb_device = "dead:BEEF" + always_switch = true + on_usb_connect = "DisplayPort2" + "#, + ) + .unwrap(); + assert_eq!(config.always_switch, true) + } + + #[test] + fn test_always_switch_is_false_deserialization() { + let config = load_test_config( + r#" + usb_device = "dead:BEEF" + always_switch = false + on_usb_connect = "DisplayPort2" + "#, + ) + .unwrap(); + assert_eq!(config.always_switch, false) + } + + #[test] + fn test_always_switch_defaults_to_false_deserialization() { + let config = load_test_config( + r#" + usb_device = "dead:BEEF" + on_usb_connect = "DisplayPort2" + "#, + ) + .unwrap(); + assert_eq!(config.always_switch, false) + } + + #[test] fn test_symbolic_input_deserialization() { let config = load_test_config( diff --git a/src/display_control.rs b/src/display_control.rs index e24d9fa..997b07a 100644 --- a/src/display_control.rs +++ b/src/display_control.rs @@ -1,5 +1,6 @@ // // Copyright © 2020 Haim Gelfenbeyn +// Copyright © 2024 Luke Nuttall // This code is licensed under MIT license (see LICENSE.txt for details) // use crate::configuration::{Configuration, SwitchDirection}; @@ -47,12 +48,16 @@ fn are_display_names_unique(displays: &[Display]) -> bool { displays.iter().all(|display| hash.insert(display_name(display, None))) } -fn try_switch_display(handle: &mut Handle, display_name: &str, input: InputSource) { +fn try_switch_display(handle: &mut Handle, display_name: &str, input: InputSource, always_switch: bool) { match handle.get_vcp_feature(INPUT_SELECT) { Ok(raw_source) => { if raw_source.value() & 0xff == input.value() { info!("Display {} is already set to {}", display_name, input); - return; + if always_switch { + info!("Let's try anyway!"); + } else { + return; + } } } Err(err) => { @@ -121,7 +126,7 @@ pub fn switch(config: &Configuration, switch_direction: SwitchDirection) { let input_sources = config.configuration_for_monitor(&display_name); debug!("Input sources found for display {}: {:?}", display_name, input_sources); if let Some(input) = input_sources.source(switch_direction) { - try_switch_display(&mut display.handle, &display_name, input); + try_switch_display(&mut display.handle, &display_name, input, config.always_switch); } else { info!( "Display {} is not configured to switch on USB {}",