Skip to content

Commit ea09334

Browse files
committed
config: Add screen-filter configuration and persistent state
1 parent c10d400 commit ea09334

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/config/mod.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub struct DynamicConfig {
6969
outputs: (Option<PathBuf>, OutputsConfig),
7070
numlock: (Option<PathBuf>, NumlockStateConfig),
7171
pub accessibility_zoom: (Option<PathBuf>, ZoomState),
72+
accessibility_filter: (Option<PathBuf>, ScreenFilter),
7273
}
7374

7475
#[derive(Debug, Deserialize, Serialize)]
@@ -182,6 +183,29 @@ pub struct ZoomState {
182183
pub last_level: f64,
183184
}
184185

186+
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)]
187+
pub struct ScreenFilter {
188+
pub inverted: bool,
189+
#[serde(skip_serializing_if = "Option::is_none")]
190+
pub color_filter: Option<ColorFilter>,
191+
}
192+
193+
impl ScreenFilter {
194+
pub fn is_noop(&self) -> bool {
195+
self.inverted == false && self.color_filter.is_none()
196+
}
197+
}
198+
199+
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
200+
#[repr(u8)]
201+
// these values need to match with offscreen.frag
202+
pub enum ColorFilter {
203+
Greyscale = 1,
204+
Protanopia = 2,
205+
Deuteranopia = 3,
206+
Tritanopia = 4,
207+
}
208+
185209
impl Config {
186210
pub fn load(loop_handle: &LoopHandle<'_, State>) -> Config {
187211
let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap();
@@ -311,6 +335,18 @@ impl Config {
311335
),
312336
};
313337

338+
let _ = loop_handle.insert_idle(|state| {
339+
let filter_conf = state.common.config.dynamic_conf.screen_filter();
340+
state
341+
.common
342+
.a11y_state
343+
.set_screen_inverted(filter_conf.inverted);
344+
state
345+
.common
346+
.a11y_state
347+
.set_screen_filter(filter_conf.color_filter);
348+
});
349+
314350
Config {
315351
dynamic_conf: Self::load_dynamic(xdg.as_ref(), &cosmic_comp_config),
316352
cosmic_conf: cosmic_comp_config,
@@ -337,10 +373,17 @@ impl Config {
337373
xdg.and_then(|base| base.place_state_file("cosmic-comp/a11y_zoom.ron").ok());
338374
let zoom = Self::load_zoom_state(&zoom_path, cosmic);
339375

376+
let filter_path = xdg.and_then(|base| {
377+
base.place_state_file("cosmic-comp/a11y_screen_filter.ron")
378+
.ok()
379+
});
380+
let filter = Self::load_filter_state(&filter_path);
381+
340382
DynamicConfig {
341383
outputs: (output_path, outputs),
342384
numlock: (numlock_path, numlock),
343385
accessibility_zoom: (zoom_path, zoom),
386+
accessibility_filter: (filter_path, filter),
344387
}
345388
}
346389

@@ -435,6 +478,29 @@ impl Config {
435478
}
436479
}
437480

481+
fn load_filter_state(path: &Option<PathBuf>) -> ScreenFilter {
482+
if let Some(path) = path.as_ref() {
483+
if path.exists() {
484+
match ron::de::from_reader::<_, ScreenFilter>(
485+
OpenOptions::new().read(true).open(path).unwrap(),
486+
) {
487+
Ok(config) => return config,
488+
Err(err) => {
489+
warn!(?err, "Failed to read screen_filter state, resetting..");
490+
if let Err(err) = std::fs::remove_file(path) {
491+
error!(?err, "Failed to remove screen_filter state.");
492+
}
493+
}
494+
};
495+
}
496+
}
497+
498+
ScreenFilter {
499+
inverted: false,
500+
color_filter: None,
501+
}
502+
}
503+
438504
pub fn shortcut_for_action(&self, action: &shortcuts::Action) -> Option<String> {
439505
self.shortcuts.shortcut_for_action(action)
440506
}
@@ -486,6 +552,7 @@ impl Config {
486552
if let Err(err) = backend.apply_config_for_outputs(
487553
false,
488554
loop_handle,
555+
self.dynamic_conf.screen_filter(),
489556
shell.clone(),
490557
workspace_state,
491558
xdg_activation_state,
@@ -511,6 +578,7 @@ impl Config {
511578
if let Err(err) = backend.apply_config_for_outputs(
512579
false,
513580
loop_handle,
581+
self.dynamic_conf.screen_filter(),
514582
shell.clone(),
515583
workspace_state,
516584
xdg_activation_state,
@@ -553,6 +621,7 @@ impl Config {
553621
if let Err(err) = backend.apply_config_for_outputs(
554622
false,
555623
loop_handle,
624+
self.dynamic_conf.screen_filter(),
556625
shell.clone(),
557626
workspace_state,
558627
xdg_activation_state,
@@ -720,6 +789,17 @@ impl DynamicConfig {
720789
&mut self.accessibility_zoom.1,
721790
)
722791
}
792+
793+
pub fn screen_filter(&self) -> &ScreenFilter {
794+
&self.accessibility_filter.1
795+
}
796+
797+
pub fn screen_filter_mut(&mut self) -> PersistenceGuard<'_, ScreenFilter> {
798+
PersistenceGuard(
799+
self.accessibility_filter.0.clone(),
800+
&mut self.accessibility_filter.1,
801+
)
802+
}
723803
}
724804

725805
fn get_config<T: Default + serde::de::DeserializeOwned>(

src/state.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
winit::WinitState,
88
x11::X11State,
99
},
10-
config::{Config, OutputConfig, OutputState},
10+
config::{Config, OutputConfig, OutputState, ScreenFilter},
1111
input::{gestures::GestureState, PointerFocusState},
1212
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
1313
utils::prelude::OutputExt,
@@ -451,6 +451,10 @@ impl BackendData {
451451
_ => unreachable!("No backend set when getting offscreen renderer"),
452452
}
453453
}
454+
455+
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> anyhow::Result<()> {
456+
let _ = screen_filter; // TODO
457+
}
454458
}
455459

456460
pub struct KmsNodes {

0 commit comments

Comments
 (0)