Implement toggling pointer for the screenshot UI

This commit is contained in:
Ivan Molodetskikh
2024-07-07 09:23:40 +04:00
parent 62c9d44b04
commit f2766b103d
4 changed files with 181 additions and 34 deletions
+2
View File
@@ -948,6 +948,8 @@ pub enum Action {
ConfirmScreenshot, ConfirmScreenshot,
#[knuffel(skip)] #[knuffel(skip)]
CancelScreenshot, CancelScreenshot,
#[knuffel(skip)]
ScreenshotTogglePointer,
Screenshot, Screenshot,
ScreenshotScreen, ScreenshotScreen,
ScreenshotWindow, ScreenshotWindow,
+4
View File
@@ -508,6 +508,10 @@ impl State {
.set_cursor_image(CursorImageStatus::default_named()); .set_cursor_image(CursorImageStatus::default_named());
self.niri.queue_redraw_all(); self.niri.queue_redraw_all();
} }
Action::ScreenshotTogglePointer => {
self.niri.screenshot_ui.toggle_pointer();
self.niri.queue_redraw_all();
}
Action::Screenshot => { Action::Screenshot => {
self.backend.with_primary_renderer(|renderer| { self.backend.with_primary_renderer(|renderer| {
self.niri.open_screenshot_ui(renderer); self.niri.open_screenshot_ui(renderer);
+35 -11
View File
@@ -124,13 +124,14 @@ use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::renderer::NiriRenderer; use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::texture::TextureBuffer; use crate::render_helpers::texture::TextureBuffer;
use crate::render_helpers::{ use crate::render_helpers::{
render_to_shm, render_to_texture, render_to_vec, shaders, RenderTarget, render_to_encompassing_texture, render_to_shm, render_to_texture, render_to_vec, shaders,
RenderTarget,
}; };
use crate::ui::config_error_notification::ConfigErrorNotification; use crate::ui::config_error_notification::ConfigErrorNotification;
use crate::ui::exit_confirm_dialog::ExitConfirmDialog; use crate::ui::exit_confirm_dialog::ExitConfirmDialog;
use crate::ui::hotkey_overlay::HotkeyOverlay; use crate::ui::hotkey_overlay::HotkeyOverlay;
use crate::ui::screen_transition::{self, ScreenTransition}; use crate::ui::screen_transition::{self, ScreenTransition};
use crate::ui::screenshot_ui::{ScreenshotUi, ScreenshotUiRenderElement}; use crate::ui::screenshot_ui::{OutputScreenshot, ScreenshotUi, ScreenshotUiRenderElement};
use crate::utils::scale::{closest_representable_scale, guess_monitor_scale}; use crate::utils::scale::{closest_representable_scale, guess_monitor_scale};
use crate::utils::spawning::CHILD_ENV; use crate::utils::spawning::CHILD_ENV;
use crate::utils::{ use crate::utils::{
@@ -3790,8 +3791,8 @@ impl Niri {
RenderTarget::Screencast, RenderTarget::Screencast,
RenderTarget::ScreenCapture, RenderTarget::ScreenCapture,
]; ];
let textures = targets.map(|target| { let screenshot = targets.map(|target| {
let elements = self.render::<GlesRenderer>(renderer, &output, true, target); let elements = self.render::<GlesRenderer>(renderer, &output, false, target);
let elements = elements.iter().rev(); let elements = elements.iter().rev();
let res = render_to_texture( let res = render_to_texture(
@@ -3802,25 +3803,48 @@ impl Niri {
Fourcc::Abgr8888, Fourcc::Abgr8888,
elements, elements,
); );
if let Err(err) = &res { if let Err(err) = &res {
warn!("error rendering output {}: {err:?}", output.name()); warn!("error rendering output {}: {err:?}", output.name());
} }
let res_output = res.ok();
res let pointer = self.pointer_element(renderer, &output);
let res_pointer = if pointer.is_empty() {
None
} else {
let res = render_to_encompassing_texture(
renderer,
scale,
Transform::Normal,
Fourcc::Abgr8888,
&pointer,
);
if let Err(err) = &res {
warn!("error rendering pointer for {}: {err:?}", output.name());
}
res.ok()
};
res_output.map(|(texture, _)| {
OutputScreenshot::from_textures(
renderer,
scale,
texture,
res_pointer.map(|(texture, _, geo)| (texture, geo)),
)
})
}); });
if textures.iter().any(|res| res.is_err()) { if screenshot.iter().any(|res| res.is_none()) {
return None; return None;
} }
let textures = textures.map(|res| res.unwrap().0); let screenshot = screenshot.map(|res| res.unwrap());
Some((output, textures)) Some((output, screenshot))
}) })
.collect(); .collect();
self.screenshot_ui self.screenshot_ui.open(screenshots, default_output);
.open(renderer, screenshots, default_output);
self.cursor_manager self.cursor_manager
.set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair)); .set_cursor_image(CursorImageStatus::Named(CursorIcon::Crosshair));
self.queue_redraw_all(); self.queue_redraw_all();
+140 -23
View File
@@ -13,13 +13,13 @@ use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::ExportMem; use smithay::backend::renderer::ExportMem;
use smithay::input::keyboard::{Keysym, ModifiersState}; use smithay::input::keyboard::{Keysym, ModifiersState};
use smithay::output::{Output, WeakOutput}; use smithay::output::{Output, WeakOutput};
use smithay::utils::{Physical, Point, Rectangle, Size, Transform}; use smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size, Transform};
use crate::niri_render_elements; use crate::niri_render_elements;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement}; use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement}; use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};
use crate::render_helpers::RenderTarget; use crate::render_helpers::{render_to_texture, RenderTarget};
use crate::utils::to_physical_precise_round; use crate::utils::to_physical_precise_round;
const BORDER: i32 = 2; const BORDER: i32 = 2;
@@ -37,6 +37,7 @@ pub enum ScreenshotUi {
selection: (Output, Point<i32, Physical>, Point<i32, Physical>), selection: (Output, Point<i32, Physical>, Point<i32, Physical>),
output_data: HashMap<Output, OutputData>, output_data: HashMap<Output, OutputData>,
mouse_down: bool, mouse_down: bool,
show_pointer: bool,
}, },
} }
@@ -45,12 +46,17 @@ pub struct OutputData {
scale: f64, scale: f64,
transform: Transform, transform: Transform,
// Output, screencast, screen capture. // Output, screencast, screen capture.
texture: [GlesTexture; 3], screenshot: [OutputScreenshot; 3],
texture_buffer: [TextureBuffer<GlesTexture>; 3],
buffers: [SolidColorBuffer; 8], buffers: [SolidColorBuffer; 8],
locations: [Point<i32, Physical>; 8], locations: [Point<i32, Physical>; 8],
} }
pub struct OutputScreenshot {
texture: GlesTexture,
buffer: TextureBuffer<GlesTexture>,
pointer: Option<(TextureBuffer<GlesTexture>, Rectangle<f64, Logical>)>,
}
niri_render_elements! { niri_render_elements! {
ScreenshotUiRenderElement => { ScreenshotUiRenderElement => {
Screenshot = PrimaryGpuTextureRenderElement, Screenshot = PrimaryGpuTextureRenderElement,
@@ -67,9 +73,8 @@ impl ScreenshotUi {
pub fn open( pub fn open(
&mut self, &mut self,
renderer: &GlesRenderer,
// Output, screencast, screen capture. // Output, screencast, screen capture.
screenshots: HashMap<Output, [GlesTexture; 3]>, screenshots: HashMap<Output, [OutputScreenshot; 3]>,
default_output: Output, default_output: Output,
) -> bool { ) -> bool {
if screenshots.is_empty() { if screenshots.is_empty() {
@@ -108,20 +113,11 @@ impl ScreenshotUi {
let output_data = screenshots let output_data = screenshots
.into_iter() .into_iter()
.map(|(output, texture)| { .map(|(output, screenshot)| {
let transform = output.current_transform(); let transform = output.current_transform();
let output_mode = output.current_mode().unwrap(); let output_mode = output.current_mode().unwrap();
let size = transform.transform_size(output_mode.size); let size = transform.transform_size(output_mode.size);
let scale = output.current_scale().fractional_scale(); let scale = output.current_scale().fractional_scale();
let texture_buffer = texture.clone().map(|texture| {
TextureBuffer::from_texture(
renderer,
texture,
scale,
Transform::Normal,
Vec::new(),
)
});
let buffers = [ let buffers = [
SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]), SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),
SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]), SolidColorBuffer::new((0., 0.), [1., 1., 1., 1.]),
@@ -137,8 +133,7 @@ impl ScreenshotUi {
size, size,
scale, scale,
transform, transform,
texture, screenshot,
texture_buffer,
buffers, buffers,
locations, locations,
}; };
@@ -150,6 +145,7 @@ impl ScreenshotUi {
selection, selection,
output_data, output_data,
mouse_down: false, mouse_down: false,
show_pointer: true,
}; };
self.update_buffers(); self.update_buffers();
@@ -177,6 +173,12 @@ impl ScreenshotUi {
true true
} }
pub fn toggle_pointer(&mut self) {
if let Self::Open { show_pointer, .. } = self {
*show_pointer = !*show_pointer;
}
}
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
matches!(self, ScreenshotUi::Open { .. }) matches!(self, ScreenshotUi::Open { .. })
} }
@@ -258,10 +260,15 @@ impl ScreenshotUi {
&self, &self,
output: &Output, output: &Output,
target: RenderTarget, target: RenderTarget,
) -> ArrayVec<ScreenshotUiRenderElement, 9> { ) -> ArrayVec<ScreenshotUiRenderElement, 10> {
let _span = tracy_client::span!("ScreenshotUi::render_output"); let _span = tracy_client::span!("ScreenshotUi::render_output");
let Self::Open { output_data, .. } = self else { let Self::Open {
output_data,
show_pointer,
..
} = self
else {
panic!("screenshot UI must be open to render it"); panic!("screenshot UI must be open to render it");
}; };
@@ -288,9 +295,26 @@ impl ScreenshotUi {
RenderTarget::Screencast => 1, RenderTarget::Screencast => 1,
RenderTarget::ScreenCapture => 2, RenderTarget::ScreenCapture => 2,
}; };
if *show_pointer {
if let Some((buffer, geo)) = output_data.screenshot[index].pointer.clone() {
elements.push( elements.push(
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer( PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
output_data.texture_buffer[index].clone(), buffer,
geo.loc,
1.,
None,
None,
Kind::Unspecified,
))
.into(),
);
}
}
elements.push(
PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
output_data.screenshot[index].buffer.clone(),
(0., 0.), (0., 0.),
1., 1.,
None, None,
@@ -312,6 +336,7 @@ impl ScreenshotUi {
let Self::Open { let Self::Open {
selection, selection,
output_data, output_data,
show_pointer,
.. ..
} = self } = self
else { else {
@@ -320,12 +345,67 @@ impl ScreenshotUi {
let data = &output_data[&selection.0]; let data = &output_data[&selection.0];
let rect = rect_from_corner_points(selection.1, selection.2); let rect = rect_from_corner_points(selection.1, selection.2);
let screenshot = &data.screenshot[0];
// Composite the pointer on top if needed.
let mut tex_rect = None;
if *show_pointer {
if let Some((buffer, geo)) = screenshot.pointer.clone() {
let scale = buffer.texture_scale();
let offset = rect.loc.to_f64().to_logical(scale);
let offset = offset.upscale(-1.);
let mut elements = ArrayVec::<_, 2>::new();
elements.push(PrimaryGpuTextureRenderElement(
TextureRenderElement::from_texture_buffer(
buffer,
geo.loc + offset,
1.,
None,
None,
Kind::Unspecified,
),
));
elements.push(PrimaryGpuTextureRenderElement(
TextureRenderElement::from_texture_buffer(
screenshot.buffer.clone(),
offset,
1.,
None,
None,
Kind::Unspecified,
),
));
let elements = elements.iter().rev();
let res = render_to_texture(
renderer,
rect.size,
scale,
Transform::Normal,
Fourcc::Abgr8888,
elements,
);
match res {
Ok((texture, _)) => {
tex_rect = Some((texture, Rectangle::from_loc_and_size((0, 0), rect.size)));
}
Err(err) => {
warn!("error compositing pointer onto screenshot: {err:?}");
}
}
}
}
let (texture, rect) = tex_rect.unwrap_or_else(|| (screenshot.texture.clone(), rect));
// The size doesn't actually matter because we're not transforming anything.
let buf_rect = rect let buf_rect = rect
.to_logical(1) .to_logical(1)
.to_buffer(1, Transform::Normal, &data.size.to_logical(1)); .to_buffer(1, Transform::Normal, &Size::from((1, 1)));
let mapping = renderer let mapping = renderer
.copy_texture(&data.texture[0], buf_rect, Fourcc::Abgr8888) .copy_texture(&texture, buf_rect, Fourcc::Abgr8888)
.context("error copying texture")?; .context("error copying texture")?;
let copy = renderer let copy = renderer
.map_texture(&mapping) .map_texture(&mapping)
@@ -389,6 +469,7 @@ impl ScreenshotUi {
selection, selection,
output_data, output_data,
mouse_down, mouse_down,
..
} = self } = self
else { else {
return false; return false;
@@ -438,6 +519,38 @@ impl Default for ScreenshotUi {
} }
} }
impl OutputScreenshot {
pub fn from_textures(
renderer: &mut GlesRenderer,
scale: Scale<f64>,
texture: GlesTexture,
pointer: Option<(GlesTexture, Rectangle<i32, Physical>)>,
) -> Self {
Self {
texture: texture.clone(),
buffer: TextureBuffer::from_texture(
renderer,
texture,
scale,
Transform::Normal,
Vec::new(),
),
pointer: pointer.map(|(texture, geo)| {
(
TextureBuffer::from_texture(
renderer,
texture,
scale,
Transform::Normal,
Vec::new(),
),
geo.to_f64().to_logical(scale),
)
}),
}
}
}
fn action(raw: Keysym, mods: ModifiersState) -> Option<Action> { fn action(raw: Keysym, mods: ModifiersState) -> Option<Action> {
if raw == Keysym::Escape { if raw == Keysym::Escape {
return Some(Action::CancelScreenshot); return Some(Action::CancelScreenshot);
@@ -453,6 +566,10 @@ fn action(raw: Keysym, mods: ModifiersState) -> Option<Action> {
return Some(Action::ConfirmScreenshot); return Some(Action::ConfirmScreenshot);
} }
if !mods.ctrl && raw == Keysym::p {
return Some(Action::ScreenshotTogglePointer);
}
None None
} }