diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bar.rs | 160 | ||||
| -rw-r--r-- | src/config.rs | 2 | ||||
| -rw-r--r-- | src/cursor.rs | 109 | ||||
| -rw-r--r-- | src/main.rs | 43 | ||||
| -rw-r--r-- | src/protocol.rs | 8 | ||||
| -rw-r--r-- | src/river_protocols.rs | 4 | ||||
| -rw-r--r-- | src/river_protocols/control.rs | 112 | ||||
| -rw-r--r-- | src/river_protocols/status.rs | 144 | ||||
| -rw-r--r-- | src/shared_state.rs | 14 | ||||
| -rw-r--r-- | src/state.rs | 713 | ||||
| -rw-r--r-- | src/status_cmd.rs | 86 |
11 files changed, 630 insertions, 765 deletions
@@ -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"); - // } } |
