summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bar.rs160
-rw-r--r--src/config.rs2
-rw-r--r--src/cursor.rs109
-rw-r--r--src/main.rs43
-rw-r--r--src/protocol.rs8
-rw-r--r--src/river_protocols.rs4
-rw-r--r--src/river_protocols/control.rs112
-rw-r--r--src/river_protocols/status.rs144
-rw-r--r--src/shared_state.rs14
-rw-r--r--src/state.rs713
-rw-r--r--src/status_cmd.rs86
11 files changed, 630 insertions, 765 deletions
diff --git a/src/bar.rs b/src/bar.rs
index 90215a2..9f55710 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -1,34 +1,34 @@
use std::collections::BinaryHeap;
+use std::ffi::CString;
use pangocairo::cairo;
-use smithay_client_toolkit::{
- reexports::client::protocol::{wl_output, wl_seat, wl_shm},
- shell::layer::LayerSurface,
-};
-use crate::{
- button_manager::ButtonManager,
- config::Config,
- i3bar_protocol::{self, Block, MinWidth},
- ord_adaptor::DefaultLess,
- pointer_btn::PointerBtn,
- river_protocols::{control::RiverControlState, status::RiverOutputStatus},
- shared_state::SharedState,
- state::ComputedBlock,
- tags::{compute_tag_label, TagState, TagsInfo},
- text::{self, ComputedText, RenderOptions},
-};
+use wayrs_client::connection::Connection;
+
+use crate::button_manager::ButtonManager;
+use crate::config::Config;
+use crate::i3bar_protocol::{self, Block, MinWidth};
+use crate::ord_adaptor::DefaultLess;
+use crate::pointer_btn::PointerBtn;
+use crate::protocol::*;
+use crate::shared_state::SharedState;
+use crate::state::{ComputedBlock, State};
+use crate::tags::{compute_tag_label, TagState, TagsInfo};
+use crate::text::{self, ComputedText, RenderOptions};
pub struct Bar {
- pub output: wl_output::WlOutput,
+ pub output: WlOutput,
+ pub output_reg_name: u32,
pub configured: bool,
+ pub frame_cb: Option<WlCallback>,
pub width: u32,
pub height: u32,
pub scale: i32,
- pub layer: LayerSurface,
+ pub surface: WlSurface,
+ pub layer_surface: ZwlrLayerSurfaceV1,
pub blocks_btns: ButtonManager<(Option<String>, Option<String>)>,
- pub river_output_status: Option<RiverOutputStatus>,
- pub river_control: Option<RiverControlState>,
+ pub river_output_status: Option<ZriverOutputStatusV1>,
+ pub river_control: Option<ZriverControlV1>,
pub layout_name: Option<String>,
pub layout_name_computed: Option<ComputedText>,
pub tags_btns: ButtonManager,
@@ -37,16 +37,6 @@ pub struct Bar {
}
impl Bar {
- pub fn configure(&mut self, ss: &mut SharedState, width: u32) {
- if self.width != width && width != 0 {
- self.width = width;
- self.layer.set_exclusive_zone(self.height as i32);
- }
-
- self.configured = true;
- self.draw(ss);
- }
-
pub fn has_tags_provider(&self) -> bool {
// TODO: add more tags providers
self.river_output_status.is_some()
@@ -59,20 +49,23 @@ impl Bar {
pub fn click(
&mut self,
+ conn: &mut Connection<State>,
ss: &mut SharedState,
button: PointerBtn,
- seat: &wl_seat::WlSeat,
+ seat: WlSeat,
x: f64,
_y: f64,
) -> anyhow::Result<()> {
if let Some(tag) = self.tags_btns.click(x) {
if let Some(river_control) = &self.river_control {
let cmd = match button {
- PointerBtn::Left => "set-focused-tags",
- PointerBtn::Right => "toggle-focused-tags",
+ PointerBtn::Left => wayrs_client::cstr!("set-focused-tags"),
+ PointerBtn::Right => wayrs_client::cstr!("toggle-focused-tags"),
_ => return Ok(()),
};
- river_control.run_command(&ss.qh, seat, [cmd.into(), (1u32 << tag).to_string()]);
+ river_control.add_argument(conn, cmd.into());
+ river_control.add_argument(conn, CString::new((1u32 << tag).to_string()).unwrap());
+ river_control.run_command(conn, seat);
}
} else if let Some((name, instance)) = self.blocks_btns.click(x) {
if let Some(cmd) = &mut ss.status_cmd {
@@ -87,10 +80,8 @@ impl Bar {
Ok(())
}
- pub fn draw(&mut self, ss: &mut SharedState) {
- if !self.configured {
- return;
- }
+ pub fn frame(&mut self, conn: &mut Connection<State>, ss: &mut SharedState) {
+ assert!(self.configured);
let stride = 4 * self.width as i32;
let width = self.width as i32;
@@ -98,15 +89,13 @@ impl Bar {
let width_f = width as f64;
let height_f = height as f64;
- let (buffer, canvas) = ss
- .pool
- .create_buffer(
- width * self.scale,
- height * self.scale,
- stride * self.scale,
- wl_shm::Format::Argb8888,
- )
- .expect("create buffer");
+ let (buffer, canvas) = ss.shm.alloc_buffer(
+ conn,
+ width * self.scale,
+ height * self.scale,
+ stride * self.scale,
+ wl_shm::Format::Argb8888,
+ );
let cairo_surf = unsafe {
cairo::ImageSurface::create_for_data_unsafe(
@@ -121,7 +110,7 @@ impl Bar {
let cairo_ctx = cairo::Context::new(&cairo_surf).expect("cairo context");
cairo_ctx.scale(self.scale as f64, self.scale as f64);
- self.layer.wl_surface().set_buffer_scale(self.scale);
+ self.surface.set_buffer_scale(conn, self.scale);
// Background
cairo_ctx.save().unwrap();
@@ -181,34 +170,36 @@ impl Bar {
}
// Display layout name
- if let Some(layout_name) = &self.layout_name {
- let text = self.layout_name_computed.get_or_insert_with(|| {
- ComputedText::new(
- layout_name,
- text::Attributes {
- font: &ss.config.font,
- padding_left: 25.0,
- padding_right: 25.0,
- min_width: None,
- align: Default::default(),
- markup: false,
- },
+ if ss.config.show_layout_name {
+ if let Some(layout_name) = &self.layout_name {
+ let text = self.layout_name_computed.get_or_insert_with(|| {
+ ComputedText::new(
+ layout_name,
+ text::Attributes {
+ font: &ss.config.font,
+ padding_left: 25.0,
+ padding_right: 25.0,
+ min_width: None,
+ align: Default::default(),
+ markup: false,
+ },
+ &cairo_ctx,
+ )
+ });
+ text.render(
&cairo_ctx,
- )
- });
- text.render(
- &cairo_ctx,
- RenderOptions {
- x_offset: offset_left,
- bar_height: height_f,
- fg_color: ss.config.tag_inactive_fg,
- bg_color: None,
- r_left: 0.0,
- r_right: 0.0,
- overlap: 0.0,
- },
- );
- offset_left += text.width;
+ RenderOptions {
+ x_offset: offset_left,
+ bar_height: height_f,
+ fg_color: ss.config.tag_inactive_fg,
+ bg_color: None,
+ r_left: 0.0,
+ r_right: 0.0,
+ overlap: 0.0,
+ },
+ );
+ offset_left += text.width;
+ }
}
// Display the blocks
@@ -223,16 +214,17 @@ impl Bar {
height_f,
);
- // Attach the buffer to the surface and mark the entire surface as damaged
- buffer
- .attach_to(self.layer.wl_surface())
- .expect("attaching buffer");
- self.layer
- .wl_surface()
- .damage_buffer(0, 0, width * self.scale, height * self.scale);
+ self.surface.attach(conn, buffer, 0, 0);
+ self.surface
+ .damage_buffer(conn, 0, 0, width * self.scale, height * self.scale);
+ self.surface.commit(conn);
+ }
- // Finally, commit the surface
- self.layer.wl_surface().commit();
+ pub fn request_frame(&mut self, conn: &mut Connection<State>) {
+ if self.configured && self.frame_cb.is_none() {
+ self.frame_cb = Some(self.surface.frame(conn));
+ self.surface.commit(conn);
+ }
}
}
diff --git a/src/config.rs b/src/config.rs
index 2af9af0..a5b05d0 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -34,6 +34,7 @@ pub struct Config {
// misc
pub hide_inactive_tags: bool,
pub invert_touchpad_scrolling: bool,
+ pub show_layout_name: bool,
}
impl Default for Config {
@@ -60,6 +61,7 @@ impl Default for Config {
command: None,
hide_inactive_tags: true,
invert_touchpad_scrolling: true,
+ show_layout_name: true,
}
}
}
diff --git a/src/cursor.rs b/src/cursor.rs
new file mode 100644
index 0000000..57db8bb
--- /dev/null
+++ b/src/cursor.rs
@@ -0,0 +1,109 @@
+use std::io;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::protocol::wl_compositor::WlCompositor;
+use wayrs_client::protocol::wl_pointer::WlPointer;
+use wayrs_client::protocol::wl_shm::Format;
+use wayrs_client::protocol::wl_surface::WlSurface;
+
+use wayrs_shm_alloc::ShmAlloc;
+
+use xcursor::parser::Image;
+
+use crate::state::State;
+
+#[derive(Debug, thiserror::Error)]
+pub enum CursorError {
+ #[error("theme not found")]
+ ThemeNotFound,
+ #[error("theme could not be parsed")]
+ ThemeParseError,
+ #[error(transparent)]
+ ReadError(#[from] io::Error),
+}
+
+pub struct Cursor {
+ pub surface: WlSurface,
+ pub images: Vec<Image>,
+}
+
+impl Cursor {
+ pub fn new(
+ conn: &mut Connection<State>,
+ compositor: WlCompositor,
+ ) -> Result<Self, CursorError> {
+ let theme_name = std::env::var("XCURSOR_THEME").ok();
+ let theme_name = theme_name.as_deref().unwrap_or("default");
+
+ let theme_path = xcursor::CursorTheme::load(theme_name)
+ .load_icon("default")
+ .ok_or(CursorError::ThemeNotFound)?;
+ let raw_theme = std::fs::read(theme_path)?;
+
+ let mut images =
+ xcursor::parser::parse_xcursor(&raw_theme).ok_or(CursorError::ThemeParseError)?;
+ images.sort_unstable_by_key(|img| img.size);
+
+ let surface = compositor.create_surface(conn);
+
+ Ok(Cursor { surface, images })
+ }
+
+ pub fn set(
+ &self,
+ conn: &mut Connection<State>,
+ serial: u32,
+ pointer: WlPointer,
+ scale: u32,
+ shm: &mut ShmAlloc,
+ ) {
+ let target_size = std::env::var("XCURSOR_SIZE")
+ .ok()
+ .and_then(|x| x.parse().ok())
+ .unwrap_or(24)
+ * scale;
+
+ let image = match self
+ .images
+ .binary_search_by_key(&target_size, |img| img.size)
+ {
+ Ok(indx) => &self.images[indx],
+ Err(indx) if indx == 0 => self.images.first().unwrap(),
+ Err(indx) if indx >= self.images.len() => self.images.last().unwrap(),
+ Err(indx) => {
+ let a = &self.images[indx - 1];
+ let b = &self.images[indx];
+ if target_size - a.size < b.size - target_size {
+ a
+ } else {
+ b
+ }
+ }
+ };
+
+ let (buffer, canvas) = shm.alloc_buffer(
+ conn,
+ image.width as i32,
+ image.height as i32,
+ (image.width * 4) as i32,
+ Format::Argb8888,
+ );
+
+ assert_eq!(image.pixels_argb.len(), canvas.len(),);
+ canvas.copy_from_slice(&image.pixels_rgba);
+
+ self.surface.attach(conn, buffer, 0, 0);
+ self.surface
+ .damage_buffer(conn, 0, 0, image.width as i32, image.height as i32);
+ self.surface.set_buffer_scale(conn, scale as i32);
+ self.surface.commit(conn);
+
+ pointer.set_cursor(
+ conn,
+ serial,
+ self.surface,
+ (image.xhot / scale) as i32,
+ (image.yhot / scale) as i32,
+ );
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 3dad693..73c2d6c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::single_component_path_imports)]
+
#[macro_use]
extern crate anyhow;
#[macro_use]
@@ -7,10 +9,11 @@ mod bar;
mod button_manager;
mod color;
mod config;
+mod cursor;
mod i3bar_protocol;
mod ord_adaptor;
mod pointer_btn;
-mod river_protocols;
+mod protocol;
mod shared_state;
mod state;
mod status_cmd;
@@ -18,45 +21,35 @@ mod tags;
mod text;
mod utils;
-use std::os::unix::io::AsRawFd;
-
-use smithay_client_toolkit::reexports::client::Connection;
-use tokio::io::{unix::AsyncFd, Interest};
+use wayrs_client::connection::Connection;
use state::State;
-use wayland_client::globals::registry_queue_init;
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
env_logger::init();
- let conn = Connection::connect_to_env()?;
- let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
- let mut state = State::new(&mut event_queue, &globals);
-
- let async_fd = AsyncFd::with_interest(
- event_queue.prepare_read()?.connection_fd().as_raw_fd(),
- Interest::READABLE,
- )?;
+ let mut conn = Connection::connect()?;
+ let globals = conn.async_collect_initial_globals().await?;
+ let mut state = State::new(&mut conn, &globals);
+ conn.async_flush().await?;
loop {
tokio::select! {
- readable = async_fd.readable() => {
- readable?.clear_ready();
- event_queue.prepare_read()?.read()?;
- event_queue.dispatch_pending(&mut state)?;
- event_queue.flush()?;
+ recv_events = conn.async_recv_events() => {
+ recv_events?;
+ conn.dispatch_events(&mut state)?;
+ conn.async_flush().await?;
}
- readable = state.wait_for_status_cmd() => {
- readable?.clear_ready();
- if let Err(e) = state.notify_available() {
+ reat_res = state.status_cmd_read() => {
+ reat_res?;
+ if let Err(e) = state.status_cmd_notify_available(&mut conn) {
if let Some(mut status_cmd) = state.shared_state.status_cmd.take() {
let _ = status_cmd.child.kill();
}
- state.set_error(e.to_string());
- state.draw_all();
+ state.set_error(&mut conn, e.to_string());
}
- event_queue.flush()?;
+ conn.async_flush().await?;
}
}
}
diff --git a/src/protocol.rs b/src/protocol.rs
new file mode 100644
index 0000000..ac8b2c1
--- /dev/null
+++ b/src/protocol.rs
@@ -0,0 +1,8 @@
+#![allow(clippy::enum_variant_names)]
+
+use wayrs_client;
+pub use wayrs_client::protocol::*;
+wayrs_scanner::generate!("protocols/xdg-shell.xml");
+wayrs_scanner::generate!("protocols/wlr-layer-shell-unstable-v1.xml");
+wayrs_scanner::generate!("protocols/river-status-unstable-v1.xml");
+wayrs_scanner::generate!("protocols/river-control-unstable-v1.xml");
diff --git a/src/river_protocols.rs b/src/river_protocols.rs
deleted file mode 100644
index 0a2cf91..0000000
--- a/src/river_protocols.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![allow(clippy::enum_variant_names)]
-
-pub mod control;
-pub mod status;
diff --git a/src/river_protocols/control.rs b/src/river_protocols/control.rs
deleted file mode 100644
index 7cce23d..0000000
--- a/src/river_protocols/control.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use wayland_client::globals::{BindError, GlobalList};
-use wayland_client::{protocol::wl_seat, Connection, Dispatch, QueueHandle};
-
-use protocol::{zriver_command_callback_v1, zriver_control_v1};
-
-#[derive(Debug, Clone)]
-pub struct RiverControlState {
- control: zriver_control_v1::ZriverControlV1,
-}
-
-impl RiverControlState {
- pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Result<Self, BindError>
- where
- D: Dispatch<zriver_control_v1::ZriverControlV1, (), D> + 'static,
- {
- let control = globals.bind(qh, 1..=1, ())?;
- Ok(Self { control })
- }
-
- pub fn run_command<D, Args, Arg>(&self, qh: &QueueHandle<D>, seat: &wl_seat::WlSeat, args: Args)
- where
- D: Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData>
- + 'static,
- Args: IntoIterator<Item = Arg>,
- Arg: Into<String>,
- {
- for arg in args {
- self.control.add_argument(arg.into());
- }
- self.control
- .run_command(seat, qh, RiverCommandCallbackData {});
- }
-}
-
-pub trait RiverControlHandler: Sized {
- fn river_control_state(&mut self) -> &mut RiverControlState;
-
- fn command_failure(&mut self, conn: &Connection, qh: &QueueHandle<Self>, message: String);
-
- fn command_success(&mut self, conn: &Connection, qh: &QueueHandle<Self>, message: String);
-}
-
-#[derive(Debug)]
-pub struct RiverCommandCallbackData {
- // This is empty right now, but may be populated in the future.
-}
-
-#[macro_export]
-macro_rules! delegate_river_control {
- ($ty: ty) => {
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::control::protocol::zriver_control_v1::ZriverControlV1: ()
- ] => $crate::river_protocols::control::RiverControlState);
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::control::protocol::zriver_command_callback_v1::ZriverCommandCallbackV1: $crate::river_protocols::control::RiverCommandCallbackData
- ] => $crate::river_protocols::control::RiverControlState);
- };
-}
-
-impl<D> Dispatch<zriver_control_v1::ZriverControlV1, (), D> for RiverControlState
-where
- D: Dispatch<zriver_control_v1::ZriverControlV1, ()> + RiverControlHandler + 'static,
-{
- fn event(
- _: &mut D,
- _: &zriver_control_v1::ZriverControlV1,
- _: zriver_control_v1::Event,
- _: &(),
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- unreachable!("zriver_control_v1 has no events")
- }
-}
-
-impl<D> Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData, D>
- for RiverControlState
-where
- D: Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData>
- + RiverControlHandler
- + 'static,
-{
- fn event(
- data: &mut D,
- _callback: &zriver_command_callback_v1::ZriverCommandCallbackV1,
- event: zriver_command_callback_v1::Event,
- _udata: &RiverCommandCallbackData,
- conn: &Connection,
- qh: &QueueHandle<D>,
- ) {
- use zriver_command_callback_v1::Event;
- match event {
- Event::Success { output } => data.command_success(conn, qh, output),
- Event::Failure { failure_message } => data.command_success(conn, qh, failure_message),
- }
- }
-}
-
-pub mod protocol {
- #![allow(non_upper_case_globals)]
-
- use smithay_client_toolkit::reexports::client as wayland_client;
- use smithay_client_toolkit::reexports::client::protocol::*;
-
- pub mod __interfaces {
- use smithay_client_toolkit::reexports::client::protocol::__interfaces::*;
- wayland_scanner::generate_interfaces!("protocols/river-control-unstable-v1.xml");
- }
- use self::__interfaces::*;
-
- wayland_scanner::generate_client_code!("protocols/river-control-unstable-v1.xml");
-}
diff --git a/src/river_protocols/status.rs b/src/river_protocols/status.rs
deleted file mode 100644
index ad93279..0000000
--- a/src/river_protocols/status.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use wayland_client::{
- globals::{BindError, GlobalList},
- protocol::wl_output,
- Connection, Dispatch, QueueHandle,
-};
-
-use protocol::{zriver_output_status_v1, zriver_status_manager_v1};
-
-#[derive(Debug, Clone)]
-pub struct RiverStatusState {
- status_manager: zriver_status_manager_v1::ZriverStatusManagerV1,
-}
-
-impl RiverStatusState {
- pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Result<Self, BindError>
- where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, (), D> + 'static,
- {
- let status_manager = globals.bind(qh, 1..=4, ())?;
- Ok(Self { status_manager })
- }
-
- #[must_use = "The output status is destroyed if dropped"]
- pub fn new_output_status<D>(
- &mut self,
- qh: &QueueHandle<D>,
- output: &wl_output::WlOutput,
- ) -> RiverOutputStatus
- where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, ()>
- + Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData>
- + 'static,
- {
- RiverOutputStatus(self.status_manager.get_river_output_status(
- output,
- qh,
- RiverStatusData {
- output: output.clone(),
- },
- ))
- }
-}
-
-pub trait RiverStatusHandler: Sized {
- fn river_status_state(&mut self) -> &mut RiverStatusState;
-
- fn focused_tags_updated(&mut self, output: &wl_output::WlOutput, focused: u32);
-
- fn urgent_tags_updated(&mut self, output: &wl_output::WlOutput, urgent: u32);
-
- fn views_tags_updated(&mut self, output: &wl_output::WlOutput, tags: Vec<u32>);
-
- fn layout_name_updated(&mut self, output: &wl_output::WlOutput, layout_name: Option<String>);
-}
-
-#[derive(Debug)]
-pub struct RiverOutputStatus(zriver_output_status_v1::ZriverOutputStatusV1);
-
-impl Drop for RiverOutputStatus {
- fn drop(&mut self) {
- self.0.destroy();
- }
-}
-
-#[macro_export]
-macro_rules! delegate_river_status {
- ($ty: ty) => {
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::status::protocol::zriver_status_manager_v1::ZriverStatusManagerV1: ()
- ] => $crate::river_protocols::status::RiverStatusState);
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::status::protocol::zriver_output_status_v1::ZriverOutputStatusV1: $crate::river_protocols::status::RiverStatusData
- ] => $crate::river_protocols::status::RiverStatusState);
- };
-}
-
-#[derive(Debug)]
-pub struct RiverStatusData {
- output: wl_output::WlOutput,
-}
-
-impl<D> Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, (), D> for RiverStatusState
-where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, ()> + RiverStatusHandler + 'static,
-{
- fn event(
- _: &mut D,
- _: &zriver_status_manager_v1::ZriverStatusManagerV1,
- _: zriver_status_manager_v1::Event,
- _: &(),
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- unreachable!("zriver_status_manager_v1 has no events")
- }
-}
-
-impl<D> Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData, D>
- for RiverStatusState
-where
- D: Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData>
- + RiverStatusHandler
- + 'static,
-{
- fn event(
- data: &mut D,
- _: &zriver_output_status_v1::ZriverOutputStatusV1,
- event: zriver_output_status_v1::Event,
- udata: &RiverStatusData,
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- use zriver_output_status_v1::Event;
-
- match event {
- Event::FocusedTags { tags } => data.focused_tags_updated(&udata.output, tags),
- Event::UrgentTags { tags } => data.urgent_tags_updated(&udata.output, tags),
- Event::ViewTags { tags } => {
- let tags = tags
- .chunks_exact(4)
- .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap()))
- .collect();
- data.views_tags_updated(&udata.output, tags);
- }
- Event::LayoutName { name } => data.layout_name_updated(&udata.output, Some(name)),
- Event::LayoutNameClear => data.layout_name_updated(&udata.output, None),
- }
- }
-}
-
-pub mod protocol {
- #![allow(non_upper_case_globals)]
-
- use smithay_client_toolkit::reexports::client as wayland_client;
- use smithay_client_toolkit::reexports::client::protocol::*;
-
- pub mod __interfaces {
- use smithay_client_toolkit::reexports::client::protocol::__interfaces::*;
- wayland_scanner::generate_interfaces!("protocols/river-status-unstable-v1.xml");
- }
- use self::__interfaces::*;
-
- wayland_scanner::generate_client_code!("protocols/river-status-unstable-v1.xml");
-}
diff --git a/src/shared_state.rs b/src/shared_state.rs
index c4ec9d6..dd18606 100644
--- a/src/shared_state.rs
+++ b/src/shared_state.rs
@@ -1,16 +1,8 @@
-use smithay_client_toolkit::{reexports::client::QueueHandle, shm::slot::SlotPool};
+use crate::{config::Config, i3bar_protocol::Block, state::ComputedBlock, status_cmd::StatusCmd};
+use wayrs_shm_alloc::ShmAlloc;
-use crate::{
- config::Config,
- i3bar_protocol::Block,
- state::{ComputedBlock, State},
- status_cmd::StatusCmd,
-};
-
-#[derive(Debug)]
pub struct SharedState {
- pub qh: QueueHandle<State>,
- pub pool: SlotPool,
+ pub shm: ShmAlloc,
pub config: Config,
pub status_cmd: Option<StatusCmd>,
pub blocks: Vec<Block>,
diff --git a/src/state.rs b/src/state.rs
index e2e57ee..dbe73fd 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,103 +1,76 @@
-use std::{future::pending, io};
+use crate::cursor::Cursor;
+use crate::protocol::*;
-use smithay_client_toolkit::{
- compositor::{CompositorHandler, CompositorState},
- delegate_compositor, delegate_layer, delegate_output, delegate_pointer, delegate_registry,
- delegate_seat, delegate_shm,
- output::{OutputHandler, OutputState},
- reexports::client::{
- protocol::{wl_output, wl_pointer, wl_seat, wl_surface},
- Connection, EventQueue, QueueHandle,
- },
- registry::{ProvidesRegistryState, RegistryState},
- registry_handlers,
- seat::{
- pointer::{PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, ThemedPointer},
- Capability, SeatHandler, SeatState,
- },
- shell::layer::{
- Anchor, Layer, LayerShell, LayerShellHandler, LayerSurface, LayerSurfaceConfigure,
- },
- shm::{slot::SlotPool, ShmHandler, ShmState},
-};
-use tokio::io::unix::AsyncFdReadyGuard;
-use wayland_client::globals::GlobalList;
+use std::convert::Infallible;
+use std::future::pending;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::global::{Global, GlobalExt, Globals, GlobalsExt};
+use wayrs_client::proxy::{Dispatch, Dispatcher, Proxy};
+use wayrs_shm_alloc::ShmAlloc;
use crate::{
- bar::Bar,
- config::Config,
- delegate_river_control, delegate_river_status,
- i3bar_protocol::Block,
- pointer_btn::PointerBtn,
- river_protocols::{
- control::{RiverControlHandler, RiverControlState},
- status::{RiverStatusHandler, RiverStatusState},
- },
- shared_state::SharedState,
- status_cmd::StatusCmd,
- text::ComputedText,
+ bar::Bar, config::Config, i3bar_protocol::Block, pointer_btn::PointerBtn,
+ shared_state::SharedState, status_cmd::StatusCmd, text::ComputedText,
};
pub struct State {
- registry_state: RegistryState,
- shm_state: ShmState,
- seat_state: SeatState,
- output_state: OutputState,
- compositor_state: CompositorState,
- layer_state: LayerShell,
- river_status_state: Option<RiverStatusState>,
- river_control_state: Option<RiverControlState>,
+ wl_compositor: WlCompositor,
+ layer_shell: ZwlrLayerShellV1,
+
+ river_status_manager: Option<ZriverStatusManagerV1>,
+ river_control: Option<ZriverControlV1>,
seats: Vec<Seat>,
bars: Vec<Bar>,
+
pub shared_state: SharedState,
}
struct Seat {
- seat: wl_seat::WlSeat,
- pointer: wl_pointer::WlPointer,
- themed_pointer: ThemedPointer,
- pointer_surface: wl_surface::WlSurface,
- finger_scroll: f64,
+ seat: WlSeat,
+ reg_name: u32,
+ pointer: Option<WlPointer>,
+ cursor: Cursor,
+ cur_surface: Option<WlSurface>,
+ x: f64,
+ y: f64,
+ pending_button: Option<PointerBtn>,
+ pending_scroll: f64,
+ scroll_frame: ScrollFrame,
}
impl State {
- pub fn new(event_queue: &mut EventQueue<Self>, globals: &GlobalList) -> Self {
+ pub fn new(conn: &mut Connection<Self>, globals: &Globals) -> Self {
let mut error = Ok(());
- let qh = event_queue.handle();
-
let config = Config::new()
.map_err(|e| error = Err(e))
.unwrap_or_default();
- let status_cmd = match &error {
- Err(_) => None,
- Ok(()) => config.command.as_ref().and_then(|cmd| {
- StatusCmd::new(cmd)
- .map_err(|e| error = Err(anyhow!(e)))
- .ok()
- }),
- };
+ let status_cmd = config
+ .command
+ .as_ref()
+ .and_then(|cmd| StatusCmd::new(cmd).map_err(|e| error = Err(e)).ok());
- let shm_state = ShmState::bind(globals, &qh).unwrap();
- let pool = SlotPool::new(1024, &shm_state).expect("Failed to create pool");
+ let shm = globals.bind(conn, 1..=1).expect("could not bind wl_shm");
let mut this = Self {
- registry_state: RegistryState::new(globals),
- shm_state,
- seat_state: SeatState::new(globals, &qh),
- output_state: OutputState::new(globals, &qh),
- compositor_state: CompositorState::bind(globals, &qh).unwrap(),
- layer_state: LayerShell::bind(globals, &qh).unwrap(),
- river_status_state: RiverStatusState::new(globals, &qh).ok(),
- river_control_state: RiverControlState::new(globals, &qh).ok(),
+ wl_compositor: globals
+ .bind(conn, 4..=5)
+ .expect("could not bind wl_compositor"),
+ layer_shell: globals
+ .bind(conn, 1..=4)
+ .expect("could not bind layer_shell"),
+
+ river_status_manager: globals.bind(conn, 1..=4).ok(),
+ river_control: globals.bind(conn, 1..=1).ok(),
seats: Vec::new(),
bars: Vec::new(),
+
shared_state: SharedState {
- qh: event_queue.handle(),
- pool,
+ shm: ShmAlloc::new(conn, shm, 1024),
config,
status_cmd,
blocks: Vec::new(),
@@ -105,113 +78,102 @@ impl State {
},
};
+ globals
+ .iter()
+ .filter(|g| g.is::<WlSeat>())
+ .for_each(|g| this.bind_seat(conn, g));
+ globals
+ .iter()
+ .filter(|g| g.is::<WlOutput>())
+ .for_each(|g| this.bind_output(conn, g));
+
if let Err(e) = error {
- this.set_error(e.to_string());
+ this.set_error(conn, e.to_string());
}
this
}
- pub fn set_blocks(&mut self, blocks: Vec<Block>) {
+ pub fn set_blocks(&mut self, conn: &mut Connection<Self>, blocks: Vec<Block>) {
self.shared_state.blocks = blocks;
- self.draw_all();
+ self.draw_all(conn);
}
- pub fn set_error(&mut self, error: impl Into<String>) {
- self.set_blocks(vec![Block {
- full_text: error.into(),
- ..Default::default()
- }]);
+ pub fn set_error(&mut self, conn: &mut Connection<Self>, error: impl Into<String>) {
+ self.set_blocks(
+ conn,
+ vec![Block {
+ full_text: error.into(),
+ ..Default::default()
+ }],
+ );
}
- pub fn notify_available(&mut self) -> anyhow::Result<()> {
- if let Some(cmd) = &mut self.shared_state.status_cmd {
- if let Some(blocks) = cmd.notify_available()? {
- self.set_blocks(blocks);
- }
- }
- Ok(())
- }
-
- pub fn draw_all(&mut self) {
+ pub fn draw_all(&mut self, conn: &mut Connection<Self>) {
for bar in &mut self.bars {
- bar.draw(&mut self.shared_state);
+ bar.request_frame(conn);
}
}
- pub async fn wait_for_status_cmd(&self) -> io::Result<AsyncFdReadyGuard<i32>> {
- match &self.shared_state.status_cmd {
- Some(cmd) => cmd.async_fd.readable().await,
+ pub async fn status_cmd_read(&mut self) -> anyhow::Result<()> {
+ match &mut self.shared_state.status_cmd {
+ Some(cmd) => cmd.read().await,
None => {
pending::<()>().await;
unreachable!()
}
}
}
-}
-impl CompositorHandler for State {
- fn scale_factor_changed(
+ pub fn status_cmd_notify_available(
&mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- surface: &wl_surface::WlSurface,
- scale: i32,
- ) {
- if let Some(bar) = self
- .bars
- .iter_mut()
- .find(|bar| bar.layer.wl_surface() == surface)
- {
- bar.scale = scale;
- bar.draw(&mut self.shared_state);
+ conn: &mut Connection<Self>,
+ ) -> anyhow::Result<()> {
+ if let Some(cmd) = &mut self.shared_state.status_cmd {
+ if let Some(blocks) = cmd.notify_available()? {
+ self.set_blocks(conn, blocks);
+ }
}
+ Ok(())
}
- fn frame(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _surface: &wl_surface::WlSurface,
- _time: u32,
- ) {
- }
-}
+ fn bind_output(&mut self, conn: &mut Connection<Self>, global: &Global) {
+ let output = global.bind(conn, 2..=4).expect("could not bind wl_output");
+ let surface = self.wl_compositor.create_surface(conn);
-impl OutputHandler for State {
- fn output_state(&mut self) -> &mut OutputState {
- &mut self.output_state
- }
+ use zwlr_layer_shell_v1::Layer;
+ use zwlr_layer_surface_v1::Anchor;
+ let layer_surface = self.layer_shell.get_layer_surface(
+ conn,
+ surface,
+ output,
+ Layer::Top,
+ wayrs_client::cstr!("i3bar-river").into(),
+ );
+ layer_surface.set_size(conn, 0, self.shared_state.config.height);
+ layer_surface.set_anchor(conn, Anchor::Top | Anchor::Left | Anchor::Right); // Top + Left + Right
+ layer_surface.set_exclusive_zone(conn, self.shared_state.config.height as i32 + 5); // TODO: make the margin configurable
+
+ // Note: layer_surface is commited when we receive the scale factor of this output
- fn new_output(
- &mut self,
- _conn: &Connection,
- qh: &QueueHandle<Self>,
- output: wl_output::WlOutput,
- ) {
- let height = self.shared_state.config.height;
- let surface = self.compositor_state.create_surface(qh);
- let layer = LayerSurface::builder()
- .output(&output)
- .size((0, height))
- .anchor(Anchor::TOP | Anchor::LEFT | Anchor::RIGHT)
- .namespace("i3bar-river")
- .map(qh, &self.layer_state, surface, Layer::Top)
- .expect("layer surface creation");
let river_output_status = self
- .river_status_state
- .as_mut()
- .map(|s| s.new_output_status(qh, &output));
+ .river_status_manager
+ .as_ref()
+ .map(|s| s.get_river_output_status(conn, output));
+
self.bars.push(Bar {
output,
+ output_reg_name: global.name,
configured: false,
+ frame_cb: None,
width: 0,
- height,
+ height: self.shared_state.config.height,
scale: 1,
- layer,
+ surface,
+ layer_surface,
blocks_btns: Default::default(),
river_output_status,
- river_control: self.river_control_state.clone(),
+ river_control: self.river_control,
layout_name: None,
layout_name_computed: None,
tags_btns: Default::default(),
@@ -220,238 +182,318 @@ impl OutputHandler for State {
});
}
- fn update_output(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _output: wl_output::WlOutput,
- ) {
+ fn bind_seat(&mut self, conn: &mut Connection<Self>, global: &Global) {
+ let seat: WlSeat = global.bind(conn, 5..=8).unwrap();
+ let cursor = Cursor::new(conn, self.wl_compositor).unwrap();
+ self.seats.push(Seat {
+ seat,
+ reg_name: global.name,
+ pointer: None,
+ cursor,
+ cur_surface: None,
+ x: 0.0,
+ y: 0.0,
+ pending_button: None,
+ pending_scroll: 0.0,
+ scroll_frame: ScrollFrame::default(),
+ });
}
- fn output_destroyed(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _output: wl_output::WlOutput,
- ) {
+ fn drop_bar(&mut self, conn: &mut Connection<Self>, bar_index: usize) {
+ let bar = self.bars.swap_remove(bar_index);
+ bar.surface.destroy(conn);
+ bar.layer_surface.destroy(conn);
+ if let Some(output_status) = bar.river_output_status {
+ output_status.destroy(conn);
+ }
+ if bar.output.version() >= 3 {
+ bar.output.release(conn);
+ }
}
-}
-impl LayerShellHandler for State {
- fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, layer: &LayerSurface) {
- self.bars.retain(|b| &b.layer != layer)
+ fn drop_seat(&mut self, conn: &mut Connection<Self>, seat_index: usize) {
+ let seat = self.seats.swap_remove(seat_index);
+ if let Some(pointer) = seat.pointer {
+ if pointer.version() >= 3 {
+ pointer.release(conn);
+ }
+ }
+ if seat.seat.version() >= 5 {
+ seat.seat.release(conn);
+ }
}
+}
- fn configure(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- layer: &LayerSurface,
- configure: LayerSurfaceConfigure,
- _serial: u32,
- ) {
- self.bars
- .iter_mut()
- .find(|b| &b.layer == layer)
- .unwrap()
- .configure(&mut self.shared_state, configure.new_size.0);
- }
+impl Dispatcher for State {
+ type Error = Infallible;
}
-impl SeatHandler for State {
- fn seat_state(&mut self) -> &mut SeatState {
- &mut self.seat_state
+impl Dispatch<WlRegistry> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, _: WlRegistry, event: wl_registry::Event) {
+ match event {
+ wl_registry::Event::Global(global) if global.is::<WlOutput>() => {
+ self.bind_output(conn, &global);
+ }
+ wl_registry::Event::Global(global) if global.is::<WlSeat>() => {
+ self.bind_seat(conn, &global);
+ }
+ wl_registry::Event::GlobalRemove(name) => {
+ if let Some(bar_index) =
+ self.bars.iter().position(|bar| bar.output_reg_name == name)
+ {
+ self.drop_bar(conn, bar_index);
+ } else if let Some(seat_index) =
+ self.seats.iter().position(|seat| seat.reg_name == name)
+ {
+ self.drop_seat(conn, seat_index);
+ }
+ }
+ _ => (),
+ }
}
+}
- fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
+impl Dispatch<WlCallback> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, cb: WlCallback, event: wl_callback::Event) {
+ let wl_callback::Event::Done(_) = event;
+ if let Some(bar) = self.bars.iter_mut().find(|bar| bar.frame_cb == Some(cb)) {
+ bar.frame_cb = None;
+ bar.frame(conn, &mut self.shared_state);
+ }
+ }
+}
- fn new_capability(
- &mut self,
- _conn: &Connection,
- qh: &QueueHandle<Self>,
- seat: wl_seat::WlSeat,
- capability: Capability,
- ) {
- if capability == Capability::Pointer {
- let (pointer, themed_pointer) = self
- .seat_state
- .get_pointer_with_theme(qh, &seat, ThemeSpec::System, 1)
- .expect("Failed to create pointer");
- self.seats.push(Seat {
- seat,
- pointer,
- themed_pointer,
- pointer_surface: self.compositor_state.create_surface(qh),
- finger_scroll: 0.0,
- });
+impl Dispatch<WlOutput> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, output: WlOutput, event: wl_output::Event) {
+ if let wl_output::Event::Scale(scale) = event {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.output == output)
+ .unwrap();
+ bar.scale = scale;
+ // If bar is not configured yet, it is because we were waiting for the "scale" event
+ // before commiting the surface. Otherwise, there is no need to do any redrawing (we'll
+ // do that after "configure" event).
+ if !bar.configured {
+ bar.surface.commit(conn);
+ }
}
}
+}
- fn remove_capability(
+impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1> for State {
+ fn event(
&mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- seat: wl_seat::WlSeat,
- capability: Capability,
+ conn: &mut Connection<Self>,
+ layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
+ event: zwlr_layer_surface_v1::Event,
) {
- if capability == Capability::Pointer {
- self.seats.retain(|p| p.seat == seat);
+ match event {
+ zwlr_layer_surface_v1::Event::Configure(args) => {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.layer_surface == layer_surface)
+ .unwrap();
+ assert_ne!(args.width, 0);
+ bar.width = args.width;
+ bar.configured = true;
+ bar.layer_surface.ack_configure(conn, args.serial);
+ bar.frame(conn, &mut self.shared_state);
+ }
+ zwlr_layer_surface_v1::Event::Closed => {
+ let bar_index = self
+ .bars
+ .iter()
+ .position(|bar| bar.layer_surface == layer_surface)
+ .unwrap();
+ self.drop_bar(conn, bar_index);
+ }
}
}
+}
- fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
+impl Dispatch<WlBuffer> for State {
+ fn event(&mut self, _: &mut Connection<Self>, buffer: WlBuffer, event: wl_buffer::Event) {
+ let wl_buffer::Event::Release = event;
+ self.shared_state.shm.free_buffer(buffer);
+ }
}
-impl PointerHandler for State {
- fn pointer_frame(
- &mut self,
- conn: &Connection,
- _qh: &QueueHandle<Self>,
- pointer: &wl_pointer::WlPointer,
- events: &[PointerEvent],
- ) {
+impl Dispatch<WlSeat> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, seat: WlSeat, event: wl_seat::Event) {
+ if let wl_seat::Event::Capabilities(capabilities) = event {
+ let seat = self.seats.iter_mut().find(|s| s.seat == seat).unwrap();
+ match &seat.pointer {
+ Some(pointer) if !capabilities.contains(wl_seat::Capability::Pointer) => {
+ if pointer.version() >= 3 {
+ pointer.release(conn);
+ }
+ seat.pointer = None;
+ }
+ None if capabilities.contains(wl_seat::Capability::Pointer) => {
+ let pointer = seat.seat.get_pointer(conn);
+ seat.pointer = Some(pointer);
+ }
+ _ => (),
+ }
+ }
+ }
+}
+
+impl Dispatch<WlPointer> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, pointer: WlPointer, event: wl_pointer::Event) {
let seat = self
.seats
.iter_mut()
- .find(|p| &p.pointer == pointer)
+ .find(|s| s.pointer == Some(pointer))
.unwrap();
- for event in events {
- if let Some(bar) = self
- .bars
- .iter_mut()
- .find(|b| b.layer.wl_surface() == &event.surface)
- {
- match event.kind {
- PointerEventKind::Enter { .. } => {
- seat.themed_pointer
- .set_cursor(
- conn,
- "default",
- self.shm_state.wl_shm(),
- &seat.pointer_surface,
- )
+
+ use wl_pointer::Event;
+ match event {
+ Event::Frame => {
+ let btn = seat.pending_button.take();
+ let scroll = seat.scroll_frame.finalize();
+ if let Some(surface) = seat.cur_surface {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.surface == surface)
+ .unwrap();
+
+ if let Some(btn) = btn {
+ bar.click(conn, &mut self.shared_state, btn, seat.seat, seat.x, seat.y)
.unwrap();
}
- PointerEventKind::Press { button, .. } => {
- bar.click(
- &mut self.shared_state,
- button.into(),
- &seat.seat,
- event.position.0,
- event.position.1,
- )
- .unwrap();
+
+ if scroll.is_finder && self.shared_state.config.invert_touchpad_scrolling {
+ seat.pending_scroll -= scroll.absolute;
+ } else {
+ seat.pending_scroll += scroll.absolute;
+ }
+
+ if scroll.stop {
+ seat.pending_scroll = 0.0;
}
- PointerEventKind::Axis {
- vertical, source, ..
- } => {
- if source == Some(wl_pointer::AxisSource::Finger) {
- if self.shared_state.config.invert_touchpad_scrolling {
- seat.finger_scroll -= vertical.absolute;
- } else {
- seat.finger_scroll += vertical.absolute;
- }
- if vertical.stop {
- seat.finger_scroll = 0.0;
- }
- }
- let btn = if vertical.discrete > 0 {
- PointerBtn::WheelDown
- } else if vertical.discrete < 0 {
- PointerBtn::WheelUp
- } else if seat.finger_scroll >= 15.0 {
- seat.finger_scroll = 0.0;
- PointerBtn::WheelDown
- } else if seat.finger_scroll <= -15.0 {
- seat.finger_scroll = 0.0;
- PointerBtn::WheelUp
- } else {
- continue;
- };
+ let btn = if seat.pending_scroll >= 15.0 {
+ seat.pending_scroll = 0.0;
+ Some(PointerBtn::WheelDown)
+ } else if seat.pending_scroll <= -15.0 {
+ seat.pending_scroll = 0.0;
+ Some(PointerBtn::WheelUp)
+ } else {
+ None
+ };
- bar.click(
- &mut self.shared_state,
- btn,
- &seat.seat,
- event.position.0,
- event.position.1,
- )
- .unwrap();
+ if let Some(btn) = btn {
+ bar.click(conn, &mut self.shared_state, btn, seat.seat, seat.x, seat.y)
+ .unwrap();
}
- _ => (),
}
}
+ Event::Enter(args) => {
+ let bar = self
+ .bars
+ .iter()
+ .find(|bar| bar.surface.id() == args.surface)
+ .unwrap();
+ seat.cur_surface = Some(bar.surface);
+ seat.x = args.surface_x.as_f64();
+ seat.y = args.surface_y.as_f64();
+ seat.cursor.set(
+ conn,
+ args.serial,
+ pointer,
+ bar.scale as u32,
+ &mut self.shared_state.shm,
+ );
+ }
+ Event::Leave(_) => seat.cur_surface = None,
+ Event::Motion(args) => {
+ seat.x = args.surface_x.as_f64();
+ seat.y = args.surface_y.as_f64();
+ }
+ Event::Button(args) => {
+ if args.state == wl_pointer::ButtonState::Pressed {
+ seat.pending_button = Some(args.button.into());
+ }
+ }
+ Event::Axis(args) => {
+ if args.axis == wl_pointer::Axis::VerticalScroll {
+ seat.scroll_frame.absolute += args.value.as_f64();
+ }
+ }
+ Event::AxisSource(source) => {
+ seat.scroll_frame.is_finder = source == wl_pointer::AxisSource::Finger;
+ }
+ Event::AxisStop(args) => {
+ if args.axis == wl_pointer::Axis::VerticalScroll {
+ seat.scroll_frame.stop = true;
+ }
+ }
+ Event::AxisDiscrete(_) | Event::AxisValue120(_) => (),
}
}
}
-impl ShmHandler for State {
- fn shm_state(&mut self) -> &mut ShmState {
- &mut self.shm_state
- }
-}
-
-impl RiverStatusHandler for State {
- fn river_status_state(&mut self) -> &mut RiverStatusState {
- self.river_status_state.as_mut().unwrap()
- }
-
- fn focused_tags_updated(&mut self, output: &wl_output::WlOutput, focused: u32) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.focused = focused;
- bar.draw(&mut self.shared_state);
- }
-
- fn urgent_tags_updated(&mut self, output: &wl_output::WlOutput, urgent: u32) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.urgent = urgent;
- bar.draw(&mut self.shared_state);
- }
+impl Dispatch<ZriverOutputStatusV1> for State {
+ fn event(
+ &mut self,
+ conn: &mut Connection<Self>,
+ status: ZriverOutputStatusV1,
+ event: zriver_output_status_v1::Event,
+ ) {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|b| b.river_output_status == Some(status))
+ .unwrap();
- fn views_tags_updated(&mut self, output: &wl_output::WlOutput, tags: Vec<u32>) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.active = tags.into_iter().fold(0, |a, b| a | b);
- bar.draw(&mut self.shared_state);
- }
+ use zriver_output_status_v1::Event;
+ match event {
+ Event::FocusedTags(tags) => bar.tags_info.focused = tags,
+ Event::UrgentTags(tags) => bar.tags_info.urgent = tags,
+ Event::LayoutName(name) => bar.set_layout_name(Some(name.into_string().unwrap())),
+ Event::LayoutNameClear => bar.set_layout_name(None),
+ Event::ViewTags(vt) => {
+ bar.tags_info.active = vt
+ .chunks_exact(4)
+ .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap()))
+ .fold(0, |a, b| a | b);
+ }
+ }
- fn layout_name_updated(&mut self, output: &wl_output::WlOutput, layout_name: Option<String>) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.set_layout_name(layout_name);
- bar.draw(&mut self.shared_state);
+ bar.request_frame(conn);
}
}
-impl RiverControlHandler for State {
- fn river_control_state(&mut self) -> &mut RiverControlState {
- self.river_control_state.as_mut().unwrap()
- }
-
- fn command_failure(&mut self, _: &Connection, _: &QueueHandle<Self>, message: String) {
- self.set_error(format!("[river_control] {message}"));
- }
-
- fn command_success(&mut self, _: &Connection, _: &QueueHandle<Self>, message: String) {
- info!("river_control: {message}");
+impl Dispatch<ZriverCommandCallbackV1> for State {
+ fn event(
+ &mut self,
+ conn: &mut Connection<Self>,
+ _: ZriverCommandCallbackV1,
+ event: zriver_command_callback_v1::Event,
+ ) {
+ use zriver_command_callback_v1::Event;
+ match event {
+ Event::Success(msg) => info!("river_control: {msg:?}"),
+ Event::Failure(msg) => self.set_error(conn, msg.into_string().unwrap()),
+ }
}
}
-delegate_compositor!(State);
-delegate_output!(State);
-delegate_shm!(State);
-delegate_seat!(State);
-delegate_pointer!(State);
-delegate_layer!(State);
-delegate_registry!(State);
-delegate_river_status!(State);
-delegate_river_control!(State);
+// Interfaces with no events
+impl Dispatch<WlCompositor> for State {}
+impl Dispatch<ZwlrLayerShellV1> for State {}
+impl Dispatch<ZriverStatusManagerV1> for State {}
+impl Dispatch<ZriverControlV1> for State {}
+impl Dispatch<WlShmPool> for State {}
-impl ProvidesRegistryState for State {
- fn registry(&mut self) -> &mut RegistryState {
- &mut self.registry_state
- }
- registry_handlers![OutputState,];
-}
+// Dont care
+impl Dispatch<WlShm> for State {}
+impl Dispatch<WlSurface> for State {}
#[derive(Debug)]
pub struct ComputedBlock {
@@ -460,3 +502,18 @@ pub struct ComputedBlock {
pub short: Option<ComputedText>,
pub min_width: Option<f64>,
}
+
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ScrollFrame {
+ stop: bool,
+ absolute: f64,
+ is_finder: bool,
+}
+
+impl ScrollFrame {
+ fn finalize(&mut self) -> Self {
+ let copy = *self;
+ *self = Self::default();
+ copy
+ }
+}
diff --git a/src/status_cmd.rs b/src/status_cmd.rs
index 4d9c453..f6c9c2c 100644
--- a/src/status_cmd.rs
+++ b/src/status_cmd.rs
@@ -1,22 +1,22 @@
-use std::io::{BufRead, BufReader, Write};
-use std::os::unix::prelude::AsRawFd;
-use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
+use std::io::Write;
+use std::process::{Child, ChildStdin, Command, Stdio};
use anyhow::Result;
-use tokio::io::unix::AsyncFd;
-use tokio::io::Interest;
+use tokio::io::AsyncReadExt;
+use tokio::process::ChildStdout;
use crate::i3bar_protocol::{Block, Event, Protocol};
+const INITIAL_BUF_CAPACITY: usize = 4096;
+
#[derive(Debug)]
pub struct StatusCmd {
pub child: Child,
- output: BufReader<ChildStdout>,
+ pub output: ChildStdout,
input: ChildStdin,
protocol: Protocol,
buf: Vec<u8>,
- pub async_fd: AsyncFd<i32>,
}
impl StatusCmd {
@@ -28,73 +28,45 @@ impl StatusCmd {
.spawn()?;
let output = child.stdout.take().unwrap();
let input = child.stdin.take().unwrap();
- let async_fd = AsyncFd::with_interest(output.as_raw_fd(), Interest::READABLE)?;
Ok(Self {
child,
- output: BufReader::new(output),
+ output: ChildStdout::from_std(output)?,
input,
protocol: Protocol::Unknown,
- buf: Vec::new(),
- async_fd,
+ buf: Vec::with_capacity(INITIAL_BUF_CAPACITY),
})
}
pub fn notify_available(&mut self) -> Result<Option<Vec<Block>>> {
- let buf = self.output.fill_buf()?;
- if buf.is_empty() {
- bail!("status command exited");
- }
- if self.buf.is_empty() {
- let rem = self.protocol.process_new_bytes(buf)?;
- if !rem.is_empty() {
- self.buf.extend_from_slice(rem);
- }
+ let rem = self.protocol.process_new_bytes(&self.buf)?;
+ if rem.is_empty() {
+ self.buf.clear();
} else {
- self.buf.extend_from_slice(buf);
- let rem = self.protocol.process_new_bytes(&self.buf)?;
- if rem.is_empty() {
- self.buf.clear();
- } else {
- let used = self.buf.len() - rem.len();
- self.buf.drain(..used);
- }
+ let used = self.buf.len() - rem.len();
+ self.buf.drain(..used);
}
- let consumed = buf.len();
- self.output.consume(consumed);
Ok(self.protocol.get_blocks())
}
+ pub async fn read(&mut self) -> Result<()> {
+ assert!(self.buf.capacity() >= INITIAL_BUF_CAPACITY);
+ if self.buf.capacity() == self.buf.len() {
+ // Double the capacity
+ self.buf.reserve(self.buf.len());
+ }
+
+ let read_len = self.output.read_buf(&mut self.buf).await?;
+ if read_len == 0 {
+ bail!("status command exited");
+ }
+
+ Ok(())
+ }
+
pub fn send_click_event(&mut self, event: &Event) -> Result<()> {
if self.protocol.supports_clicks() {
writeln!(self.input, "{}", serde_json::to_string(event).unwrap())?;
}
Ok(())
}
-
- // pub fn quick_insert(&self, handle: LoopHandle<BarState>) {
- // handle
- // .insert_source(
- // calloop::generic::Generic::new(
- // self.output.get_ref().as_raw_fd(),
- // calloop::Interest {
- // readable: true,
- // writable: false,
- // },
- // calloop::Mode::Level,
- // ),
- // move |ready, _, bar_state| {
- // if ready.readable {
- // bar_state.notify_available()?;
- // Ok(calloop::PostAction::Continue)
- // } else {
- // bar_state.set_error("error reading from status command");
- // if let Some(mut child) = bar_state.status_cmd.take() {
- // let _ = child.child.kill();
- // }
- // Ok(calloop::PostAction::Remove)
- // }
- // },
- // )
- // .expect("failed to inser calloop source");
- // }
}