mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-14 08:01:14 +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() {
|
fn main() {
|
||||||
pinnacle_api::setup(|pinnacle| {
|
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.input.keybind(&[Modifier::Ctrl], 'a', move || {
|
||||||
pinnacle.process.spawn(vec!["alacritty"]).unwrap();
|
pinnacle.process.spawn(vec!["alacritty"]).unwrap();
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod input;
|
||||||
mod msg;
|
mod msg;
|
||||||
mod output;
|
mod output;
|
||||||
mod process;
|
mod process;
|
||||||
|
mod tag;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use input::Input;
|
use input::Input;
|
||||||
|
@ -9,6 +10,7 @@ pub use input::MouseButton;
|
||||||
pub use msg::Modifier;
|
pub use msg::Modifier;
|
||||||
pub use msg::MouseEdge;
|
pub use msg::MouseEdge;
|
||||||
use output::Output;
|
use output::Output;
|
||||||
|
use tag::Tag;
|
||||||
use window::Window;
|
use window::Window;
|
||||||
pub use xkbcommon::xkb::keysyms;
|
pub use xkbcommon::xkb::keysyms;
|
||||||
pub use xkbcommon::xkb::Keysym;
|
pub use xkbcommon::xkb::Keysym;
|
||||||
|
@ -49,6 +51,7 @@ pub fn setup(config_func: impl FnOnce(Pinnacle)) -> anyhow::Result<()> {
|
||||||
input: Input,
|
input: Input,
|
||||||
window: Window,
|
window: Window,
|
||||||
output: Output,
|
output: Output,
|
||||||
|
tag: Tag,
|
||||||
};
|
};
|
||||||
|
|
||||||
config_func(pinnacle);
|
config_func(pinnacle);
|
||||||
|
@ -174,4 +177,5 @@ pub struct Pinnacle {
|
||||||
pub window: Window,
|
pub window: Window,
|
||||||
pub input: Input,
|
pub input: Input,
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
|
pub tag: Tag,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
msg::{OutputName, Request, RequestResponse},
|
msg::{Args, CallbackId, Msg, OutputName, Request, RequestResponse},
|
||||||
request,
|
request, send_msg,
|
||||||
|
tag::TagHandle,
|
||||||
|
CALLBACK_VEC,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Output management.
|
/// Output management.
|
||||||
|
@ -46,6 +48,27 @@ impl Output {
|
||||||
.map(|s| OutputHandle(OutputName(s)))
|
.map(|s| OutputHandle(OutputName(s)))
|
||||||
.find(|op| op.properties().focused == Some(true))
|
.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.
|
/// An output handle.
|
||||||
|
@ -53,31 +76,33 @@ impl Output {
|
||||||
/// This is a handle to one of your monitors.
|
/// 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
|
/// It serves to make it easier to deal with them, defining methods for getting properties and
|
||||||
/// helpers for things like positioning multiple monitors.
|
/// 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.
|
/// Properties of an output.
|
||||||
pub struct OutputProperties {
|
pub struct OutputProperties {
|
||||||
/// The make.
|
/// The make.
|
||||||
make: Option<String>,
|
pub make: Option<String>,
|
||||||
/// The model.
|
/// The model.
|
||||||
///
|
///
|
||||||
/// This is something like `27GL850` or whatever gibberish monitor manufacturers name their
|
/// This is something like `27GL850` or whatever gibberish monitor manufacturers name their
|
||||||
/// displays.
|
/// displays.
|
||||||
model: Option<String>,
|
pub model: Option<String>,
|
||||||
/// The location of the output in the global space.
|
/// 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
|
/// The resolution of the output in pixels, where `res.0` is the width and `res.1` is the
|
||||||
/// height.
|
/// height.
|
||||||
res: Option<(i32, i32)>,
|
pub res: Option<(i32, i32)>,
|
||||||
/// The refresh rate of the output in millihertz.
|
/// The refresh rate of the output in millihertz.
|
||||||
///
|
///
|
||||||
/// For example, 60Hz is returned as 60000.
|
/// For example, 60Hz is returned as 60000.
|
||||||
refresh_rate: Option<i32>,
|
pub refresh_rate: Option<i32>,
|
||||||
/// The physical size of the output in millimeters.
|
/// 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.
|
/// Whether or not the output is focused.
|
||||||
focused: Option<bool>,
|
pub focused: Option<bool>,
|
||||||
// TODO: tags
|
/// The tags on this output.
|
||||||
|
pub tags: Vec<TagHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputHandle {
|
impl OutputHandle {
|
||||||
|
@ -108,6 +133,11 @@ impl OutputHandle {
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
physical_size,
|
physical_size,
|
||||||
focused,
|
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