aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--resources/default-config.kdl16
-rw-r--r--src/config.rs71
-rw-r--r--src/layout.rs121
-rw-r--r--src/niri.rs3
4 files changed, 185 insertions, 26 deletions
diff --git a/resources/default-config.kdl b/resources/default-config.kdl
index 3e15b260..00e0bcaa 100644
--- a/resources/default-config.kdl
+++ b/resources/default-config.kdl
@@ -40,6 +40,22 @@ input {
// which may be more convenient to use.
// spawn-at-startup "alacritty" "-e" "fish"
+// You can change how the focus ring looks.
+// Remember to uncommend the node by removing "/-"!
+focus-ring {
+ // Uncomment this line to disable the focus ring.
+ // off
+
+ // How many logical pixels the ring extends out from the windows.
+ width 4
+
+ // Color of the ring on the active monitor: red, green, blue, alpha.
+ active-color 0.5 0.8 1.0 1.0
+
+ // Color of the ring on inactive monitors: red, green, blue, alpha.
+ inactive-color 0.3 0.3 0.3 1.0
+}
+
binds {
// Keys consist of modifiers separated by + signs, followed by an XKB key name
// in the end. To find an XKB name for a particular key, you may use a program
diff --git a/src/config.rs b/src/config.rs
index 3818ed68..aae77fde 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -17,6 +17,8 @@ pub struct Config {
#[knuffel(children(name = "spawn-at-startup"))]
pub spawn_at_startup: Vec<SpawnAtStartup>,
#[knuffel(child, default)]
+ pub focus_ring: FocusRing,
+ #[knuffel(child, default)]
pub binds: Binds,
#[knuffel(child, default)]
pub debug: DebugConfig,
@@ -90,6 +92,53 @@ pub struct SpawnAtStartup {
pub command: Vec<String>,
}
+#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
+pub struct FocusRing {
+ #[knuffel(child)]
+ pub off: bool,
+ #[knuffel(child, unwrap(argument), default = 4)]
+ pub width: u16,
+ #[knuffel(child, default = Color::new(0.5, 0.8, 1.0, 1.0))]
+ pub active_color: Color,
+ #[knuffel(child, default = Color::new(0.3, 0.3, 0.3, 1.0))]
+ pub inactive_color: Color,
+}
+
+impl Default for FocusRing {
+ fn default() -> Self {
+ Self {
+ off: false,
+ width: 4,
+ active_color: Color::new(0.5, 0.8, 1.0, 1.0),
+ inactive_color: Color::new(0.3, 0.3, 0.3, 1.0),
+ }
+ }
+}
+
+#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
+pub struct Color {
+ #[knuffel(argument)]
+ pub r: f32,
+ #[knuffel(argument)]
+ pub g: f32,
+ #[knuffel(argument)]
+ pub b: f32,
+ #[knuffel(argument)]
+ pub a: f32,
+}
+
+impl Color {
+ pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
+ Self { r, g, b, a }
+ }
+}
+
+impl From<Color> for [f32; 4] {
+ fn from(c: Color) -> Self {
+ [c.r, c.g, c.b, c.a]
+ }
+}
+
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq)]
pub struct Binds(#[knuffel(children)] pub Vec<Bind>);
@@ -295,6 +344,12 @@ mod tests {
spawn-at-startup "alacritty" "-e" "fish"
+ focus-ring {
+ width 5
+ active-color 0.0 0.25 0.5 1.0
+ inactive-color 1.0 0.5 0.25 0.0
+ }
+
binds {
Mod+T { spawn "alacritty"; }
Mod+Q { close-window; }
@@ -332,6 +387,22 @@ mod tests {
spawn_at_startup: vec![SpawnAtStartup {
command: vec!["alacritty".to_owned(), "-e".to_owned(), "fish".to_owned()],
}],
+ focus_ring: FocusRing {
+ off: false,
+ width: 5,
+ active_color: Color {
+ r: 0.,
+ g: 0.25,
+ b: 0.5,
+ a: 1.,
+ },
+ inactive_color: Color {
+ r: 1.,
+ g: 0.5,
+ b: 0.25,
+ a: 0.,
+ },
+ },
binds: Binds(vec![
Bind {
key: Key {
diff --git a/src/layout.rs b/src/layout.rs
index 76afaf0e..4a82396d 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -53,11 +53,9 @@ use smithay::wayland::dmabuf::DmabufFeedback;
use smithay::wayland::shell::xdg::SurfaceCachedState;
use crate::animation::Animation;
+use crate::config::{Color, Config};
const PADDING: i32 = 16;
-const FOCUS_RING_PADDING: i32 = 4;
-const FOCUS_RING_ACTIVE_COLOR: [f32; 4] = [0.5, 0.8, 1.0, 1.0];
-const FOCUS_RING_INACTIVE_COLOR: [f32; 4] = [0.3, 0.3, 0.3, 1.0];
const WIDTH_PROPORTIONS: [ColumnWidth; 3] = [
ColumnWidth::Proportion(1. / 3.),
ColumnWidth::Proportion(0.5),
@@ -151,8 +149,8 @@ pub struct Workspace<W: LayoutElement> {
/// Index of the currently active column, if any.
active_column_idx: usize,
- /// The solid color buffer for the focus ring.
- focus_ring: SolidColorBuffer,
+ /// Focus ring buffer and parameters.
+ focus_ring: FocusRing,
/// Offset of the view computed from the active column.
view_offset: i32,
@@ -171,6 +169,15 @@ pub struct Workspace<W: LayoutElement> {
activate_prev_column_on_removal: bool,
}
+#[derive(Debug)]
+struct FocusRing {
+ buffer: SolidColorBuffer,
+ is_off: bool,
+ width: i32,
+ active_color: Color,
+ inactive_color: Color,
+}
+
/// Width of a column.
#[derive(Debug, Clone, Copy)]
enum ColumnWidth {
@@ -267,6 +274,52 @@ impl LayoutElement for Window {
}
}
+impl FocusRing {
+ fn resize(&mut self, size: Size<i32, Logical>) {
+ let size = size + Size::from((self.width * 2, self.width * 2));
+ self.buffer.resize(size);
+ }
+
+ fn set_active(&mut self, is_active: bool) {
+ self.buffer.set_color(if is_active {
+ self.active_color.into()
+ } else {
+ self.inactive_color.into()
+ });
+ }
+
+ fn render(
+ &self,
+ loc: Point<i32, Logical>,
+ scale: Scale<f64>,
+ ) -> Option<SolidColorRenderElement> {
+ if self.is_off {
+ return None;
+ }
+
+ let offset = Point::from((self.width, self.width));
+ Some(SolidColorRenderElement::from_buffer(
+ &self.buffer,
+ (loc - offset).to_physical_precise_round(scale),
+ scale,
+ 1.,
+ Kind::Unspecified,
+ ))
+ }
+}
+
+impl Default for FocusRing {
+ fn default() -> Self {
+ Self {
+ buffer: SolidColorBuffer::new((0, 0), [0., 0., 0., 0.]),
+ is_off: true,
+ width: 0,
+ active_color: Color::default(),
+ inactive_color: Color::default(),
+ }
+ }
+}
+
impl ColumnWidth {
fn resolve(self, view_width: i32) -> i32 {
match self {
@@ -873,6 +926,21 @@ impl<W: LayoutElement> MonitorSet<W> {
}
}
+ pub fn update_config(&mut self, config: &Config) {
+ match self {
+ MonitorSet::Normal { monitors, .. } => {
+ for mon in monitors {
+ mon.update_config(config);
+ }
+ }
+ MonitorSet::NoOutputs(workspaces) => {
+ for ws in workspaces {
+ ws.update_config(config);
+ }
+ }
+ }
+ }
+
pub fn toggle_width(&mut self) {
let Some(monitor) = self.active_monitor() else {
return;
@@ -1235,6 +1303,12 @@ impl<W: LayoutElement> Monitor<W> {
}
}
+ pub fn update_config(&mut self, config: &Config) {
+ for ws in &mut self.workspaces {
+ ws.update_config(config);
+ }
+ }
+
fn toggle_width(&mut self) {
self.active_workspace().toggle_width();
}
@@ -1319,7 +1393,7 @@ impl<W: LayoutElement> Workspace<W> {
output: Some(output),
columns: vec![],
active_column_idx: 0,
- focus_ring: SolidColorBuffer::new((0, 0), FOCUS_RING_ACTIVE_COLOR),
+ focus_ring: FocusRing::default(),
view_offset: 0,
view_offset_anim: None,
activate_prev_column_on_removal: false,
@@ -1333,7 +1407,7 @@ impl<W: LayoutElement> Workspace<W> {
view_size: Size::from((1280, 720)),
columns: vec![],
active_column_idx: 0,
- focus_ring: SolidColorBuffer::new((0, 0), FOCUS_RING_ACTIVE_COLOR),
+ focus_ring: FocusRing::default(),
view_offset: 0,
view_offset_anim: None,
activate_prev_column_on_removal: false,
@@ -1357,17 +1431,20 @@ impl<W: LayoutElement> Workspace<W> {
let col = &self.columns[self.active_column_idx];
let active_win = &col.windows[col.active_window_idx];
let geom = active_win.geometry();
- self.focus_ring
- .resize(geom.size + Size::from((FOCUS_RING_PADDING * 2, FOCUS_RING_PADDING * 2)));
-
- self.focus_ring.set_color(if is_active {
- FOCUS_RING_ACTIVE_COLOR
- } else {
- FOCUS_RING_INACTIVE_COLOR
- });
+ self.focus_ring.resize(geom.size);
+ self.focus_ring.set_active(is_active);
}
}
+ pub fn update_config(&mut self, config: &Config) {
+ let c = &config.focus_ring;
+ self.focus_ring.is_off = c.off;
+ self.focus_ring.width = c.width.into();
+ self.focus_ring.active_color = c.active_color;
+ self.focus_ring.inactive_color = c.inactive_color;
+ // The focus ring buffer will be updated in a subsequent update_animations call.
+ }
+
fn windows(&self) -> impl Iterator<Item = &W> + '_ {
self.columns.iter().flat_map(|col| col.windows.iter())
}
@@ -1917,16 +1994,10 @@ impl Workspace<Window> {
));
// Draw the focus ring.
- rv.push(
- SolidColorRenderElement::from_buffer(
- &self.focus_ring,
- (win_pos + geom.loc - Point::from((FOCUS_RING_PADDING, FOCUS_RING_PADDING)))
- .to_physical_precise_round(output_scale),
- output_scale,
- 1.,
- Kind::Unspecified,
- )
- .into(),
+ rv.extend(
+ self.focus_ring
+ .render(win_pos + geom.loc, output_scale)
+ .map(Into::into),
);
let mut x = PADDING;
diff --git a/src/niri.rs b/src/niri.rs
index 777554fe..58952a35 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -959,7 +959,8 @@ impl Niri {
assert!(state.queued_redraw.take().is_some());
assert!(!state.waiting_for_vblank);
- // Advance the animations.
+ // Update from the config and advance the animations.
+ self.monitor_set.update_config(&self.config.borrow());
self.monitor_set.advance_animations(presentation_time);
// Render the elements.