1use std::env;
19use std::fs;
20use std::os;
21use std::path;
22
23use anyhow::Context;
24use drm::control::{Device as ControlDevice, Mode};
25use drm::Device;
26
27mod fsr;
28
29struct Card(std::fs::File);
32
33impl os::fd::AsFd for Card {
34 fn as_fd(&self) -> os::fd::BorrowedFd<'_> {
35 self.0.as_fd()
36 }
37}
38
39impl Card {
40 pub fn open<P: AsRef<path::Path>>(path: P) -> Self {
41 let mut options = std::fs::OpenOptions::new();
42 options.read(true);
43 options.write(true);
44 Card(options.open(path).unwrap())
45 }
46}
47
48impl Device for Card {}
50impl ControlDevice for Card {}
51
52pub fn gamescope(res: (u16, u16), fsr_mode: &str) -> anyhow::Result<Vec<String>> {
54 let gamescope_bin: String = env::var("RRES_GAMESCOPE").unwrap_or("gamescope".to_string());
55 let mut gamescope_runner: Vec<String> = vec![gamescope_bin];
56
57 let args = if !fsr_mode.is_empty() && fsr_mode.to_lowercase() != "native" {
58 let Ok(fsr) = fsr::Fsr::try_from(fsr_mode) else {
59 return Err(anyhow::anyhow!("invalid FSR mode: {}", fsr_mode));
60 };
61
62 let fsr_res = fsr.generate(res);
63 format!(
64 "-W {} -H {} -U -w {} -h {}",
65 res.0, res.1, fsr_res.0, fsr_res.1
66 )
67 } else {
68 format!("-W {} -H {}", res.0, res.1)
69 };
70
71 gamescope_runner.extend(args.split(' ').map(|s| s.to_owned()));
72
73 Ok(gamescope_runner)
74}
75
76pub fn get_displays(card: Option<String>) -> anyhow::Result<Vec<Mode>> {
78 let mut displays: Vec<Mode> = vec![];
80 let mut cards: Vec<path::PathBuf> = vec![];
82
83 if let Some(c) = card {
84 let mut file = path::PathBuf::from("/dev/dri/");
86 file.push(&c);
87 if !file.exists() || !c.starts_with("card") {
88 return Err(anyhow::anyhow!("invalid card ({c})"));
89 }
90 cards.push(file);
91 } else {
92 for entry in fs::read_dir("/dev/dri/")? {
94 let file = entry?;
95 if let Some(name) = file.file_name().to_str() {
96 if name.starts_with("card") {
97 cards.push(file.path());
98 }
99 }
100 }
101 }
102
103 cards.sort();
105
106 for file in cards {
108 let gpu = Card::open(file);
109 let info = gpu.get_driver()?;
110 log::debug!("Found GPU: {}", info.name().to_string_lossy());
111 match get_card_modes(&gpu) {
113 Ok(modes) => displays.extend_from_slice(&modes),
114 Err(e) => log::error!("failed to read modes: {e}"),
115 }
116 }
117
118 Ok(displays)
119}
120
121pub fn get_res() -> anyhow::Result<(u16, u16)> {
123 get_res_card(None)
124}
125
126pub fn get_res_card(card: Option<String>) -> anyhow::Result<(u16, u16)> {
128 let res;
129
130 if let Ok(forced) = env::var("RRES_FORCE_RES") {
131 if let Some((x, y)) = forced.split_once('x') {
132 res = (x.parse()?, y.parse()?);
133 } else {
134 return Err(anyhow::anyhow!("failed to parse RRES_FORCE_RES"));
135 }
136 } else {
137 let displays = get_displays(card)?;
138
139 let selection: usize = env::var("RRES_DISPLAY")
140 .unwrap_or_else(|_| "0".to_string())
141 .parse()
142 .context("Failed to parse RRES_DISPLAY")?;
143
144 if selection > displays.len() - 1 {
145 return Err(anyhow::anyhow!("invalid display: {}", selection));
146 }
147
148 res = displays[selection].size();
149 }
150
151 Ok(res)
152}
153
154pub fn get_card_modes<G: ControlDevice>(gpu: &G) -> anyhow::Result<Vec<Mode>> {
156 let mut modes: Vec<Mode> = vec![];
157
158 let resources = gpu
159 .resource_handles()
160 .context("failed to get resource handles")?;
161 let connectors = resources.connectors();
162 for handle in connectors {
163 let connector = gpu
164 .get_connector(*handle, false)
165 .context("failed to get connector handle")?;
166 if connector.state() == drm::control::connector::State::Connected {
167 modes.push(get_connector_mode(gpu, &connector)?);
169 }
170 }
171 Ok(modes)
172}
173
174fn get_connector_mode<G: ControlDevice>(
179 gpu: &G,
180 connector: &drm::control::connector::Info,
181) -> anyhow::Result<Mode> {
182 if connector.state() != drm::control::connector::State::Connected {
183 return Err(anyhow::anyhow!("Connector is disconnected"));
184 }
185 if let Some(encoder_handle) = connector.current_encoder() {
186 let encoder = gpu.get_encoder(encoder_handle)?;
188 if let Some(crtc_handle) = encoder.crtc() {
189 let crtc = gpu.get_crtc(crtc_handle).context("failed to get crtc")?;
190 if let Some(current_mode) = crtc.mode() {
192 log::debug!(
193 "Found display: {:?}, {}x{}",
194 connector.interface(),
195 current_mode.size().0,
196 current_mode.size().1
197 );
198 return Ok(current_mode);
199 }
200 }
201 }
202 log::warn!(
204 "Could not detect current mode for display {:?},",
205 connector.interface()
206 );
207 log::warn!("reading native resolution");
208 return Ok(connector.modes()[0]);
209}