input: Add screenshotting shortcut
This commit is contained in:
parent
979e282768
commit
63b252e47d
8 changed files with 193 additions and 14 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
|
@ -33,6 +33,12 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler32"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
|
|
@ -160,6 +166,18 @@ version = "3.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calloop"
|
name = "calloop"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
@ -242,6 +260,12 @@ dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
@ -335,6 +359,7 @@ dependencies = [
|
||||||
"edid-rs",
|
"edid-rs",
|
||||||
"egui",
|
"egui",
|
||||||
"id_tree",
|
"id_tree",
|
||||||
|
"image",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libsystemd",
|
"libsystemd",
|
||||||
|
|
@ -379,6 +404,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
|
@ -450,6 +484,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
|
||||||
|
dependencies = [
|
||||||
|
"adler32",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
|
@ -757,6 +800,20 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.24.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
"byteorder",
|
||||||
|
"color_quant",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"png",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.1"
|
version = "1.9.1"
|
||||||
|
|
@ -1081,6 +1138,27 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
@ -1213,6 +1291,18 @@ version = "0.3.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"deflate",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ serde_json = "1"
|
||||||
sendfd = "0.4.1"
|
sendfd = "0.4.1"
|
||||||
egui = { version = "0.18.1", optional = true }
|
egui = { version = "0.18.1", optional = true }
|
||||||
edid-rs = { version = "0.1" }
|
edid-rs = { version = "0.1" }
|
||||||
|
image = { version = "0.24.3", default-features = false, features = ["png"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
thiserror = "1.0.26"
|
thiserror = "1.0.26"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
(modifiers: [Logo], key: "y"): ToggleTiling,
|
(modifiers: [Logo], key: "y"): ToggleTiling,
|
||||||
(modifiers: [Logo], key: "g"): ToggleWindowFloating,
|
(modifiers: [Logo], key: "g"): ToggleWindowFloating,
|
||||||
(modifiers: [Logo, Shift], key: "f"): Fullscreen,
|
(modifiers: [Logo, Shift], key: "f"): Fullscreen,
|
||||||
|
(modifiers: [Logo, Shift], key: "s"): Screenshot,
|
||||||
//TODO: ability to select default web browser
|
//TODO: ability to select default web browser
|
||||||
(modifiers: [Logo], key: "b"): Spawn("firefox"),
|
(modifiers: [Logo], key: "b"): Spawn("firefox"),
|
||||||
//TODO: ability to select default file browser
|
//TODO: ability to select default file browser
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ use socket::*;
|
||||||
|
|
||||||
pub struct KmsState {
|
pub struct KmsState {
|
||||||
devices: HashMap<DrmNode, Device>,
|
devices: HashMap<DrmNode, Device>,
|
||||||
api: GpuManager<EglGlesBackend>,
|
pub api: GpuManager<EglGlesBackend>,
|
||||||
pub primary: DrmNode,
|
pub primary: DrmNode,
|
||||||
session: AutoSession,
|
session: AutoSession,
|
||||||
signaler: Signaler<Signal>,
|
signaler: Signaler<Signal>,
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ pub fn render_output<R>(
|
||||||
state: &mut Common,
|
state: &mut Common,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
hardware_cursor: bool,
|
hardware_cursor: bool,
|
||||||
#[cfg(feature = "debug")] fps: &mut Fps,
|
#[cfg(feature = "debug")] fps: Option<&mut Fps>,
|
||||||
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + AsGles2Renderer,
|
R: Renderer + ImportAll + AsGles2Renderer,
|
||||||
|
|
@ -198,7 +198,7 @@ fn render_desktop<R>(
|
||||||
state: &mut Common,
|
state: &mut Common,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
hardware_cursor: bool,
|
hardware_cursor: bool,
|
||||||
#[cfg(feature = "debug")] fps: &mut Fps,
|
#[cfg(feature = "debug")] fps: Option<&mut Fps>,
|
||||||
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + AsGles2Renderer,
|
R: Renderer + ImportAll + AsGles2Renderer,
|
||||||
|
|
@ -216,14 +216,16 @@ where
|
||||||
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)));
|
.unwrap_or(Rectangle::from_loc_and_size((0, 0), (0, 0)));
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
|
|
||||||
let fps_overlay = fps_ui(
|
if let Some(fps) = fps {
|
||||||
_gpu,
|
let fps_overlay = fps_ui(
|
||||||
state,
|
_gpu,
|
||||||
fps,
|
state,
|
||||||
output_geo.to_f64().to_physical(scale),
|
fps,
|
||||||
scale,
|
output_geo.to_f64().to_physical(scale),
|
||||||
);
|
scale,
|
||||||
custom_elements.push(fps_overlay.into());
|
);
|
||||||
|
custom_elements.push(fps_overlay.into());
|
||||||
|
}
|
||||||
|
|
||||||
let area = Rectangle::<f64, smithay::utils::Logical>::from_loc_and_size(
|
let area = Rectangle::<f64, smithay::utils::Logical>::from_loc_and_size(
|
||||||
state
|
state
|
||||||
|
|
@ -282,7 +284,7 @@ fn render_fullscreen<R>(
|
||||||
state: &mut Common,
|
state: &mut Common,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
hardware_cursor: bool,
|
hardware_cursor: bool,
|
||||||
#[cfg(feature = "debug")] fps: &mut Fps,
|
#[cfg(feature = "debug")] fps: Option<&mut Fps>,
|
||||||
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
) -> Result<Option<Vec<Rectangle<i32, Physical>>>, RenderError<R>>
|
||||||
where
|
where
|
||||||
R: Renderer + ImportAll + AsGles2Renderer,
|
R: Renderer + ImportAll + AsGles2Renderer,
|
||||||
|
|
@ -296,7 +298,7 @@ where
|
||||||
let mut custom_elements = Vec::<CustomElem>::new();
|
let mut custom_elements = Vec::<CustomElem>::new();
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
{
|
if let Some(fps) = fps {
|
||||||
let output_geo = output.geometry();
|
let output_geo = output.geometry();
|
||||||
let fps_overlay = fps_ui(
|
let fps_overlay = fps_ui(
|
||||||
_gpu,
|
_gpu,
|
||||||
|
|
|
||||||
|
|
@ -752,5 +752,6 @@ pub enum Action {
|
||||||
ToggleTiling,
|
ToggleTiling,
|
||||||
ToggleWindowFloating,
|
ToggleWindowFloating,
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
|
Screenshot,
|
||||||
Spawn(String),
|
Spawn(String),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,35 @@ impl State {
|
||||||
slog_scope::warn!("Failed to spawn: {}", err);
|
slog_scope::warn!("Failed to spawn: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action::Screenshot => {
|
||||||
|
let home = match std::env::var("HOME") {
|
||||||
|
Ok(home) => home,
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::error!("$HOME is not set, can't save screenshots: {}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let timestamp = match std::time::SystemTime::UNIX_EPOCH.elapsed() {
|
||||||
|
Ok(duration) => duration.as_secs(),
|
||||||
|
Err(err) => {
|
||||||
|
slog_scope::error!("Unable to get timestamp, can't save screenshots: {}", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for output in self.common.shell.outputs.clone().into_iter() {
|
||||||
|
match self.backend.offscreen_for_output(&output, &mut self.common) {
|
||||||
|
Ok(buffer) => {
|
||||||
|
let mut path = std::path::PathBuf::new();
|
||||||
|
path.push(&home);
|
||||||
|
path.push(format!("{}_{}.png", output.name(), timestamp));
|
||||||
|
if let Err(err) = buffer.save(&path) {
|
||||||
|
slog_scope::error!("Unable to save screenshot at {}: {}", path.display(), err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => slog_scope::error!("Could not save screenshot for output {}: {}", output.name(), err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
57
src/state.rs
57
src/state.rs
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
wayland::protocols::{
|
wayland::protocols::{
|
||||||
drm::WlDrmState, output_configuration::OutputConfigurationState,
|
drm::WlDrmState, output_configuration::OutputConfigurationState,
|
||||||
workspace::WorkspaceClientState,
|
workspace::WorkspaceClientState,
|
||||||
},
|
}, utils::prelude::OutputExt,
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::drm::DrmNode,
|
backend::drm::DrmNode,
|
||||||
|
|
@ -199,6 +199,61 @@ impl BackendData {
|
||||||
_ => unreachable!("No backend was initialized"),
|
_ => unreachable!("No backend was initialized"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn offscreen_for_output(
|
||||||
|
&mut self,
|
||||||
|
output: &Output,
|
||||||
|
state: &mut Common,
|
||||||
|
) -> anyhow::Result<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>> {
|
||||||
|
use anyhow::Context;
|
||||||
|
use smithay::{
|
||||||
|
backend::{
|
||||||
|
drm::NodeType,
|
||||||
|
renderer::{
|
||||||
|
Bind, Offscreen, ExportMem,
|
||||||
|
gles2::Gles2Renderbuffer,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils::Rectangle,
|
||||||
|
};
|
||||||
|
use crate::backend::render::render_output;
|
||||||
|
|
||||||
|
let mut _tmp_multirenderer = None;
|
||||||
|
let (node, renderer) = match self {
|
||||||
|
BackendData::Winit(winit) => (None, winit.backend.renderer()),
|
||||||
|
BackendData::X11(x11) => (None, &mut x11.renderer),
|
||||||
|
BackendData::Kms(kms) => {
|
||||||
|
let node = kms.target_node_for_output(output)
|
||||||
|
.unwrap_or(kms.primary)
|
||||||
|
.node_with_type(NodeType::Render)
|
||||||
|
.with_context(|| "Unable to find node")??;
|
||||||
|
_tmp_multirenderer = Some(kms.api.renderer::<Gles2Renderbuffer>(&node, &node)?);
|
||||||
|
(Some(node), _tmp_multirenderer.as_mut().map(|x| x.as_mut()).unwrap())
|
||||||
|
},
|
||||||
|
BackendData::Unset => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = output.geometry().size.to_f64().to_buffer(
|
||||||
|
output.current_scale().fractional_scale(),
|
||||||
|
output.current_transform().into()
|
||||||
|
).to_i32_round();
|
||||||
|
let buffer = Offscreen::<Gles2Renderbuffer>::create_buffer(renderer, size)?;
|
||||||
|
renderer.bind(buffer)?;
|
||||||
|
render_output(
|
||||||
|
node.as_ref(),
|
||||||
|
renderer,
|
||||||
|
0,
|
||||||
|
state,
|
||||||
|
output,
|
||||||
|
false,
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let mapping = renderer.copy_framebuffer(Rectangle::from_loc_and_size((0, 0), size))?;
|
||||||
|
let data = Vec::from(renderer.map_texture(&mapping)?);
|
||||||
|
|
||||||
|
Ok(image::ImageBuffer::from_raw(size.w as u32, size.h as u32, data).with_context(|| "buffer smaller then dimensions")?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue