diff --git a/api/rust/examples/example_config.rs b/api/rust/examples/example_config.rs index d5deb46..bb312c1 100644 --- a/api/rust/examples/example_config.rs +++ b/api/rust/examples/example_config.rs @@ -2,115 +2,125 @@ use pinnacle_api::prelude::*; use pinnacle_api::*; fn main() { - pinnacle::setup(|| { - let mod_key = Modifier::Ctrl; + // let mut num = 5; + // let test = &mut num; - let terminal = "alacritty"; + pinnacle_api::connect().unwrap(); - process::set_env("MOZ_ENABLE_WAYLAND", "1"); + let mod_key = Modifier::Ctrl; - input::mousebind(&[mod_key], MouseButton::Left, MouseEdge::Press, move || { - window::begin_move(MouseButton::Left); + let terminal = "alacritty"; + + process::set_env("MOZ_ENABLE_WAYLAND", "1"); + + input::mousebind(&[mod_key], MouseButton::Left, MouseEdge::Press, move || { + window::begin_move(MouseButton::Left); + }); + + input::mousebind( + &[mod_key], + MouseButton::Right, + MouseEdge::Press, + move || { + window::begin_resize(MouseButton::Right); + }, + ); + + input::keybind(&[mod_key, Modifier::Alt], 'q', pinnacle::quit); + + input::keybind(&[mod_key, Modifier::Alt], 'c', move || { + if let Some(window) = window::get_focused() { + window.close(); + } + }); + + input::keybind(&[mod_key], xkbcommon::xkb::keysyms::KEY_Return, move || { + process::spawn(vec![terminal]).unwrap(); + }); + + input::keybind( + &[mod_key, Modifier::Alt], + xkbcommon::xkb::keysyms::KEY_space, + move || { + if let Some(window) = window::get_focused() { + window.toggle_floating(); + } + }, + ); + + input::keybind(&[mod_key], 'f', move || { + if let Some(window) = window::get_focused() { + window.toggle_fullscreen(); + } + }); + + input::keybind(&[mod_key], 'm', move || { + if let Some(window) = window::get_focused() { + window.toggle_maximized(); + } + }); + + let tags = ["1", "2", "3", "4", "5"]; + + output::connect_for_all(move |output| { + tag::add(&output, tags.as_slice()); + tag::get("1", Some(&output)).unwrap().toggle(); + }); + + // let mut num = 5; + // + // input::keybind(&[mod_key], 'm', || { + // num += 1; + // }); + + // let layout_cycler = tag.layout_cycler(&[ + // Layout::MasterStack, + // Layout::Dwindle, + // Layout::Spiral, + // Layout::CornerTopLeft, + // Layout::CornerTopRight, + // Layout::CornerBottomLeft, + // Layout::CornerBottomRight, + // ]); + // + // input.keybind(&[mod_key], xkbcommon::xkb::keysyms::KEY_space, move || { + // layout_cycler.next(None); + // }); + + for tag_name in tags.iter().map(|t| t.to_string()) { + let t = tag_name.clone(); + input::keybind(&[mod_key], tag_name.chars().next().unwrap(), move || { + tag::get(&t, None).unwrap().switch_to(); }); - - input::mousebind( - &[mod_key], - MouseButton::Right, - MouseEdge::Press, + let t = tag_name.clone(); + input::keybind( + &[mod_key, Modifier::Shift], + tag_name.chars().next().unwrap(), move || { - window::begin_resize(MouseButton::Right); + tag::get(&t, None).unwrap().toggle(); }, ); - - input::keybind(&[mod_key, Modifier::Alt], 'q', pinnacle::quit); - - input::keybind(&[mod_key, Modifier::Alt], 'c', move || { - if let Some(window) = window::get_focused() { - window.close(); - } - }); - - input::keybind(&[mod_key], xkbcommon::xkb::keysyms::KEY_Return, move || { - process::spawn(vec![terminal]).unwrap(); - }); - + let t = tag_name.clone(); input::keybind( &[mod_key, Modifier::Alt], - xkbcommon::xkb::keysyms::KEY_space, + tag_name.chars().next().unwrap(), move || { if let Some(window) = window::get_focused() { - window.toggle_floating(); + window.move_to_tag(&tag::get(&t, None).unwrap()); } }, ); + let t = tag_name.clone(); + input::keybind( + &[mod_key, Modifier::Shift, Modifier::Alt], + tag_name.chars().next().unwrap(), + move || { + if let Some(window) = window::get_focused() { + window.toggle_tag(&tag::get(&t, None).unwrap()); + } + }, + ); + } - input::keybind(&[mod_key], 'f', move || { - if let Some(window) = window::get_focused() { - window.toggle_fullscreen(); - } - }); - - input::keybind(&[mod_key], 'm', move || { - if let Some(window) = window::get_focused() { - window.toggle_maximized(); - } - }); - - let tags = ["1", "2", "3", "4", "5"]; - - output::connect_for_all(move |output| { - tag::add(&output, tags.as_slice()); - tag::get("1", Some(&output)).unwrap().toggle(); - }); - - // let layout_cycler = tag.layout_cycler(&[ - // Layout::MasterStack, - // Layout::Dwindle, - // Layout::Spiral, - // Layout::CornerTopLeft, - // Layout::CornerTopRight, - // Layout::CornerBottomLeft, - // Layout::CornerBottomRight, - // ]); - // - // input.keybind(&[mod_key], xkbcommon::xkb::keysyms::KEY_space, move || { - // layout_cycler.next(None); - // }); - - for tag_name in tags.iter().map(|t| t.to_string()) { - let t = tag_name.clone(); - input::keybind(&[mod_key], tag_name.chars().next().unwrap(), move || { - tag::get(&t, None).unwrap().switch_to(); - }); - let t = tag_name.clone(); - input::keybind( - &[mod_key, Modifier::Shift], - tag_name.chars().next().unwrap(), - move || { - tag::get(&t, None).unwrap().toggle(); - }, - ); - let t = tag_name.clone(); - input::keybind( - &[mod_key, Modifier::Alt], - tag_name.chars().next().unwrap(), - move || { - if let Some(window) = window::get_focused() { - window.move_to_tag(&tag::get(&t, None).unwrap()); - } - }, - ); - let t = tag_name.clone(); - input::keybind( - &[mod_key, Modifier::Shift, Modifier::Alt], - tag_name.chars().next().unwrap(), - move || { - if let Some(window) = window::get_focused() { - window.toggle_tag(&tag::get(&t, None).unwrap()); - } - }, - ); - } - }) - .unwrap(); + pinnacle_api::listen(); } diff --git a/api/rust/src/input.rs b/api/rust/src/input.rs index 8be0f10..0dbe95b 100644 --- a/api/rust/src/input.rs +++ b/api/rust/src/input.rs @@ -16,8 +16,7 @@ use crate::{ /// - `key`: The key that needs to be pressed. This takes `impl Into` and can /// take the following three types: /// - [`char`]: A character of the key you want. This can be `a`, `~`, `@`, and so on. -/// - [`u32`]: The key in numeric form. You can use the keys defined in -/// [`xkbcommon::xkb::keysyms`] for this. +/// - [`u32`]: The key in numeric form. You can use the keys defined in [`xkbcommon::xkb::keysyms`] for this. /// - [`Keysym`]: The key in `Keysym` form, from [xkbcommon::xkb::Keysym]. pub fn keybind(modifiers: &[Modifier], key: impl Into, mut action: F) where diff --git a/api/rust/src/lib.rs b/api/rust/src/lib.rs index bb2438b..83ce972 100644 --- a/api/rust/src/lib.rs +++ b/api/rust/src/lib.rs @@ -33,9 +33,11 @@ pub mod prelude { } use std::{ - collections::HashMap, + collections::{hash_map::Entry, HashMap}, + convert::Infallible, io::{Read, Write}, os::unix::net::UnixStream, + path::PathBuf, sync::{atomic::AtomicU32, Mutex, OnceLock}, }; @@ -134,3 +136,56 @@ fn request(request: Request) -> RequestResponse { response } + +/// Connect to Pinnacle. This needs to be called before you begin calling config functions. +/// +/// This will open up a connection to the Unix socket at `$PINNACLE_SOCKET`, +/// which should be set when you start the compositor. +pub fn connect() -> anyhow::Result<()> { + STREAM + .set(Mutex::new( + UnixStream::connect(PathBuf::from( + std::env::var("PINNACLE_SOCKET").unwrap_or("/tmp/pinnacle_socket".to_string()), + )) + .unwrap(), + )) + .unwrap(); + + Ok(()) +} + +/// Begin listening for messages coming from Pinnacle. +/// +/// This needs to be called at the very end of your `setup` function. +pub fn listen() -> Infallible { + loop { + let mut unread_callback_msgs = UNREAD_CALLBACK_MSGS.lock().unwrap(); + + for cb_id in unread_callback_msgs.keys().copied().collect::>() { + let Entry::Occupied(entry) = unread_callback_msgs.entry(cb_id) else { + unreachable!(); + }; + let IncomingMsg::CallCallback { callback_id, args } = entry.remove() else { + unreachable!(); + }; + let mut callback_vec = CALLBACK_VEC.lock().unwrap(); + let Some(callback) = callback_vec.get_mut(callback_id.0 as usize) else { + unreachable!(); + }; + callback(args); + } + + let incoming_msg = read_msg(None); + + let IncomingMsg::CallCallback { callback_id, args } = incoming_msg else { + unreachable!(); + }; + + let mut callback_vec = CALLBACK_VEC.lock().unwrap(); + let Some(callback) = callback_vec.get_mut(callback_id.0 as usize) else { + unreachable!(); + }; + + callback(args); + } +} diff --git a/api/rust/src/pinnacle.rs b/api/rust/src/pinnacle.rs index 39b3d8d..e0084cb 100644 --- a/api/rust/src/pinnacle.rs +++ b/api/rust/src/pinnacle.rs @@ -1,66 +1,8 @@ //! Functions for compositor control, like `setup` and `quit`. -use std::{ - collections::hash_map::Entry, convert::Infallible, os::unix::net::UnixStream, path::PathBuf, - sync::Mutex, -}; - -use crate::{ - msg::{IncomingMsg, Msg}, - read_msg, send_msg, CALLBACK_VEC, STREAM, UNREAD_CALLBACK_MSGS, -}; +use crate::{msg::Msg, send_msg}; /// Quit Pinnacle. pub fn quit() { send_msg(Msg::Quit).unwrap(); } - -/// Setup Pinnacle. -/// -/// This will attempt to connect to the socket at `$PINNACLE_SOCKET`, which should be set by the -/// compositor when opened. -/// -/// It will then run your `config_func`. -/// -/// Lastly, it will enter a loop to listen to messages coming from Pinnacle. -/// -/// If this function returns, an error has occurred. -pub fn setup(config_func: impl FnOnce()) -> anyhow::Result { - STREAM - .set(Mutex::new(UnixStream::connect(PathBuf::from( - std::env::var("PINNACLE_SOCKET").unwrap_or("/tmp/pinnacle_socket".to_string()), - ))?)) - .unwrap(); - - config_func(); - - loop { - let mut unread_callback_msgs = UNREAD_CALLBACK_MSGS.lock().unwrap(); - let mut callback_vec = CALLBACK_VEC.lock().unwrap(); - - for cb_id in unread_callback_msgs.keys().copied().collect::>() { - let Entry::Occupied(entry) = unread_callback_msgs.entry(cb_id) else { - unreachable!(); - }; - let IncomingMsg::CallCallback { callback_id, args } = entry.remove() else { - unreachable!(); - }; - let Some(callback) = callback_vec.get_mut(callback_id.0 as usize) else { - unreachable!(); - }; - callback(args); - } - - let incoming_msg = read_msg(None); - - let IncomingMsg::CallCallback { callback_id, args } = incoming_msg else { - unreachable!(); - }; - - let Some(callback) = callback_vec.get_mut(callback_id.0 as usize) else { - unreachable!(); - }; - - callback(args); - } -}