@@ -57,52 +57,137 @@ pub fn get_display_scale_factor() -> f32 {
5757#[ cfg( target_os = "linux" ) ]
5858fn linux_desktop_scale_candidate ( ) -> Option < f32 > {
5959 * LINUX_DESKTOP_SCALE_CANDIDATE . get_or_init ( || {
60- let scales = gnome_monitors_xml_scales ( ) ;
61- scales
62- . into_iter ( )
63- . filter ( |scale| scale. is_finite ( ) && * scale > 1.0 )
64- . max_by ( |a, b| a. total_cmp ( b) )
60+ let scales = gnome_monitor_scales ( ) ;
61+ match scales. primary_scale {
62+ Some ( scale) if scale. is_finite ( ) && scale > 1.0 => Some ( scale) ,
63+ Some ( _) => None ,
64+ None => scales
65+ . all_scales
66+ . into_iter ( )
67+ . filter ( |scale| scale. is_finite ( ) && * scale > 1.0 )
68+ . max_by ( |a, b| a. total_cmp ( b) ) ,
69+ }
6570 } )
6671}
6772
6873#[ cfg( target_os = "linux" ) ]
69- fn gnome_monitors_xml_scales ( ) -> Vec < f32 > {
74+ #[ derive( Default ) ]
75+ struct GnomeMonitorScales {
76+ primary_scale : Option < f32 > ,
77+ all_scales : Vec < f32 > ,
78+ }
79+
80+ #[ cfg( target_os = "linux" ) ]
81+ fn gnome_monitor_scales ( ) -> GnomeMonitorScales {
82+ let scales = gnome_monitors_xml_scales ( ) ;
83+ let primary_scale = scales
84+ . logical_monitors
85+ . iter ( )
86+ . find ( |monitor| monitor. primary )
87+ . and_then ( |monitor| monitor. scale ) ;
88+ let all_scales = scales
89+ . logical_monitors
90+ . into_iter ( )
91+ . filter_map ( |monitor| monitor. scale )
92+ . collect :: < Vec < _ > > ( ) ;
93+ GnomeMonitorScales {
94+ primary_scale,
95+ all_scales,
96+ }
97+ }
98+
99+ #[ cfg( target_os = "linux" ) ]
100+ #[ derive( Default ) ]
101+ struct GnomeMonitorsXml {
102+ logical_monitors : Vec < GnomeLogicalMonitor > ,
103+ }
104+
105+ #[ cfg( target_os = "linux" ) ]
106+ #[ derive( Default ) ]
107+ struct GnomeLogicalMonitor {
108+ primary : bool ,
109+ scale : Option < f32 > ,
110+ }
111+
112+ #[ cfg( target_os = "linux" ) ]
113+ enum MonitorXmlText {
114+ Primary ,
115+ Scale ,
116+ }
117+
118+ #[ cfg( target_os = "linux" ) ]
119+ fn gnome_monitors_xml_scales ( ) -> GnomeMonitorsXml {
70120 use quick_xml:: Reader ;
71121 use quick_xml:: events:: Event ;
72122
73- let Some ( home ) = std :: env :: var_os ( "HOME" ) else {
74- return Vec :: new ( ) ;
123+ let Some ( config_home ) = linux_config_home ( ) else {
124+ return GnomeMonitorsXml :: default ( ) ;
75125 } ;
76- let path = PathBuf :: from ( home ) . join ( ".config/ monitors.xml" ) ;
126+ let path = config_home . join ( "monitors.xml" ) ;
77127 let Ok ( contents) = std:: fs:: read_to_string ( path) else {
78- return Vec :: new ( ) ;
128+ return GnomeMonitorsXml :: default ( ) ;
79129 } ;
80130 let mut reader = Reader :: from_str ( & contents) ;
81131 reader. config_mut ( ) . trim_text ( true ) ;
82132
83- let mut scales = Vec :: new ( ) ;
84- let mut in_scale = false ;
133+ let mut logical_monitors = Vec :: new ( ) ;
134+ let mut current_monitor: Option < GnomeLogicalMonitor > = None ;
135+ let mut text_target: Option < MonitorXmlText > = None ;
85136 loop {
86137 match reader. read_event ( ) {
87- Ok ( Event :: Start ( element) ) if element. name ( ) . as_ref ( ) == b"scale" => {
88- in_scale = true ;
138+ Ok ( Event :: Start ( element) ) if element. name ( ) . as_ref ( ) == b"logicalmonitor" => {
139+ current_monitor = Some ( GnomeLogicalMonitor :: default ( ) ) ;
140+ }
141+ Ok ( Event :: End ( element) ) if element. name ( ) . as_ref ( ) == b"logicalmonitor" => {
142+ if let Some ( monitor) = current_monitor. take ( ) {
143+ logical_monitors. push ( monitor) ;
144+ }
145+ text_target = None ;
146+ }
147+ Ok ( Event :: Start ( element) ) if current_monitor. is_some ( ) => {
148+ text_target = match element. name ( ) . as_ref ( ) {
149+ b"primary" => Some ( MonitorXmlText :: Primary ) ,
150+ b"scale" => Some ( MonitorXmlText :: Scale ) ,
151+ _ => None ,
152+ } ;
89153 }
90- Ok ( Event :: End ( element) ) if element. name ( ) . as_ref ( ) == b"scale" => {
91- in_scale = false ;
154+ Ok ( Event :: End ( element) ) if matches ! ( element. name( ) . as_ref( ) , b"primary" | b"scale" ) => {
155+ text_target = None ;
92156 }
93- Ok ( Event :: Text ( text) ) if in_scale => {
94- if let Ok ( value ) = text . decode ( )
95- && let Ok ( scale ) = value . parse :: < f32 > ( )
157+ Ok ( Event :: Text ( text) ) => {
158+ if let Some ( monitor ) = current_monitor . as_mut ( )
159+ && let Some ( value ) = text . decode ( ) . ok ( )
96160 {
97- scales. push ( scale) ;
161+ match text_target {
162+ Some ( MonitorXmlText :: Primary ) => {
163+ let normalized = value. trim ( ) ;
164+ monitor. primary =
165+ normalized == "yes" || normalized == "true" || normalized == "1" ;
166+ }
167+ Some ( MonitorXmlText :: Scale ) => {
168+ if let Ok ( scale) = value. parse :: < f32 > ( ) {
169+ monitor. scale = Some ( scale) ;
170+ }
171+ }
172+ None => { }
173+ }
98174 }
99175 }
100176 Ok ( Event :: Eof ) => break ,
101- Err ( _) => return Vec :: new ( ) ,
177+ Err ( _) => return GnomeMonitorsXml :: default ( ) ,
102178 _ => { }
103179 }
104180 }
105- scales
181+
182+ GnomeMonitorsXml { logical_monitors }
183+ }
184+
185+ #[ cfg( target_os = "linux" ) ]
186+ fn linux_config_home ( ) -> Option < PathBuf > {
187+ std:: env:: var_os ( "XDG_CONFIG_HOME" )
188+ . filter ( |path| !path. is_empty ( ) )
189+ . map ( PathBuf :: from)
190+ . or_else ( || std:: env:: var_os ( "HOME" ) . map ( |home| PathBuf :: from ( home) . join ( ".config" ) ) )
106191}
107192
108193#[ cfg( target_os = "linux" ) ]
0 commit comments