From 3427fe5d7c524792324bf5a6f2527a6350618b66 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Thu, 14 Dec 2023 22:00:04 -0600 Subject: [PATCH] Do other misc stuff These are some horrible commit messages --- api/lua/example_config.lua | 68 +++++----- src/{config => }/api.rs | 15 ++- .../api_handlers.rs => api/handlers.rs} | 123 +++++++++--------- src/{config => }/api/msg.rs | 0 src/backend/udev.rs | 12 +- src/config.rs | 10 +- src/input.rs | 11 +- src/main.rs | 1 + src/state.rs | 14 +- 9 files changed, 135 insertions(+), 119 deletions(-) rename src/{config => }/api.rs (96%) rename src/{state/api_handlers.rs => api/handlers.rs} (91%) rename src/{config => }/api/msg.rs (100%) diff --git a/api/lua/example_config.lua b/api/lua/example_config.lua index b420ee1..143b64a 100644 --- a/api/lua/example_config.lua +++ b/api/lua/example_config.lua @@ -23,7 +23,6 @@ require("pinnacle").setup(function(pinnacle) ---@type Modifier local mod_key = "Ctrl" -- This is set to `Ctrl` instead of `Super` to not conflict with your WM/DE keybinds - -- ^ Add type annotations for that sweet, sweet autocomplete local terminal = "alacritty" @@ -43,49 +42,55 @@ require("pinnacle").setup(function(pinnacle) -- Mousebinds -------------------------------------------------------------------- - input.mousebind({"Ctrl"}, buttons.left, "Press", - function() window.begin_move(buttons.left) end) - input.mousebind({"Ctrl"}, buttons.right, "Press", - function() window.begin_resize(buttons.right) end) + input.mousebind({ "Ctrl" }, buttons.left, "Press", function() + window.begin_move(buttons.left) + end) + input.mousebind({ "Ctrl" }, buttons.right, "Press", function() + window.begin_resize(buttons.right) + end) -- Keybinds ---------------------------------------------------------------------- -- mod_key + Alt + q quits the compositor - input.keybind({mod_key, "Alt"}, keys.q, pinnacle.quit) + input.keybind({ mod_key, "Alt" }, keys.q, pinnacle.quit) -- mod_key + Alt + c closes the focused window - input.keybind({mod_key, "Alt"}, keys.c, - function() window.get_focused():close() end) + input.keybind({ mod_key, "Alt" }, keys.c, function() + window.get_focused():close() + end) -- mod_key + return spawns a terminal - input.keybind({mod_key}, keys.Return, function() + input.keybind({ mod_key }, keys.Return, function() process.spawn(terminal, function(stdout, stderr, exit_code, exit_msg) -- do something with the output here end) end) -- mod_key + Alt + Space toggle floating on the focused window - input.keybind({mod_key, "Alt"}, keys.space, - function() window.get_focused():toggle_floating() end) + input.keybind({ mod_key, "Alt" }, keys.space, function() + window.get_focused():toggle_floating() + end) -- mod_key + f toggles fullscreen on the focused window - input.keybind({mod_key}, keys.f, - function() window.get_focused():toggle_fullscreen() end) + input.keybind({ mod_key }, keys.f, function() + window.get_focused():toggle_fullscreen() + end) -- mod_key + m toggles maximized on the focused window - input.keybind({mod_key}, keys.m, - function() window.get_focused():toggle_maximized() end) + input.keybind({ mod_key }, keys.m, function() + window.get_focused():toggle_maximized() + end) -- Tags --------------------------------------------------------------------------- - local tags = {"1", "2", "3", "4", "5"} + local tags = { "1", "2", "3", "4", "5" } output.connect_for_all(function(op) -- Add tags 1, 2, 3, 4 and 5 on all monitors, and toggle tag 1 active by default op:add_tags(tags) -- Same as tag.add(op, "1", "2", "3", "4", "5") - tag.toggle({name = "1", output = op}) + tag.toggle({ name = "1", output = op }) -- Window rules -- Add your own window rules here. Below is an example. @@ -112,30 +117,35 @@ require("pinnacle").setup(function(pinnacle) -- Create a layout cycler to cycle your tag layouts. This will store which layout each tag has -- and change to the next or previous one in the array when the respective function is called. local layout_cycler = tag.layout_cycler({ - "MasterStack", "Dwindle", "Spiral", "CornerTopLeft", "CornerTopRight", - "CornerBottomLeft", "CornerBottomRight" + "MasterStack", + "Dwindle", + "Spiral", + "CornerTopLeft", + "CornerTopRight", + "CornerBottomLeft", + "CornerBottomRight", }) - input.keybind({mod_key}, keys.space, layout_cycler.next) - input.keybind({mod_key, "Shift"}, keys.space, layout_cycler.prev) + input.keybind({ mod_key }, keys.space, layout_cycler.next) + input.keybind({ mod_key, "Shift" }, keys.space, layout_cycler.prev) -- Tag manipulation for _, tag_name in pairs(tags) do -- mod_key + 1-5 switches tags - input.keybind({mod_key}, tag_name, - function() tag.switch_to(tag_name) end) + input.keybind({ mod_key }, tag_name, function() + tag.switch_to(tag_name) + end) -- mod_key + Shift + 1-5 toggles tags - input.keybind({mod_key, "Shift"}, tag_name, - function() tag.toggle(tag_name) end) + input.keybind({ mod_key, "Shift" }, tag_name, function() + tag.toggle(tag_name) + end) -- mod_key + Alt + 1-5 moves windows to tags - input.keybind({mod_key, "Alt"}, tag_name, - function() + input.keybind({ mod_key, "Alt" }, tag_name, function() window:get_focused():move_to_tag(tag_name) end) -- mod_key + Shift + Alt + 1-5 toggles tags on windows - input.keybind({mod_key, "Shift", "Alt"}, tag_name, - function() + input.keybind({ mod_key, "Shift", "Alt" }, tag_name, function() window.get_focused():toggle_tag(tag_name) end) end diff --git a/src/config/api.rs b/src/api.rs similarity index 96% rename from src/config/api.rs rename to src/api.rs index 6f20484..db28b9c 100644 --- a/src/config/api.rs +++ b/src/api.rs @@ -34,6 +34,7 @@ //! //! For an example, look at the Lua implementation in the repository. +pub mod handlers; pub mod msg; use std::{ @@ -52,7 +53,6 @@ use sysinfo::{ProcessRefreshKind, RefreshKind, SystemExt}; use self::msg::{Msg, OutgoingMsg}; -pub const DEFAULT_SOCKET_DIR: &str = "/tmp"; pub const SOCKET_NAME: &str = "pinnacle_socket"; fn handle_client( @@ -159,7 +159,8 @@ pub fn send_to_client( stream: &mut UnixStream, msg: &OutgoingMsg, ) -> Result<(), rmp_serde::encode::Error> { - // tracing::debug!("Sending {msg:?}"); + tracing::trace!("Sending {msg:?}"); + let msg = rmp_serde::to_vec_named(msg)?; let msg_len = msg.len() as u32; let bytes = msg_len.to_ne_bytes(); @@ -201,11 +202,13 @@ impl EventSource for PinnacleSocketSource { .process_events(readiness, token, |_readiness, listener| { while let Ok((stream, _sock_addr)) = listener.accept() { let sender = self.sender.clone(); - let callback_stream = match stream.try_clone() { - Ok(callback_stream) => callback_stream, - Err(err) => return Err(err), - }; + let callback_stream = stream.try_clone()?; + callback(callback_stream, &mut ()); + + // Handle the client in another thread as to not block the main one. + // + // No idea if this is even needed or if it's premature optimization. std::thread::spawn(move || { if let Err(err) = handle_client(stream, sender) { tracing::error!("handle_client errored: {err}"); diff --git a/src/state/api_handlers.rs b/src/api/handlers.rs similarity index 91% rename from src/state/api_handlers.rs rename to src/api/handlers.rs index d72a2ea..3f08bd6 100644 --- a/src/state/api_handlers.rs +++ b/src/api/handlers.rs @@ -1,7 +1,7 @@ use std::ffi::OsString; use async_process::Stdio; -use futures_lite::AsyncBufReadExt; +use futures_lite::{AsyncBufReadExt, StreamExt}; use smithay::{ desktop::space::SpaceElement, input::keyboard::XkbConfig, @@ -11,18 +11,16 @@ use smithay::{ }; use crate::{ - config::{ - api::msg::{ - Args, CallbackId, KeyIntOrString, Msg, OutgoingMsg, Request, RequestId, RequestResponse, - }, - ConnectorSavedState, + api::msg::{ + Args, CallbackId, KeyIntOrString, Msg, OutgoingMsg, Request, RequestId, RequestResponse, }, + config::ConnectorSavedState, focus::FocusTarget, tag::Tag, window::WindowElement, }; -use super::{State, WithState}; +use crate::state::{State, WithState}; impl State { pub fn handle_msg(&mut self, msg: Msg) { @@ -361,10 +359,10 @@ impl State { .stream .as_ref() .expect("stream doesn't exist"); - let mut stream = stream.lock().expect("couldn't lock stream"); + for output in self.space.outputs() { - crate::config::api::send_to_client( - &mut stream, + crate::api::send_to_client( + &mut stream.lock().expect("couldn't lock stream"), &OutgoingMsg::CallCallback { callback_id, args: Some(Args::ConnectForAllOutputs { @@ -374,6 +372,7 @@ impl State { ) .expect("Send to client failed"); } + self.config.output_callback_ids.push(callback_id); } Msg::SetOutputLocation { output_name, x, y } => { @@ -457,10 +456,10 @@ impl State { let stream = self .api_state .stream - .as_ref() - .expect("Stream doesn't exist") - .clone(); + .clone() // clone due to use of self below + .expect("Stream doesn't exist"); let mut stream = stream.lock().expect("Couldn't lock stream"); + match request { Request::GetWindows => { let window_ids = self @@ -469,8 +468,7 @@ impl State { .map(|win| win.with_state(|state| state.id)) .collect::>(); - // FIXME: figure out what to do if error - crate::config::api::send_to_client( + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -481,13 +479,16 @@ impl State { } Request::GetWindowProps { window_id } => { let window = window_id.window(self); + let size = window .as_ref() .map(|win| (win.geometry().size.w, win.geometry().size.h)); + let loc = window .as_ref() .and_then(|win| self.space.element_location(win)) .map(|loc| (loc.x, loc.y)); + let (class, title) = window.as_ref().map_or((None, None), |win| match &win { WindowElement::Wayland(_) => { if let Some(wl_surf) = win.wl_surface() { @@ -508,17 +509,21 @@ impl State { (Some(surface.class()), Some(surface.title())) } }); + let focused = window.as_ref().and_then(|win| { let output = win.output(self)?; self.focused_window(&output).map(|foc_win| win == &foc_win) }); + let floating = window .as_ref() .map(|win| win.with_state(|state| state.floating_or_tiled.is_floating())); + let fullscreen_or_maximized = window .as_ref() .map(|win| win.with_state(|state| state.fullscreen_or_maximized)); - crate::config::api::send_to_client( + + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -541,7 +546,8 @@ impl State { .outputs() .map(|output| output.name()) .collect::>(); - crate::config::api::send_to_client( + + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -555,38 +561,47 @@ impl State { .space .outputs() .find(|output| output.name() == output_name); + let res = output.as_ref().and_then(|output| { output.current_mode().map(|mode| (mode.size.w, mode.size.h)) }); + let refresh_rate = output .as_ref() .and_then(|output| output.current_mode().map(|mode| mode.refresh)); + let model = output .as_ref() .map(|output| output.physical_properties().model); + let physical_size = output.as_ref().map(|output| { ( output.physical_properties().size.w, output.physical_properties().size.h, ) }); + let make = output .as_ref() .map(|output| output.physical_properties().make); + let loc = output .as_ref() .map(|output| (output.current_location().x, output.current_location().y)); + let focused = self .focus_state .focused_output .as_ref() .and_then(|foc_op| output.map(|op| op == foc_op)); + let tag_ids = output.as_ref().map(|output| { output.with_state(|state| { state.tags.iter().map(|tag| tag.id()).collect::>() }) }); - crate::config::api::send_to_client( + + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -611,8 +626,8 @@ impl State { .flat_map(|op| op.with_state(|state| state.tags.clone())) .map(|tag| tag.id()) .collect::>(); - tracing::debug!("GetTags: {:?}", tag_ids); - crate::config::api::send_to_client( + + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -623,13 +638,16 @@ impl State { } Request::GetTagProps { tag_id } => { let tag = tag_id.tag(self); + let output_name = tag .as_ref() .and_then(|tag| tag.output(self)) .map(|output| output.name()); + let active = tag.as_ref().map(|tag| tag.active()); let name = tag.as_ref().map(|tag| tag.name()); - crate::config::api::send_to_client( + + crate::api::send_to_client( &mut stream, &OutgoingMsg::RequestResponse { request_id, @@ -649,6 +667,7 @@ impl State { let mut command = command.into_iter(); let Some(program) = command.next() else { // TODO: notify that command was nothing + tracing::warn!("got an empty command"); return; }; @@ -690,31 +709,23 @@ impl State { if let Some(callback_id) = callback_id { let stdout = child.stdout.take(); let stderr = child.stderr.take(); - let stream_out = self - .api_state - .stream - .as_ref() - .expect("Stream doesn't exist") - .clone(); + + let stream_out = self.api_state.stream.clone().expect("Stream doesn't exist"); let stream_err = stream_out.clone(); let stream_exit = stream_out.clone(); if let Some(stdout) = stdout { let future = async move { - // TODO: use BufReader::new().lines() - let mut reader = futures_lite::io::BufReader::new(stdout); - loop { - let mut buf = String::new(); - match reader.read_line(&mut buf).await { - Ok(0) => break, - Ok(_) => { - let mut stream = stream_out.lock().expect("Couldn't lock stream"); - crate::config::api::send_to_client( - &mut stream, + let mut reader = futures_lite::io::BufReader::new(stdout).lines(); + while let Some(line) = reader.next().await { + match line { + Ok(line) => { + crate::api::send_to_client( + &mut stream_out.lock().expect("Couldn't lock stream"), &OutgoingMsg::CallCallback { callback_id, args: Some(Args::Spawn { - stdout: Some(buf.trim_end_matches('\n').to_string()), + stdout: Some(line), stderr: None, exit_code: None, exit_msg: None, @@ -723,10 +734,8 @@ impl State { ) .expect("Send to client failed"); // TODO: notify instead of crash } - Err(err) => { - tracing::warn!("child read err: {err}"); - break; - } + // TODO: possibly break on err? + Err(err) => tracing::warn!("read err: {err}"), } } }; @@ -736,22 +745,20 @@ impl State { tracing::error!("Failed to schedule future: {err}"); } } + if let Some(stderr) = stderr { let future = async move { - let mut reader = futures_lite::io::BufReader::new(stderr); - loop { - let mut buf = String::new(); - match reader.read_line(&mut buf).await { - Ok(0) => break, - Ok(_) => { - let mut stream = stream_err.lock().expect("Couldn't lock stream"); - crate::config::api::send_to_client( - &mut stream, + let mut reader = futures_lite::io::BufReader::new(stderr).lines(); + while let Some(line) = reader.next().await { + match line { + Ok(line) => { + crate::api::send_to_client( + &mut stream_err.lock().expect("Couldn't lock stream"), &OutgoingMsg::CallCallback { callback_id, args: Some(Args::Spawn { stdout: None, - stderr: Some(buf.trim_end_matches('\n').to_string()), + stderr: Some(line), exit_code: None, exit_msg: None, }), @@ -759,13 +766,11 @@ impl State { ) .expect("Send to client failed"); // TODO: notify instead of crash } - Err(err) => { - tracing::warn!("child read err: {err}"); - break; - } + Err(err) => tracing::warn!("read err: {err}"), } } }; + if let Err(err) = self.async_scheduler.schedule(future) { tracing::error!("Failed to schedule future: {err}"); } @@ -774,9 +779,8 @@ impl State { let future = async move { match child.status().await { Ok(exit_status) => { - let mut stream = stream_exit.lock().expect("Couldn't lock stream"); - crate::config::api::send_to_client( - &mut stream, + crate::api::send_to_client( + &mut stream_exit.lock().expect("Couldn't lock stream"), &OutgoingMsg::CallCallback { callback_id, args: Some(Args::Spawn { @@ -794,6 +798,7 @@ impl State { } } }; + if let Err(err) = self.async_scheduler.schedule(future) { tracing::error!("Failed to schedule future: {err}"); } diff --git a/src/config/api/msg.rs b/src/api/msg.rs similarity index 100% rename from src/config/api/msg.rs rename to src/api/msg.rs diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 8099fa5..e0f4dd5 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -74,11 +74,9 @@ use smithay_drm_extras::{ }; use crate::{ + api::msg::{Args, OutgoingMsg}, backend::Backend, - config::{ - api::msg::{Args, OutgoingMsg}, - ConnectorSavedState, - }, + config::ConnectorSavedState, output::OutputName, render::{pointer::PointerElement, take_presentation_feedback}, state::{CalloopData, State, SurfaceDmabufFeedback, WithState}, @@ -1015,10 +1013,10 @@ impl State { .api_state .stream .as_ref() - .expect("Stream doesn't exist"); - let mut stream = stream.lock().expect("Couldn't lock stream"); + .expect("stream doesn't exist"); + let mut stream = stream.lock().expect("couldn't lock stream"); for callback_id in dt.state.config.output_callback_ids.iter() { - crate::config::api::send_to_client( + crate::api::send_to_client( &mut stream, &OutgoingMsg::CallCallback { callback_id: *callback_id, diff --git a/src/config.rs b/src/config.rs index afecf34..564d4aa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,5 @@ -pub mod api; - use crate::{ - config::api::{msg::ModifierMask, PinnacleSocketSource}, + api::{msg::ModifierMask, PinnacleSocketSource}, output::OutputName, tag::Tag, window::rules::{WindowRule, WindowRuleCondition}, @@ -19,7 +17,7 @@ use smithay::{ }; use toml::Table; -use api::msg::Modifier; +use crate::api::msg::{CallbackId, Modifier}; use xkbcommon::xkb::Keysym; use crate::{ @@ -27,7 +25,7 @@ use crate::{ tag::TagId, }; -use self::api::msg::CallbackId; +const DEFAULT_SOCKET_DIR: &str = "/tmp"; #[derive(serde::Deserialize, Debug)] pub struct Metaconfig { @@ -226,7 +224,7 @@ impl State { crate::XDG_BASE_DIRS .get_runtime_directory() .cloned() - .unwrap_or(PathBuf::from(crate::config::api::DEFAULT_SOCKET_DIR)) + .unwrap_or(PathBuf::from(DEFAULT_SOCKET_DIR)) }; let socket_source = PinnacleSocketSource::new(tx_channel, &socket_dir) diff --git a/src/input.rs b/src/input.rs index b34f740..dd826cd 100644 --- a/src/input.rs +++ b/src/input.rs @@ -5,7 +5,7 @@ pub mod libinput; use std::collections::HashMap; use crate::{ - config::api::msg::{CallbackId, Modifier, ModifierMask, MouseEdge, OutgoingMsg}, + api::msg::{CallbackId, Modifier, ModifierMask, MouseEdge, OutgoingMsg}, focus::FocusTarget, state::WithState, window::WindowElement, @@ -244,7 +244,7 @@ impl State { match action { Some(KeyAction::CallCallback(callback_id)) => { if let Some(stream) = self.api_state.stream.as_ref() { - if let Err(err) = crate::config::api::send_to_client( + if let Err(err) = crate::api::send_to_client( &mut stream.lock().expect("Could not lock stream mutex"), &OutgoingMsg::CallCallback { callback_id, @@ -295,10 +295,9 @@ impl State { .mousebinds .get(&(modifier_mask, button, edge)) { - if let Some(stream) = self.api_state.stream.clone() { - let mut stream = stream.lock().expect("failed to lock api stream"); - crate::config::api::send_to_client( - &mut stream, + if let Some(stream) = self.api_state.stream.as_ref() { + crate::api::send_to_client( + &mut stream.lock().expect("failed to lock api stream"), &OutgoingMsg::CallCallback { callback_id, args: None, diff --git a/src/main.rs b/src/main.rs index cb7ce03..150668c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ use tracing_appender::rolling::Rotation; use tracing_subscriber::{fmt::writer::MakeWriterExt, EnvFilter}; use xdg::BaseDirectories; +mod api; mod backend; mod config; mod cursor; diff --git a/src/state.rs b/src/state.rs index 3d95733..7a13d76 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,15 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later -mod api_handlers; - use std::{cell::RefCell, sync::Arc, time::Duration}; use crate::{ + api::{msg::Msg, ApiState}, backend::Backend, - config::{ - api::{msg::Msg, ApiState}, - Config, - }, + config::Config, cursor::Cursor, focus::FocusState, grab::resize_grab::ResizeSurfaceState, @@ -77,8 +73,11 @@ pub struct State { pub primary_selection_state: PrimarySelectionState, pub layer_shell_state: WlrLayerShellState, + /// The state of key and mousebinds along with libinput settings pub input_state: InputState, + /// The state holding stuff dealing with the api, like the stream pub api_state: ApiState, + /// Keeps track of the focus stack and focused output pub focus_state: FocusState, pub popup_manager: PopupManager, @@ -87,12 +86,15 @@ pub struct State { pub pointer_location: Point, pub dnd_icon: Option, + /// The main window vec pub windows: Vec, pub config: Config, + /// A scheduler for futures pub async_scheduler: Scheduler<()>, + // xwayland stuff pub xwayland: XWayland, pub xwm: Option, pub xdisplay: Option,