render: Add focus indicator

This commit is contained in:
Victoria Brekenfeld 2023-02-25 00:17:54 +01:00
parent a8920f533e
commit 9b416b5779
11 changed files with 291 additions and 68 deletions

View file

@ -72,7 +72,7 @@ mod drm_helpers;
mod socket;
use socket::*;
use super::render::{CursorMode, GlMultiRenderer};
use super::render::{init_shaders, CursorMode, GlMultiRenderer};
// for now we assume we need at least 3ms
const MIN_RENDER_TIME: Duration = Duration::from_millis(3);
@ -542,14 +542,18 @@ impl State {
render_node
)
})?;
let mut renderer = match backend.api.single_renderer(&render_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Failed to initialize output.");
backend.api.as_mut().remove_node(&render_node);
return Ok(());
}
};
init_shaders(&mut renderer).expect("Failed to initialize renderer");
for (crtc, conn) in outputs {
let mut renderer = match backend.api.single_renderer(&render_node) {
Ok(renderer) => renderer,
Err(err) => {
warn!(?err, "Failed to initialize output.");
continue;
}
};
match device.setup_surface(crtc, conn, (w, 0), &mut renderer) {
Ok(output) => {
w += output

View file

@ -1,5 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only
use std::{
borrow::{Borrow, BorrowMut},
cell::RefCell,
};
#[cfg(feature = "debug")]
use crate::{debug::fps_ui, utils::prelude::*};
use crate::{
@ -26,15 +31,18 @@ use smithay::{
damage::{
DamageTrackedRenderer, DamageTrackedRendererError as RenderError, OutputNoMode,
},
element::{RenderElement, RenderElementStates},
gles2::Gles2Error,
element::{Element, RenderElement, RenderElementStates},
gles2::{
element::PixelShaderElement, Gles2Error, Gles2PixelProgram, Gles2Renderer, Uniform,
UniformName, UniformType,
},
glow::GlowRenderer,
multigpu::{gbm::GbmGlesBackend, MultiFrame, MultiRenderer},
Bind, Blit, ExportMem, ImportAll, ImportMem, Offscreen, Renderer, TextureFilter,
},
},
output::Output,
utils::{Physical, Rectangle},
utils::{Logical, Physical, Point, Rectangle, Size},
wayland::dmabuf::get_dmabuf,
};
use tracing::warn;
@ -50,6 +58,93 @@ pub type GlMultiFrame<'a, 'b, 'frame> =
MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
pub static FOCUS_INDICATOR_COLOR: [f32; 4] = [0.580, 0.921, 0.921, 1.0];
pub static FOCUS_INDICATOR_THICKNESS: f32 = 4.0;
pub static FOCUS_INDICATOR_RADIUS: f32 = 8.0;
pub static FOCUS_INDICATOR_SHADER: &str = include_str!("./shaders/focus_indicator.frag");
pub struct IndicatorShader(pub Gles2PixelProgram);
struct IndicatorElement(pub RefCell<PixelShaderElement>);
impl IndicatorShader {
pub fn get<R: AsGlowRenderer>(renderer: &R) -> Gles2PixelProgram {
Borrow::<Gles2Renderer>::borrow(renderer.glow_renderer())
.egl_context()
.user_data()
.get::<IndicatorShader>()
.expect("Custom Shaders not initialized")
.0
.clone()
}
pub fn element<R: AsGlowRenderer>(
renderer: &R,
geo: Rectangle<i32, Logical>,
) -> PixelShaderElement {
let thickness = FOCUS_INDICATOR_THICKNESS;
let thickness_loc = (thickness as i32, thickness as i32);
let thickness_size = ((thickness * 2.0) as i32, (thickness * 2.0) as i32);
let geo = Rectangle::from_loc_and_size(
geo.loc - Point::from(thickness_loc),
geo.size + Size::from(thickness_size),
);
let user_data = Borrow::<Gles2Renderer>::borrow(renderer.glow_renderer())
.egl_context()
.user_data();
match user_data.get::<IndicatorElement>() {
Some(elem) => {
let mut elem = elem.0.borrow_mut();
if elem.geometry(1.0.into()).to_logical(1) != geo {
elem.resize(geo, None);
}
elem.clone()
}
None => {
let shader = Self::get(renderer);
let color = FOCUS_INDICATOR_COLOR;
let elem = PixelShaderElement::new(
shader,
dbg!(geo),
None, //TODO
color[3],
vec![
Uniform::new("color", [color[0], color[1], color[2]]),
Uniform::new("thickness", thickness),
Uniform::new("radius", FOCUS_INDICATOR_RADIUS),
],
);
if !user_data.insert_if_missing(|| IndicatorElement(RefCell::new(elem.clone()))) {
*user_data.get::<IndicatorElement>().unwrap().0.borrow_mut() = elem.clone();
}
elem
}
}
}
}
pub fn init_shaders<R: AsGlowRenderer>(renderer: &mut R) -> Result<(), Gles2Error> {
let glow_renderer = renderer.glow_renderer_mut();
let gles_renderer: &mut Gles2Renderer = glow_renderer.borrow_mut();
let indicator_shader = gles_renderer.compile_custom_pixel_shader(
FOCUS_INDICATOR_SHADER,
&[
UniformName::new("color", UniformType::_3f),
UniformName::new("thickness", UniformType::_1f),
UniformName::new("radius", UniformType::_1f),
],
)?;
let egl_context = gles_renderer.egl_context();
egl_context
.user_data()
.insert_if_missing(|| IndicatorShader(indicator_shader));
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CursorMode {
@ -248,6 +343,13 @@ where
}
}
let last_active_seat = state.last_active_seat().clone();
let move_active = last_active_seat
.user_data()
.get::<SeatMoveGrabState>()
.unwrap()
.borrow()
.is_some();
elements.extend(
workspace
.render_output::<R>(
@ -255,6 +357,7 @@ where
output,
&state.shell.override_redirect_windows,
state.xwayland_state.values_mut(),
(!move_active).then_some(&last_active_seat),
exclude_workspace_overview,
)
.map_err(|_| OutputNoMode)?

View file

@ -0,0 +1,33 @@
precision mediump float;
uniform float alpha;
#if defined(DEBUG_FLAGS)
uniform float tint;
#endif
uniform vec2 size;
varying vec2 v_coords;
uniform vec3 color;
uniform float thickness;
uniform float radius;
float rounded_box(vec2 center, vec2 size, float radius) {
return length(max(abs(center) - size + radius, 0.0)) - radius;
}
void main() {
vec2 center = size / 2.0 - vec2(0.5);
vec2 location = v_coords * size;
vec4 mix_color;
float distance = rounded_box(location - center, size / 2.0 - vec2(thickness / 2.0), radius);
float smoothedAlpha = 1.0 - smoothstep(0.0, 2.0, abs(distance) - (thickness / 2.0));
mix_color = mix(vec4(0.0, 0.0, 0.0, 0.0), vec4(color, alpha), smoothedAlpha);
#if defined(DEBUG_FLAGS)
if (tint == 1.0)
mix_color = vec4(0.0, 0.3, 0.0, 0.2) + mix_color * 0.8;
#endif
gl_FragColor = mix_color;
}

View file

@ -32,7 +32,7 @@ use tracing::{error, info, warn};
#[cfg(feature = "debug")]
use crate::state::Fps;
use super::render::CursorMode;
use super::render::{init_shaders, CursorMode};
pub struct WinitState {
// The winit backend currently has no notion of multiple windows
@ -147,6 +147,7 @@ pub fn init_backend(
) -> Result<()> {
let (mut backend, mut input) =
winit::init().map_err(|_| anyhow!("Failed to initilize winit backend"))?;
init_shaders(backend.renderer()).expect("Failed to initialize renderer");
init_egl_client_side(dh, state, &mut backend)?;

View file

@ -42,6 +42,8 @@ use tracing::{debug, error, info, warn};
#[cfg(feature = "debug")]
use crate::state::Fps;
use super::render::init_shaders;
enum Allocator {
Gbm(GbmAllocator<DrmDeviceFd>),
Vulkan(PhysicalDevice),
@ -348,6 +350,7 @@ pub fn init_backend(
let mut renderer =
unsafe { GlowRenderer::new(context) }.with_context(|| "Failed to initialize renderer")?;
init_shaders(&mut renderer).expect("Failed to initialize renderer");
init_egl_client_side(dh, state, &mut renderer)?;
state.backend = BackendData::X11(X11State {

View file

@ -1,5 +1,8 @@
use crate::{
backend::render::{element::AsGlowRenderer, GlMultiFrame, GlMultiRenderer},
backend::render::{
element::{AsGlowFrame, AsGlowRenderer},
GlMultiFrame, GlMultiRenderer,
},
state::State,
utils::prelude::SeatExt,
};
@ -9,7 +12,9 @@ use smithay::{
input::KeyState,
renderer::{
element::{AsRenderElements, Element, RenderElement, UnderlyingStorage},
gles2::element::PixelShaderElement,
glow::GlowRenderer,
multigpu::Error as MultiError,
ImportAll, ImportMem, Renderer,
},
},
@ -46,14 +51,10 @@ pub use self::stack::CosmicStack;
pub mod window;
pub use self::window::CosmicWindow;
#[cfg(feature = "debug")]
use crate::backend::render::element::AsGlowFrame;
#[cfg(feature = "debug")]
use egui::plot::{Corner, Legend, Plot, PlotPoints, Polygon};
#[cfg(feature = "debug")]
use smithay::backend::renderer::{
element::texture::TextureRenderElement, gles2::Gles2Texture, multigpu::Error as MultiError,
};
use smithay::backend::renderer::{element::texture::TextureRenderElement, gles2::Gles2Texture};
#[cfg(feature = "debug")]
use tracing::debug;
@ -659,6 +660,7 @@ where
{
Stack(self::stack::CosmicStackRenderElement<R>),
Window(self::window::CosmicWindowRenderElement<R>),
Indicator(PixelShaderElement),
#[cfg(feature = "debug")]
Egui(TextureRenderElement<Gles2Texture>),
}
@ -672,6 +674,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.id(),
CosmicMappedRenderElement::Window(elem) => elem.id(),
CosmicMappedRenderElement::Indicator(elem) => elem.id(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.id(),
}
@ -681,6 +684,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.current_commit(),
CosmicMappedRenderElement::Window(elem) => elem.current_commit(),
CosmicMappedRenderElement::Indicator(elem) => elem.current_commit(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.current_commit(),
}
@ -690,6 +694,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.src(),
CosmicMappedRenderElement::Window(elem) => elem.src(),
CosmicMappedRenderElement::Indicator(elem) => elem.src(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.src(),
}
@ -699,6 +704,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Window(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.geometry(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.geometry(scale),
}
@ -708,6 +714,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.location(scale),
CosmicMappedRenderElement::Window(elem) => elem.location(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.location(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.location(scale),
}
@ -717,6 +724,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.transform(),
CosmicMappedRenderElement::Window(elem) => elem.transform(),
CosmicMappedRenderElement::Indicator(elem) => elem.transform(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.transform(),
}
@ -730,6 +738,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Window(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Indicator(elem) => elem.damage_since(scale, commit),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.damage_since(scale, commit),
}
@ -739,6 +748,7 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Window(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.opaque_regions(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.opaque_regions(scale),
}
@ -756,6 +766,9 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
@ -767,6 +780,7 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => elem.underlying_storage(renderer),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.underlying_storage(renderer),
}
@ -786,6 +800,10 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| MultiError::Render(err))
}
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => {
let glow_frame = frame.glow_frame_mut();
@ -802,6 +820,9 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => {
let glow_renderer = renderer.glow_renderer_mut();
@ -836,6 +857,18 @@ where
CosmicMappedRenderElement::Window(elem)
}
}
impl<R> From<PixelShaderElement> for CosmicMappedRenderElement<R>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
fn from(elem: PixelShaderElement) -> Self {
CosmicMappedRenderElement::Indicator(elem)
}
}
#[cfg(feature = "debug")]
impl<R> From<TextureRenderElement<Gles2Texture>> for CosmicMappedRenderElement<R>
where

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::element::AsGlowRenderer,
backend::render::{element::AsGlowRenderer, IndicatorShader},
shell::{
element::{CosmicMapped, CosmicMappedRenderElement},
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
@ -56,13 +56,22 @@ impl MoveGrabState {
}
let scale = output.current_scale().fractional_scale().into();
AsRenderElements::<R>::render_elements::<I>(
let mut elements: Vec<I> = vec![CosmicMappedRenderElement::from(IndicatorShader::element(
renderer,
Rectangle::from_loc_and_size(
location.to_i32_round() - output.geometry().loc,
self.window.geometry().size,
),
))
.into()];
elements.extend(AsRenderElements::<R>::render_elements::<I>(
&self.window,
renderer,
(location.to_i32_round() - output.geometry().loc - self.window.geometry().loc)
.to_physical_precise_round(scale),
scale,
)
));
elements
}
pub fn send_frames(

View file

@ -1,7 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::{
backend::renderer::{element::RenderElement, ImportAll, ImportMem, Renderer},
backend::renderer::{
element::{AsRenderElements, RenderElement},
ImportAll, ImportMem, Renderer,
},
desktop::{layer_map_for_output, space::SpaceElement, Space},
input::{pointer::GrabStartData as PointerGrabStartData, Seat},
output::Output,
@ -10,11 +13,11 @@ use smithay::{
use std::collections::HashMap;
use crate::{
backend::render::element::AsGlowRenderer,
backend::render::{element::AsGlowRenderer, IndicatorShader},
shell::{
element::{CosmicMapped, CosmicMappedRenderElement},
grabs::ResizeEdge,
CosmicSurface, OutputNotMapped,
CosmicSurface,
},
state::State,
utils::prelude::*,
@ -342,16 +345,37 @@ impl FloatingLayout {
&self,
renderer: &mut R,
output: &Output,
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
focused: Option<&CosmicMapped>,
) -> Vec<CosmicMappedRenderElement<R>>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
{
let output_scale = output.current_scale().fractional_scale();
let output_geo = self.space.output_geometry(output).ok_or(OutputNotMapped)?;
Ok(self
.space
.render_elements_for_region(renderer, &output_geo, output_scale))
self.space
.elements_for_output(output)
.rev()
.flat_map(|elem| {
let render_location =
self.space.element_location(elem).unwrap() - elem.geometry().loc;
let mut elements = elem.render_elements(
renderer,
render_location.to_physical_precise_round(output_scale),
output_scale.into(),
);
if focused == Some(elem) {
let element = IndicatorShader::element(
renderer,
Rectangle::from_loc_and_size(
self.space.element_location(elem).unwrap(),
elem.geometry().size,
),
);
elements.insert(0, element.into());
}
elements
})
.collect()
}
}

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::element::AsGlowRenderer,
backend::render::{element::AsGlowRenderer, IndicatorShader},
shell::{
element::{CosmicMapped, CosmicMappedRenderElement},
focus::{
@ -1299,6 +1299,7 @@ impl TilingLayout {
&self,
renderer: &mut R,
output: &Output,
focused: Option<&CosmicMapped>,
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
@ -1360,16 +1361,25 @@ impl TilingLayout {
})
.flatten()
.flat_map(|(mapped, loc)| {
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
)
let mut elements =
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
);
if focused == Some(mapped) {
let element = IndicatorShader::element(
renderer,
Rectangle::from_loc_and_size(loc, mapped.geometry().size),
);
elements.insert(0, element.into());
}
elements
})
.collect::<Vec<_>>())
}

View file

@ -435,6 +435,7 @@ impl Workspace {
output: &Output,
override_redirect_windows: &[X11Surface],
xwm_state: impl Iterator<Item = &'a mut XWaylandState>,
draw_focus_indicator: Option<&Seat<State>>,
exclude_workspace_overview: bool,
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
where
@ -559,10 +560,12 @@ impl Workspace {
}),
);
let focused =
draw_focus_indicator.and_then(|seat| self.focus_stack.get(seat).last().cloned());
// floating surfaces
render_elements.extend(
self.floating_layer
.render_output::<R>(renderer, output)?
.render_output::<R>(renderer, output, focused.as_ref())
.into_iter()
.map(WorkspaceRenderElement::from),
);
@ -570,7 +573,7 @@ impl Workspace {
//tiling surfaces
render_elements.extend(
self.tiling_layer
.render_output::<R>(renderer, output)?
.render_output::<R>(renderer, output, focused.as_ref())?
.into_iter()
.map(WorkspaceRenderElement::from),
);