mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Add tag and some other stuff
This commit is contained in:
parent
c62d090f9f
commit
cd602fee09
4 changed files with 211 additions and 12 deletions
|
@ -2,7 +2,10 @@ use pinnacle_api::{Modifier, MouseButton, MouseEdge};
|
|||
|
||||
fn main() {
|
||||
pinnacle_api::setup(|pinnacle| {
|
||||
pinnacle.process.spawn(vec!["alacritty"]).unwrap();
|
||||
pinnacle.output.connect_for_all(move |output| {
|
||||
pinnacle.tag.add(&output, &["1", "2", "3", "4", "5"]);
|
||||
pinnacle.tag.get("1", Some(&output)).unwrap().toggle();
|
||||
});
|
||||
|
||||
pinnacle.input.keybind(&[Modifier::Ctrl], 'a', move || {
|
||||
pinnacle.process.spawn(vec!["alacritty"]).unwrap();
|
||||
|
|
|
@ -2,6 +2,7 @@ mod input;
|
|||
mod msg;
|
||||
mod output;
|
||||
mod process;
|
||||
mod tag;
|
||||
mod window;
|
||||
|
||||
use input::Input;
|
||||
|
@ -9,6 +10,7 @@ pub use input::MouseButton;
|
|||
pub use msg::Modifier;
|
||||
pub use msg::MouseEdge;
|
||||
use output::Output;
|
||||
use tag::Tag;
|
||||
use window::Window;
|
||||
pub use xkbcommon::xkb::keysyms;
|
||||
pub use xkbcommon::xkb::Keysym;
|
||||
|
@ -49,6 +51,7 @@ pub fn setup(config_func: impl FnOnce(Pinnacle)) -> anyhow::Result<()> {
|
|||
input: Input,
|
||||
window: Window,
|
||||
output: Output,
|
||||
tag: Tag,
|
||||
};
|
||||
|
||||
config_func(pinnacle);
|
||||
|
@ -174,4 +177,5 @@ pub struct Pinnacle {
|
|||
pub window: Window,
|
||||
pub input: Input,
|
||||
pub output: Output,
|
||||
pub tag: Tag,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{
|
||||
msg::{OutputName, Request, RequestResponse},
|
||||
request,
|
||||
msg::{Args, CallbackId, Msg, OutputName, Request, RequestResponse},
|
||||
request, send_msg,
|
||||
tag::TagHandle,
|
||||
CALLBACK_VEC,
|
||||
};
|
||||
|
||||
/// Output management.
|
||||
|
@ -46,6 +48,27 @@ impl Output {
|
|||
.map(|s| OutputHandle(OutputName(s)))
|
||||
.find(|op| op.properties().focused == Some(true))
|
||||
}
|
||||
|
||||
pub fn connect_for_all<F>(&self, mut func: F)
|
||||
where
|
||||
F: FnMut(OutputHandle) + Send + 'static,
|
||||
{
|
||||
let args_callback = move |args: Option<Args>| {
|
||||
if let Some(Args::ConnectForAllOutputs { output_name }) = args {
|
||||
func(OutputHandle(OutputName(output_name)));
|
||||
}
|
||||
};
|
||||
|
||||
let mut callback_vec = CALLBACK_VEC.lock().unwrap();
|
||||
let len = callback_vec.len();
|
||||
callback_vec.push(Box::new(args_callback));
|
||||
|
||||
let msg = Msg::ConnectForAllOutputs {
|
||||
callback_id: CallbackId(len as u32),
|
||||
};
|
||||
|
||||
send_msg(msg).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// An output handle.
|
||||
|
@ -53,31 +76,33 @@ impl Output {
|
|||
/// This is a handle to one of your monitors.
|
||||
/// It serves to make it easier to deal with them, defining methods for getting properties and
|
||||
/// helpers for things like positioning multiple monitors.
|
||||
pub struct OutputHandle(OutputName);
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct OutputHandle(pub OutputName);
|
||||
|
||||
/// Properties of an output.
|
||||
pub struct OutputProperties {
|
||||
/// The make.
|
||||
make: Option<String>,
|
||||
pub make: Option<String>,
|
||||
/// The model.
|
||||
///
|
||||
/// This is something like `27GL850` or whatever gibberish monitor manufacturers name their
|
||||
/// displays.
|
||||
model: Option<String>,
|
||||
pub model: Option<String>,
|
||||
/// The location of the output in the global space.
|
||||
loc: Option<(i32, i32)>,
|
||||
pub loc: Option<(i32, i32)>,
|
||||
/// The resolution of the output in pixels, where `res.0` is the width and `res.1` is the
|
||||
/// height.
|
||||
res: Option<(i32, i32)>,
|
||||
pub res: Option<(i32, i32)>,
|
||||
/// The refresh rate of the output in millihertz.
|
||||
///
|
||||
/// For example, 60Hz is returned as 60000.
|
||||
refresh_rate: Option<i32>,
|
||||
pub refresh_rate: Option<i32>,
|
||||
/// The physical size of the output in millimeters.
|
||||
physical_size: Option<(i32, i32)>,
|
||||
pub physical_size: Option<(i32, i32)>,
|
||||
/// Whether or not the output is focused.
|
||||
focused: Option<bool>,
|
||||
// TODO: tags
|
||||
pub focused: Option<bool>,
|
||||
/// The tags on this output.
|
||||
pub tags: Vec<TagHandle>,
|
||||
}
|
||||
|
||||
impl OutputHandle {
|
||||
|
@ -108,6 +133,11 @@ impl OutputHandle {
|
|||
refresh_rate,
|
||||
physical_size,
|
||||
focused,
|
||||
tags: tag_ids
|
||||
.unwrap_or(vec![])
|
||||
.into_iter()
|
||||
.map(TagHandle)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
162
api/rust/src/tag.rs
Normal file
162
api/rust/src/tag.rs
Normal file
|
@ -0,0 +1,162 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
msg::{Layout, Msg, OutputName, Request, RequestResponse, TagId},
|
||||
output::{Output, OutputHandle},
|
||||
request, send_msg,
|
||||
};
|
||||
|
||||
pub struct Tag;
|
||||
|
||||
impl Tag {
|
||||
/// Get a tag by its name and output. If `output` is `None`, the currently focused output will
|
||||
/// be used instead.
|
||||
///
|
||||
/// If multiple tags have the same name, this returns the first one.
|
||||
pub fn get(&self, name: &str, output: Option<&OutputHandle>) -> Option<TagHandle> {
|
||||
self.get_all()
|
||||
.filter(|tag| {
|
||||
tag.properties()
|
||||
.output
|
||||
.is_some_and(|op| Some(&op) == output)
|
||||
})
|
||||
.find(|tag| tag.properties().name.is_some_and(|s| s == name))
|
||||
}
|
||||
|
||||
/// Get all tags.
|
||||
pub fn get_all(&self) -> impl Iterator<Item = TagHandle> {
|
||||
let RequestResponse::Tags { tag_ids } = request(Request::GetTags) else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
tag_ids.into_iter().map(TagHandle)
|
||||
}
|
||||
|
||||
// TODO: return taghandles here
|
||||
/// Add tags with the names from `names` to `output`.
|
||||
pub fn add(&self, output: &OutputHandle, names: &[&str]) {
|
||||
let msg = Msg::AddTags {
|
||||
output_name: output.0.clone(),
|
||||
tag_names: names.iter().map(|s| s.to_string()).collect(),
|
||||
};
|
||||
|
||||
send_msg(msg).unwrap();
|
||||
}
|
||||
|
||||
pub fn layout_cycler(&self, layouts: &[Layout]) -> LayoutCycler {
|
||||
let mut indices = HashMap::<TagId, usize>::new();
|
||||
let layouts = layouts.to_vec();
|
||||
let len = layouts.len();
|
||||
let cycle = move |cycle: Cycle, output: Option<&OutputHandle>| {
|
||||
let Some(output) = output.cloned().or_else(|| Output.get_focused()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(tag) = output
|
||||
.properties()
|
||||
.tags
|
||||
.into_iter()
|
||||
.find(|tag| tag.properties().active == Some(true))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let index = indices.entry(tag.0).or_insert(0);
|
||||
|
||||
match cycle {
|
||||
Cycle::Forward => {
|
||||
if *index + 1 >= len {
|
||||
*index = 0;
|
||||
} else {
|
||||
*index += 1;
|
||||
}
|
||||
}
|
||||
Cycle::Backward => {
|
||||
if index.wrapping_sub(1) == usize::MAX {
|
||||
*index = len - 1;
|
||||
} else {
|
||||
*index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tag.set_layout(layouts[*index]);
|
||||
};
|
||||
|
||||
LayoutCycler {
|
||||
cycle: Box::new(cycle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which direction to cycle layouts.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Cycle {
|
||||
/// Cycle layouts forward.
|
||||
Forward,
|
||||
/// Cycle layouts backward.
|
||||
Backward,
|
||||
}
|
||||
|
||||
/// A layout cycler that keeps track of tags and their layouts and provides methods to cycle
|
||||
/// layouts on them.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct LayoutCycler {
|
||||
cycle: Box<dyn FnMut(Cycle, Option<&OutputHandle>)>,
|
||||
}
|
||||
|
||||
impl LayoutCycler {
|
||||
pub fn next(&mut self, output: Option<&OutputHandle>) {
|
||||
(self.cycle)(Cycle::Forward, output);
|
||||
}
|
||||
|
||||
pub fn prev(&mut self, output: Option<&OutputHandle>) {
|
||||
(self.cycle)(Cycle::Backward, output);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TagHandle(pub TagId);
|
||||
|
||||
pub struct TagProperties {
|
||||
active: Option<bool>,
|
||||
name: Option<String>,
|
||||
output: Option<OutputHandle>,
|
||||
}
|
||||
|
||||
impl TagHandle {
|
||||
pub fn properties(&self) -> TagProperties {
|
||||
let RequestResponse::TagProps {
|
||||
active,
|
||||
name,
|
||||
output_name,
|
||||
} = request(Request::GetTagProps { tag_id: self.0 })
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
TagProperties {
|
||||
active,
|
||||
name,
|
||||
output: output_name.map(|name| OutputHandle(OutputName(name))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(&self) {
|
||||
let msg = Msg::ToggleTag { tag_id: self.0 };
|
||||
send_msg(msg).unwrap();
|
||||
}
|
||||
|
||||
pub fn switch_to(&self) {
|
||||
let msg = Msg::SwitchToTag { tag_id: self.0 };
|
||||
send_msg(msg).unwrap();
|
||||
}
|
||||
|
||||
pub fn set_layout(&self, layout: Layout) {
|
||||
let msg = Msg::SetLayout {
|
||||
tag_id: self.0,
|
||||
layout,
|
||||
};
|
||||
|
||||
send_msg(msg).unwrap()
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue