@@ -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+
185209impl 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
725805fn get_config < T : Default + serde:: de:: DeserializeOwned > (
0 commit comments