Skip to content

Commit

Permalink
Add UI for Monitor Overrides (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob-pro authored Jan 14, 2024
1 parent 565b104 commit 06d57b9
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 135 deletions.
9 changes: 1 addition & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "solar-screen-brightness"
version = "2.0.1"
version = "2.1.0"
authors = ["Jacob Halsey <[email protected]>"]
edition = "2021"
build = "build.rs"
Expand Down Expand Up @@ -32,7 +32,6 @@ human-repr = "1.1.0"
image = "0.24.7"
itertools = "0.11.0"
log = "0.4.14"
maplit = "1.0.2"
png = "0.17.10"
pollster = "0.3.0"
serde = { version = "1.0.110", features = ["derive"] }
Expand Down
Binary file modified screenshots/brightness.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/location.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified screenshots/status.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 60 additions & 38 deletions src/apply.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::calculator::calculate_brightness;
use crate::config::{BrightnessValues, Location, MonitorOverride};
use crate::config::{BrightnessValues, Location, MonitorOverride, MonitorProperty};
use brightness::blocking::{Brightness, BrightnessDevice};
use maplit::btreemap;
use itertools::Itertools;
use serde::Serialize;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
use sunrise_sunset_calculator::SunriseSunsetParameters;
use wildmatch::WildMatch;
Expand Down Expand Up @@ -34,12 +34,22 @@ impl From<sunrise_sunset_calculator::SunriseSunsetResult> for SunriseSunsetResul

#[derive(Debug, Serialize)]
pub struct MonitorResult {
pub device_name: String,
pub properties: BTreeMap<&'static str, String>,
pub properties: MonitorProperties,
pub brightness: Option<BrightnessDetails>,
pub error: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct MonitorProperties {
pub device_name: String,
#[cfg(windows)]
pub device_description: String,
#[cfg(windows)]
pub device_key: String,
#[cfg(windows)]
pub device_path: String,
}

#[derive(Debug, Serialize)]
pub struct BrightnessDetails {
pub expiry_time: Option<i64>,
Expand All @@ -50,27 +60,28 @@ pub struct BrightnessDetails {

pub struct MonitorOverrideCompiled {
pub pattern: WildMatch,
pub key: String,
pub key: MonitorProperty,
pub brightness: Option<BrightnessValues>,
}

impl From<&MonitorOverride> for MonitorOverrideCompiled {
fn from(value: &MonitorOverride) -> Self {
Self {
pattern: WildMatch::new(&value.pattern),
key: value.key.clone(),
brightness: value.brightness.clone(),
key: value.key,
brightness: value.brightness,
}
}
}

/// Find the first override that matches this monitor's properties
fn match_monitor<'o>(
overrides: &'o [MonitorOverrideCompiled],
monitor: &BTreeMap<&'static str, String>,
monitor: &MonitorProperties,
) -> Option<&'o MonitorOverrideCompiled> {
let map = monitor.to_map();
for o in overrides {
if let Some(value) = monitor.get(o.key.as_str()) {
if let Some(value) = map.get(&o.key) {
if o.pattern.matches(value) {
return Some(o);
}
Expand Down Expand Up @@ -108,14 +119,13 @@ pub fn apply_brightness(
let monitor_results = monitors
.into_iter()
.map(|m| {
let device_name = m.device_name().unwrap_or_default();
let properties = get_properties(&m).unwrap_or_default();
let properties = MonitorProperties::from_device(&m);
let monitor_values = match match_monitor(&overrides, &properties) {
None => Some(BrightnessValues {
brightness_day,
brightness_night,
}),
Some(o) => o.brightness.clone(),
Some(o) => o.brightness,
};

if let Some(BrightnessValues {
Expand All @@ -132,25 +142,28 @@ pub fn apply_brightness(
);
log::debug!(
"Computed brightness for '{}' = {:?} (day={}) (night={})",
device_name,
properties.device_name,
brightness,
brightness_day,
brightness_night
);

let error = m.set(brightness.brightness).err();
if let Some(err) = error.as_ref() {
log::error!("Failed to set brightness for '{}': {:?}", device_name, err);
log::error!(
"Failed to set brightness for '{}': {:?}",
properties.device_name,
err
);
} else {
log::info!(
"Successfully set brightness for '{}' to {}%",
device_name,
properties.device_name,
brightness.brightness
);
}

MonitorResult {
device_name,
properties,
brightness: Some(BrightnessDetails {
expiry_time: brightness.expiry_time,
Expand All @@ -161,15 +174,18 @@ pub fn apply_brightness(
error: error.map(|e| e.to_string()),
}
} else {
log::info!("Skipping '{}' due to monitor override", device_name,);
log::info!(
"Skipping '{}' due to monitor override",
properties.device_name
);
MonitorResult {
device_name,
properties,
brightness: None,
error: None,
}
}
})
.sorted_by_key(|m| m.properties.device_name.clone())
.collect::<Vec<_>>();

ApplyResults {
Expand All @@ -179,24 +195,30 @@ pub fn apply_brightness(
}
}

#[cfg(windows)]
pub fn get_properties(
device: &BrightnessDevice,
) -> Result<BTreeMap<&'static str, String>, brightness::Error> {
use brightness::blocking::windows::BrightnessExt;
Ok(btreemap! {
"device_name" => device.device_name()?,
"device_description" => device.device_description()?,
"device_key" => device.device_registry_key()?,
"device_path" => device.device_path()?,
})
}
impl MonitorProperties {
fn from_device(device: &BrightnessDevice) -> Self {
#[cfg(windows)]
use brightness::blocking::windows::BrightnessExt;
Self {
device_name: device.device_name().unwrap(),
#[cfg(windows)]
device_description: device.device_description().unwrap(),
#[cfg(windows)]
device_key: device.device_registry_key().unwrap(),
#[cfg(windows)]
device_path: device.device_path().unwrap(),
}
}

#[cfg(target_os = "linux")]
pub fn get_properties(
device: &BrightnessDevice,
) -> Result<BTreeMap<&'static str, String>, brightness::Error> {
Ok(btreemap! {
"device_name" => device.device_name()?,
})
pub fn to_map(&self) -> HashMap<MonitorProperty, &str> {
let mut map = HashMap::<_, &str>::new();
map.insert(MonitorProperty::DeviceName, &self.device_name);
#[cfg(windows)]
{
map.insert(MonitorProperty::DeviceDescription, &self.device_description);
map.insert(MonitorProperty::DeviceKey, &self.device_key);
map.insert(MonitorProperty::DevicePath, &self.device_path);
}
map
}
}
32 changes: 29 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! SSB Config file definition

use crate::common::config_directory;
use anyhow::Context;
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
Expand Down Expand Up @@ -34,15 +34,41 @@ pub struct SsbConfig {
pub overrides: Vec<MonitorOverride>,
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone, Eq, Hash, PartialEq, Sequence)]
#[serde(rename_all = "snake_case")]
pub enum MonitorProperty {
DeviceName,
#[cfg(windows)]
DeviceDescription,
#[cfg(windows)]
DeviceKey,
#[cfg(windows)]
DevicePath,
}

impl MonitorProperty {
pub fn as_str(&self) -> &'static str {
match self {
MonitorProperty::DeviceName => "Name",
#[cfg(windows)]
MonitorProperty::DeviceDescription => "Description",
#[cfg(windows)]
MonitorProperty::DeviceKey => "Key",
#[cfg(windows)]
MonitorProperty::DevicePath => "Path",
}
}
}

#[derive(Debug, Deserialize, Serialize, Validate, Clone)]
pub struct MonitorOverride {
pub pattern: String,
pub key: String,
pub key: MonitorProperty,
#[validate]
pub brightness: Option<BrightnessValues>,
}

#[derive(Debug, Serialize, Deserialize, Validate, Clone)]
#[derive(Debug, Serialize, Deserialize, Validate, Copy, Clone)]
pub struct BrightnessValues {
#[validate(range(max = 100))]
pub brightness_day: u32,
Expand Down
29 changes: 28 additions & 1 deletion src/gui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::controller::Message;
use crate::gui::brightness_settings::BrightnessSettingsPage;
use crate::gui::help::HelpPage;
use crate::gui::location_settings::LocationSettingsPage;
use crate::gui::monitor_overrides::MonitorOverridePage;
use crate::gui::status::StatusPage;
use crate::gui::UserEvent;
use egui::{Align, Color32, Layout, ScrollArea};
Expand All @@ -16,14 +17,15 @@ use std::sync::{Arc, Mutex, RwLock};
pub const SPACING: f32 = 10.0;

pub trait Page {
fn render(&mut self, ui: &mut egui::Ui, context: &mut AppState);
fn render(&mut self, ui: &mut egui::Ui, app_state: &mut AppState);
}

pub struct SsbEguiApp {
selected_page: PageId,
brightness_settings_page: BrightnessSettingsPage,
pub location_settings_page: LocationSettingsPage,
help_page: HelpPage,
monitor_override_page: MonitorOverridePage,
context: AppState,
pub modal: Option<Box<dyn Modal>>,
}
Expand Down Expand Up @@ -55,6 +57,7 @@ enum PageId {
Status,
BrightnessSettings,
LocationSettings,
MonitorOverrides,
Help,
}

Expand All @@ -65,6 +68,7 @@ impl PageId {
PageId::BrightnessSettings => "Brightness Settings",
PageId::LocationSettings => "Location Settings",
PageId::Help => "Help",
PageId::MonitorOverrides => "Monitor Overrides",
}
}

Expand All @@ -74,6 +78,7 @@ impl PageId {
PageId::BrightnessSettings => "🔅",
PageId::LocationSettings => "🌐",
PageId::Help => "❔",
PageId::MonitorOverrides => "💻",
}
}
}
Expand Down Expand Up @@ -143,6 +148,7 @@ impl SsbEguiApp {
selected_page: PageId::Status,
brightness_settings_page: BrightnessSettingsPage::from_config(&config_read),
location_settings_page: LocationSettingsPage::from_config(&config_read),
monitor_override_page: MonitorOverridePage::from_config(&config_read),
modal: None,
context: AppState {
main_loop,
Expand Down Expand Up @@ -221,7 +227,28 @@ impl SsbEguiApp {
self.location_settings_page.render(ui, &mut self.context)
}
PageId::Help => self.help_page.render(ui, &mut self.context),
PageId::MonitorOverrides => {
self.monitor_override_page.render(ui, &mut self.context)
}
}
});
}
}

pub fn set_red_widget_border(ui: &mut egui::Ui) {
ui.style_mut().visuals.widgets.inactive.bg_stroke.color = egui::Color32::RED;
ui.style_mut().visuals.widgets.inactive.bg_stroke.width = 1.0;
ui.style_mut().visuals.widgets.hovered.bg_stroke.color = egui::Color32::RED;
}

pub fn save_config(config: &mut SsbConfig, transitions: &Transitions) {
if let Err(e) = config.save() {
log::error!("Unable to save config: {:#}", e);
transitions.queue_state_transition(move |app| {
app.modal = Some(Box::new(MessageModal {
title: "Error".to_string(),
message: format!("Unable to save config: {}", e),
}));
});
}
}
Loading

0 comments on commit 06d57b9

Please sign in to comment.