screenshot_rs/
lib.rs

1// Simple library that allows for simple method of asking for screenshots from various Linux/BSD desktops
2
3use std::env;
4use std::fs;
5use std::process::Command;
6
7static SELECTION_TEMPORARY_FILE: &str = "/tmp/selection-tmp.png";
8
9pub enum ScreenshotKind {
10    Area,
11    Window,
12    Full,
13}
14
15enum SessionKind {
16    Wayland,
17    X11,
18    Macos,
19}
20
21enum DesktopKind {
22    GNOME,
23    KDE,
24    Sway,
25    Generic,
26    Macos,
27}
28
29fn session_type() -> SessionKind {
30    return match env::var("XDG_SESSION_TYPE") {
31        Ok(ok) => match ok.to_lowercase().as_ref() {
32            "wayland" => SessionKind::Wayland,
33            _ => SessionKind::X11,
34        },
35        Err(_) => {
36            if !cfg!(target_os = "macos") {
37                SessionKind::Macos
38            } else {
39                SessionKind::X11
40            }
41        }
42    };
43}
44
45fn screenshot_tool_selection(session: SessionKind) -> DesktopKind {
46    return match session {
47        SessionKind::Wayland => match Command::new("grim").arg("--version").spawn() {
48            Ok(_) => DesktopKind::Sway,
49            Err(_) => match Command::new("spectacle").arg("--version").spawn() {
50                Ok(_) => DesktopKind::KDE,
51                Err(_) => match Command::new("gnome-screenshot").arg("--version").spawn() {
52                    Ok(_) => DesktopKind::GNOME,
53                    Err(_) => panic!("Uncompatible Wayland desktop"),
54                },
55            },
56        },
57        SessionKind::X11 => match Command::new("spectacle").arg("--version").spawn() {
58            Ok(_) => DesktopKind::KDE,
59            Err(_) => match Command::new("gnome-screenshot").arg("--version").spawn() {
60                Ok(_) => DesktopKind::GNOME,
61                Err(_) => match Command::new("scrot").arg("--version").spawn() {
62                    Ok(_) => DesktopKind::Generic,
63                    Err(_) => panic!("Uncompatible X11 desktop (install scrot)"),
64                },
65            },
66        },
67        SessionKind::Macos => DesktopKind::Macos,
68    };
69}
70
71pub fn screenshot_area(file: String, freeze: bool) {
72    match screenshot_tool_selection(session_type()) {
73        DesktopKind::GNOME => gnome(ScreenshotKind::Area, file, freeze),
74        DesktopKind::KDE => kde(ScreenshotKind::Area, file),
75        DesktopKind::Sway => sway(ScreenshotKind::Area, file),
76        DesktopKind::Generic => scrot(ScreenshotKind::Area, file, freeze),
77        DesktopKind::Macos => mac(ScreenshotKind::Area, file),
78    }
79}
80pub fn screenshot_window(file: String) {
81    match screenshot_tool_selection(session_type()) {
82        DesktopKind::GNOME => gnome(ScreenshotKind::Window, file, false),
83        DesktopKind::KDE => kde(ScreenshotKind::Window, file),
84        DesktopKind::Sway => sway(ScreenshotKind::Window, file),
85        DesktopKind::Generic => scrot(ScreenshotKind::Window, file, false),
86        DesktopKind::Macos => mac(ScreenshotKind::Window, file),
87    }
88}
89pub fn screenshot_full(file: String) {
90    match screenshot_tool_selection(session_type()) {
91        DesktopKind::GNOME => gnome(ScreenshotKind::Full, file, false),
92        DesktopKind::KDE => kde(ScreenshotKind::Full, file),
93        DesktopKind::Sway => sway(ScreenshotKind::Full, file),
94        DesktopKind::Generic => scrot(ScreenshotKind::Full, file, false),
95        DesktopKind::Macos => mac(ScreenshotKind::Full, file),
96    }
97}
98fn gnome(option: ScreenshotKind, file: String, freeze: bool) {
99    match option {
100        ScreenshotKind::Area => {
101            let mut feh = match Command::new("feh").arg("--version").spawn() {
102                Ok(_) => {
103                    Command::new("gnome-screenshot")
104                        .args(&["-f", SELECTION_TEMPORARY_FILE])
105                        .output()
106                        .expect("gnome-screenshot did not launch");
107                    Command::new("feh")
108                        .args(&[SELECTION_TEMPORARY_FILE, "-F"])
109                        .spawn()
110                        .expect("'feh' did not launch to pause screen for selection")
111                }
112                Err(_) => Command::new("sh")
113                    .arg("-c")
114                    .arg("echo Feh does not exist")
115                    .spawn()
116                    .unwrap(),
117            };
118            Command::new("gnome-screenshot")
119                .args(&["-a", "-f", &file])
120                .output()
121                .expect("gnome-screenshot did not launch");
122            if freeze {
123                match fs::remove_file(SELECTION_TEMPORARY_FILE) {
124                    Ok(ok) => ok,
125                    Err(_) => eprintln!("Unable to remove temporary selection file"),
126                };
127                match feh.kill() {
128                    Ok(ok) => ok,
129                    Err(_) => eprintln!("Unable to kill feh, must have already been closed"),
130                };
131            }
132        }
133        ScreenshotKind::Window => {
134            Command::new("gnome-screenshot")
135                .args(&["-w", "-e", "shadow", "-f", &file])
136                .output()
137                .expect("gnome-screenshot did not launch");
138        }
139        ScreenshotKind::Full => {
140            Command::new("gnome-screenshot")
141                .args(&["-f", &file])
142                .output()
143                .expect("gnome-screenshot did not launch");
144        }
145    };
146}
147fn kde(option: ScreenshotKind, file: String) {
148    match option {
149        ScreenshotKind::Area => {
150            Command::new("spectacle")
151                .args(&["-rbno", &file])
152                .output()
153                .expect("spectacle did not launch");
154        }
155        ScreenshotKind::Window => {
156            Command::new("spectacle")
157                .args(&["-abno", &file])
158                .output()
159                .expect("spectacle did not launch");
160        }
161        ScreenshotKind::Full => {
162            Command::new("spectacle")
163                .args(&["-fbno", &file])
164                .output()
165                .expect("spectacle did not launch");
166        }
167    };
168}
169fn sway(option: ScreenshotKind, file: String) {
170    match option {
171        ScreenshotKind::Area => {
172            let slurp = Command::new("slurp")
173                .output()
174                .expect("slurp did not launch");
175            Command::new("grim")
176                .args(&["-g", &String::from_utf8(slurp.stdout).unwrap(), &file])
177                .output()
178                .expect("grim did not launch");
179        }
180        ScreenshotKind::Window => {
181            let slurp = Command::new("slurp")
182                .output()
183                .expect("slurp did not launch");
184            Command::new("grim")
185                .args(&["-g", &String::from_utf8(slurp.stdout).unwrap(), &file])
186                .output()
187                .expect("grim did not launch");
188        }
189        ScreenshotKind::Full => {
190            Command::new("grim")
191                .arg(&file)
192                .output()
193                .expect("grim did not launch");
194        }
195    };
196}
197fn mac(option: ScreenshotKind, file: String) {
198    match option {
199        ScreenshotKind::Area => {
200            Command::new("screencapture")
201                .args(&["-s", &file])
202                .output()
203                .expect("screencapture did not launch");
204        }
205        ScreenshotKind::Window => {
206            Command::new("screencapture")
207                .args(&["-w", &file])
208                .output()
209                .expect("screencapture did not launch");
210        }
211        ScreenshotKind::Full => {
212            Command::new("screencapture")
213                .args(&["-S", &file])
214                .output()
215                .expect("screencapture did not launch");
216        }
217    };
218}
219fn scrot(option: ScreenshotKind, file: String, freeze: bool) {
220    match option {
221        ScreenshotKind::Area => {
222            let mut feh = match Command::new("feh").arg("--version").spawn() {
223                Ok(_) => {
224                    Command::new("scrot")
225                        .arg(SELECTION_TEMPORARY_FILE)
226                        .output()
227                        .expect("scrot did not launch");
228                    Command::new("feh")
229                        .args(&[SELECTION_TEMPORARY_FILE, "-F"])
230                        .spawn()
231                        .expect("'feh' did not launch to pause screen for selection")
232                }
233                Err(_) => Command::new("sh")
234                    .arg("-c")
235                    .arg("echo Feh does not exist")
236                    .spawn()
237                    .unwrap(),
238            };
239            Command::new("scrot")
240                .args(&["--select", &file])
241                .output()
242                .expect("scrot did not launch");
243            if freeze {
244                match fs::remove_file(SELECTION_TEMPORARY_FILE) {
245                    Ok(ok) => ok,
246                    Err(_) => eprintln!("Unable to remove temporary selection file"),
247                };
248                match feh.kill() {
249                    Ok(ok) => ok,
250                    Err(_) => eprintln!("Unable to kill feh, must have already been closed"),
251                };
252            }
253        }
254        ScreenshotKind::Window => {
255            Command::new("scrot")
256                .args(&["--border", "--focused", &file])
257                .output()
258                .expect("gnome-screenshot did not launch");
259        }
260        ScreenshotKind::Full => {
261            Command::new("scrot")
262                .args(&[&file])
263                .output()
264                .expect("gnome-screenshot did not launch");
265        }
266    };
267}