Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
dab90e081e
|
|||
| 07d1921bd3 | |||
| f8c4381a11 | |||
| 9171f0771a |
+71
-98
@@ -22,48 +22,97 @@ pub struct Config {
|
|||||||
pub indicator_thickness: u32,
|
pub indicator_thickness: u32,
|
||||||
|
|
||||||
#[arg(long, value_parser = util::parse_blur_effect)]
|
#[arg(long, value_parser = util::parse_blur_effect)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_blur_effect",
|
||||||
|
serialize_with = "util::serialize_blur_effect",
|
||||||
|
default
|
||||||
|
)]
|
||||||
pub effect_blur: Option<(u32, u32)>,
|
pub effect_blur: Option<(u32, u32)>,
|
||||||
|
|
||||||
#[arg(long, value_parser = util::parse_vignette_effect)]
|
#[arg(long, value_parser = util::parse_vignette_effect)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_vignette_effect",
|
||||||
|
serialize_with = "util::serialize_vignette_effect",
|
||||||
|
default
|
||||||
|
)]
|
||||||
pub effect_vignette: Option<(f32, f32)>,
|
pub effect_vignette: Option<(f32, f32)>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub effect_pixelate: Option<u32>,
|
pub effect_pixelate: Option<u32>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub effect_swirl: Option<f32>,
|
pub effect_swirl: Option<f32>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub effect_melting: Option<f32>,
|
pub effect_melting: Option<f32>,
|
||||||
|
|
||||||
#[arg(long, default_value = "785412", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "785412", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub ring_color: (f64, f64, f64, f64),
|
pub ring_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "4EAC41", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "4EAC41", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub key_hl_color: (f64, f64, f64, f64),
|
pub key_hl_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "4EAC41", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "4EAC41", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub caps_lock_key_hl_color: (f64, f64, f64, f64),
|
pub caps_lock_key_hl_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "DB3300", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "DB3300", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub caps_lock_bs_hl_color: (f64, f64, f64, f64),
|
pub caps_lock_bs_hl_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub caps_lock_color: (f64, f64, f64, f64),
|
pub caps_lock_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub caps_lock_text_color: (f64, f64, f64, f64),
|
pub caps_lock_text_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
|
#[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
|
||||||
pub show_caps_lock_text: bool,
|
pub show_caps_lock_text: bool,
|
||||||
|
|
||||||
#[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub line_color: (f64, f64, f64, f64),
|
pub line_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "00000088", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "00000088", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub inside_color: (f64, f64, f64, f64),
|
pub inside_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)]
|
#[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)]
|
||||||
|
#[serde(
|
||||||
|
deserialize_with = "util::deserialize_hex_color",
|
||||||
|
serialize_with = "util::serialize_hex_color"
|
||||||
|
)]
|
||||||
pub separator_color: (f64, f64, f64, f64),
|
pub separator_color: (f64, f64, f64, f64),
|
||||||
|
|
||||||
#[arg(long, default_value = "2")]
|
#[arg(long, default_value = "2")]
|
||||||
@@ -107,34 +156,44 @@ pub struct Config {
|
|||||||
pub show_keyboard_layout: bool,
|
pub show_keyboard_layout: bool,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub image: Option<PathBuf>,
|
pub image: Option<PathBuf>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub wifi_icon: Option<String>,
|
pub wifi_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub bluetooth_icon: Option<String>,
|
pub bluetooth_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub battery_icon: Option<String>,
|
pub battery_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub media_prev_icon: Option<String>,
|
pub media_prev_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub media_stop_icon: Option<String>,
|
pub media_stop_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub media_play_icon: Option<String>,
|
pub media_play_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub media_pause_icon: Option<String>,
|
pub media_pause_icon: Option<String>,
|
||||||
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub media_next_icon: Option<String>,
|
pub media_next_icon: Option<String>,
|
||||||
|
|
||||||
/// Apply a pre-defined theme preset
|
/// Apply a pre-defined theme preset
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
|
#[serde(default)]
|
||||||
pub theme: Option<String>,
|
pub theme: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,111 +218,25 @@ impl Config {
|
|||||||
|
|
||||||
if config_path.exists() {
|
if config_path.exists() {
|
||||||
if let Ok(file_content) = std::fs::read_to_string(&config_path) {
|
if let Ok(file_content) = std::fs::read_to_string(&config_path) {
|
||||||
if let Ok(table) = toml::from_str::<toml::Table>(&file_content) {
|
if let Ok(file_table) = toml::from_str::<toml::Table>(&file_content) {
|
||||||
log::debug!("Loaded configuration from {:?}", config_path);
|
log::debug!("Loaded configuration from {:?}", config_path);
|
||||||
|
|
||||||
let merge_bool = |val: &mut bool, key: &str| {
|
// Convert current config to a TOML table to facilitate merging
|
||||||
if !is_cli(key) {
|
if let Ok(mut config_table) = toml::Value::try_from(config.clone()) {
|
||||||
if let Some(toml::Value::Boolean(b)) = table.get(key) {
|
if let Some(config_table) = config_table.as_table_mut() {
|
||||||
*val = *b;
|
for (key, value) in file_table {
|
||||||
}
|
if !is_cli(&key) {
|
||||||
}
|
config_table.insert(key, value);
|
||||||
};
|
|
||||||
|
|
||||||
let merge_u32 = |val: &mut u32, key: &str| {
|
|
||||||
if !is_cli(key) {
|
|
||||||
if let Some(toml::Value::Integer(i)) = table.get(key) {
|
|
||||||
*val = *i as u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let merge_f32 = |val: &mut f32, key: &str| {
|
|
||||||
if !is_cli(key) {
|
|
||||||
if let Some(toml::Value::Float(f)) = table.get(key) {
|
|
||||||
*val = *f as f32;
|
|
||||||
} else if let Some(toml::Value::Integer(i)) = table.get(key) {
|
|
||||||
*val = *i as f32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let merge_string = |val: &mut String, key: &str| {
|
|
||||||
if !is_cli(key) {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get(key) {
|
|
||||||
*val = s.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
merge_bool(&mut config.screenshots, "screenshots");
|
|
||||||
merge_bool(&mut config.clock, "clock");
|
|
||||||
merge_bool(&mut config.indicator, "indicator");
|
|
||||||
merge_u32(&mut config.indicator_radius, "indicator_radius");
|
|
||||||
merge_u32(&mut config.indicator_thickness, "indicator_thickness");
|
|
||||||
merge_f32(&mut config.grace, "grace");
|
|
||||||
merge_f32(&mut config.fade_in, "fade_in");
|
|
||||||
merge_string(&mut config.pam_service, "pam_service");
|
|
||||||
merge_bool(&mut config.show_media, "show_media");
|
|
||||||
merge_bool(&mut config.show_battery, "show_battery");
|
|
||||||
merge_bool(&mut config.show_network, "show_network");
|
|
||||||
merge_bool(&mut config.show_bluetooth, "show_bluetooth");
|
|
||||||
merge_bool(&mut config.show_album_art, "show_album_art");
|
|
||||||
merge_bool(&mut config.hide_password, "hide_password");
|
|
||||||
merge_bool(&mut config.show_keyboard_layout, "show_keyboard_layout");
|
|
||||||
|
|
||||||
if !is_cli("image") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("image") {
|
|
||||||
config.image = Some(std::path::PathBuf::from(s));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("wifi_icon") {
|
// Convert back to Config struct
|
||||||
if let Some(toml::Value::String(s)) = table.get("wifi_icon") {
|
if let Ok(new_config) =
|
||||||
config.wifi_icon = Some(s.clone());
|
toml::Value::Table(config_table.clone()).try_into::<Config>()
|
||||||
|
{
|
||||||
|
config = new_config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("bluetooth_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("bluetooth_icon") {
|
|
||||||
config.bluetooth_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("battery_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("battery_icon") {
|
|
||||||
config.battery_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("media_prev_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_prev_icon") {
|
|
||||||
config.media_prev_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("media_stop_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_stop_icon") {
|
|
||||||
config.media_stop_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("media_play_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_play_icon") {
|
|
||||||
config.media_play_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("media_pause_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_pause_icon") {
|
|
||||||
config.media_pause_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_cli("media_next_icon") {
|
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_next_icon") {
|
|
||||||
config.media_next_icon = Some(s.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -265,6 +265,17 @@ impl LockManager {
|
|||||||
action
|
action
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_surface_by_output(&mut self, output: &wl_output::WlOutput) -> Option<usize> {
|
||||||
|
use wayland_client::Proxy;
|
||||||
|
let output_id = Proxy::id(output);
|
||||||
|
let idx = self
|
||||||
|
.surfaces
|
||||||
|
.iter()
|
||||||
|
.position(|s| Proxy::id(s.output()) == output_id)?;
|
||||||
|
self.surfaces.remove(idx);
|
||||||
|
Some(idx)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_system_status(&mut self, status: SystemStatus) {
|
pub fn set_system_status(&mut self, status: SystemStatus) {
|
||||||
for surface in &mut self.surfaces {
|
for surface in &mut self.surfaces {
|
||||||
surface.set_system_status(status.clone());
|
surface.set_system_status(status.clone());
|
||||||
|
|||||||
+60
-10
@@ -105,6 +105,7 @@ impl log::Log for DualLogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct WaylandLock {
|
struct WaylandLock {
|
||||||
|
conn: Connection,
|
||||||
loop_handle: LoopHandle<'static, Self>,
|
loop_handle: LoopHandle<'static, Self>,
|
||||||
lock_manager: Arc<Mutex<LockManager>>,
|
lock_manager: Arc<Mutex<LockManager>>,
|
||||||
config: Config,
|
config: Config,
|
||||||
@@ -124,7 +125,6 @@ struct WaylandLock {
|
|||||||
captured_backgrounds: Vec<Option<cairo::ImageSurface>>,
|
captured_backgrounds: Vec<Option<cairo::ImageSurface>>,
|
||||||
pending_screenshots: usize,
|
pending_screenshots: usize,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
unlocking: bool,
|
|
||||||
screenshot_manager: Option<ScreenshotManager>,
|
screenshot_manager: Option<ScreenshotManager>,
|
||||||
grace_until: Option<Instant>,
|
grace_until: Option<Instant>,
|
||||||
system_manager: Arc<SystemManager>,
|
system_manager: Arc<SystemManager>,
|
||||||
@@ -141,8 +141,9 @@ impl WaylandLock {
|
|||||||
log::info!("✅ Authentication successful - unlocking session");
|
log::info!("✅ Authentication successful - unlocking session");
|
||||||
if let Some(session_lock) = &self.session_lock {
|
if let Some(session_lock) = &self.session_lock {
|
||||||
session_lock.unlock();
|
session_lock.unlock();
|
||||||
self.unlocking = true;
|
let _ = self.conn.flush();
|
||||||
log::debug!("Unlock requested - waiting for compositor finished event");
|
self.exit = true;
|
||||||
|
log::debug!("Unlock requested - exiting");
|
||||||
} else {
|
} else {
|
||||||
log::error!("No session_lock available to unlock!");
|
log::error!("No session_lock available to unlock!");
|
||||||
self.exit = true;
|
self.exit = true;
|
||||||
@@ -298,9 +299,61 @@ impl OutputHandler for WaylandLock {
|
|||||||
fn output_state(&mut self) -> &mut OutputState {
|
fn output_state(&mut self) -> &mut OutputState {
|
||||||
&mut self.output_state
|
&mut self.output_state
|
||||||
}
|
}
|
||||||
fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: WlOutput) {}
|
fn new_output(&mut self, _conn: &Connection, qh: &QueueHandle<Self>, output: WlOutput) {
|
||||||
fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: WlOutput) {}
|
// If we are already locked, we must create a lock surface for this newly
|
||||||
fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: WlOutput) {
|
// available output. This happens e.g. when a monitor powers back on after
|
||||||
|
// "niri msg action power-off-monitors" — niri re-advertises the output and
|
||||||
|
// the compositor requires a lock surface on every output or it shows a
|
||||||
|
// compositor-defined fallback (typically a solid red/black screen).
|
||||||
|
if let Some(session_lock) = &self.session_lock {
|
||||||
|
let surface = self.compositor_state.create_surface(qh);
|
||||||
|
let (width, height) = self.get_output_dimensions(&output);
|
||||||
|
let lock_surface = session_lock.create_lock_surface(surface.clone(), &output, qh);
|
||||||
|
self.lock_surfaces.push(lock_surface);
|
||||||
|
if !self.outputs.contains(&output) {
|
||||||
|
self.outputs.push(output.clone());
|
||||||
|
}
|
||||||
|
if let Ok(mut lm) = self.lock_manager.lock() {
|
||||||
|
lm.add_surface(width, height, output);
|
||||||
|
let count = lm.surface_count();
|
||||||
|
if let Some(ls) = lm.get_surface_mut(count - 1) {
|
||||||
|
ls.set_wayland_surface(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("Created lock surface for newly available output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _output: WlOutput) {
|
||||||
|
// Dimension changes while locked are handled by the compositor sending a configure
|
||||||
|
// event on the lock surface, which the SessionLockHandler::configure callback
|
||||||
|
// already processes via locked_surface.resize().
|
||||||
|
}
|
||||||
|
fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, output: WlOutput) {
|
||||||
|
// Clean up the lock surface and associated state for this output.
|
||||||
|
// This happens e.g. when a monitor is powered off with its physical power switch.
|
||||||
|
// When the monitor comes back on, new_output() will fire and recreate everything.
|
||||||
|
//
|
||||||
|
// outputs and captured_backgrounds are kept at the same indices, so we remove
|
||||||
|
// from both using the same position.
|
||||||
|
let output_id = Proxy::id(&output);
|
||||||
|
if let Some(idx) = self.outputs.iter().position(|o| Proxy::id(o) == output_id) {
|
||||||
|
self.outputs.remove(idx);
|
||||||
|
if idx < self.captured_backgrounds.len() {
|
||||||
|
self.captured_backgrounds.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock_manager.surfaces and lock_surfaces are built in tandem and share indices,
|
||||||
|
// so the index returned from the lock_manager removal applies to lock_surfaces too.
|
||||||
|
if let Ok(mut lm) = self.lock_manager.lock() {
|
||||||
|
if let Some(idx) = lm.remove_surface_by_output(&output) {
|
||||||
|
if idx < self.lock_surfaces.len() {
|
||||||
|
drop(self.lock_surfaces.remove(idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("Removed lock surface for destroyed output");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,6 +713,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
let pool = SlotPool::new(1, &shm_state)?;
|
let pool = SlotPool::new(1, &shm_state)?;
|
||||||
|
|
||||||
let mut state = WaylandLock {
|
let mut state = WaylandLock {
|
||||||
|
conn: conn.clone(),
|
||||||
loop_handle: event_loop.handle(),
|
loop_handle: event_loop.handle(),
|
||||||
lock_manager: lock_manager.clone(),
|
lock_manager: lock_manager.clone(),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
@@ -679,7 +733,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
captured_backgrounds: Vec::new(),
|
captured_backgrounds: Vec::new(),
|
||||||
pending_screenshots: 0,
|
pending_screenshots: 0,
|
||||||
exit: false,
|
exit: false,
|
||||||
unlocking: false,
|
|
||||||
screenshot_manager: ScreenshotManager::new(&globals, &qh).ok(),
|
screenshot_manager: ScreenshotManager::new(&globals, &qh).ok(),
|
||||||
grace_until: None,
|
grace_until: None,
|
||||||
system_manager: system_manager.clone(),
|
system_manager: system_manager.clone(),
|
||||||
@@ -770,9 +823,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.unlocking {
|
|
||||||
return calloop::timer::TimeoutAction::ToDuration(Duration::from_millis(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut status = state.system_manager.get_status();
|
let mut status = state.system_manager.get_status();
|
||||||
status.keyboard_layout = Some(state.current_layout.to_string());
|
status.keyboard_layout = Some(state.current_layout.to_string());
|
||||||
|
|||||||
+82
@@ -1,3 +1,5 @@
|
|||||||
|
use serde::{Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
pub fn parse_hex_color(s: &str) -> Result<(f64, f64, f64, f64), String> {
|
pub fn parse_hex_color(s: &str) -> Result<(f64, f64, f64, f64), String> {
|
||||||
let s = s.trim_start_matches('#');
|
let s = s.trim_start_matches('#');
|
||||||
let len = s.len();
|
let len = s.len();
|
||||||
@@ -19,6 +21,34 @@ pub fn parse_hex_color(s: &str) -> Result<(f64, f64, f64, f64), String> {
|
|||||||
Ok((r, g, b, a))
|
Ok((r, g, b, a))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_hex_color<'de, D>(deserializer: D) -> Result<(f64, f64, f64, f64), D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
parse_hex_color(&s).map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_hex_color<S>(
|
||||||
|
color: &(f64, f64, f64, f64),
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let (r, g, b, a) = color;
|
||||||
|
let r = (r * 255.0) as u8;
|
||||||
|
let g = (g * 255.0) as u8;
|
||||||
|
let b = (b * 255.0) as u8;
|
||||||
|
let a = (a * 255.0) as u8;
|
||||||
|
|
||||||
|
if a == 255 {
|
||||||
|
serializer.serialize_str(&format!("{:02x}{:02x}{:02x}", r, g, b))
|
||||||
|
} else {
|
||||||
|
serializer.serialize_str(&format!("{:02x}{:02x}{:02x}{:02x}", r, g, b, a))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_blur_effect(s: &str) -> Result<(u32, u32), String> {
|
pub fn parse_blur_effect(s: &str) -> Result<(u32, u32), String> {
|
||||||
let parts: Vec<&str> = s.split('x').collect();
|
let parts: Vec<&str> = s.split('x').collect();
|
||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
@@ -29,6 +59,32 @@ pub fn parse_blur_effect(s: &str) -> Result<(u32, u32), String> {
|
|||||||
Ok((radius, times))
|
Ok((radius, times))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_blur_effect<'de, D>(deserializer: D) -> Result<Option<(u32, u32)>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = Option::<String>::deserialize(deserializer)?;
|
||||||
|
match s {
|
||||||
|
Some(s) => parse_blur_effect(&s)
|
||||||
|
.map(Some)
|
||||||
|
.map_err(serde::de::Error::custom),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_blur_effect<S>(
|
||||||
|
val: &Option<(u32, u32)>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match val {
|
||||||
|
Some((radius, times)) => serializer.serialize_str(&format!("{}x{}", radius, times)),
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_vignette_effect(s: &str) -> Result<(f32, f32), String> {
|
pub fn parse_vignette_effect(s: &str) -> Result<(f32, f32), String> {
|
||||||
let parts: Vec<&str> = s.split(':').collect();
|
let parts: Vec<&str> = s.split(':').collect();
|
||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
@@ -38,3 +94,29 @@ pub fn parse_vignette_effect(s: &str) -> Result<(f32, f32), String> {
|
|||||||
let factor = parts[1].parse().map_err(|_| "Invalid factor")?;
|
let factor = parts[1].parse().map_err(|_| "Invalid factor")?;
|
||||||
Ok((base, factor))
|
Ok((base, factor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_vignette_effect<'de, D>(deserializer: D) -> Result<Option<(f32, f32)>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = Option::<String>::deserialize(deserializer)?;
|
||||||
|
match s {
|
||||||
|
Some(s) => parse_vignette_effect(&s)
|
||||||
|
.map(Some)
|
||||||
|
.map_err(serde::de::Error::custom),
|
||||||
|
None => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize_vignette_effect<S>(
|
||||||
|
val: &Option<(f32, f32)>,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match val {
|
||||||
|
Some((base, factor)) => serializer.serialize_str(&format!("{}:{}", base, factor)),
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user