Add more stuff to API

This commit is contained in:
Ottatop 2023-07-18 10:31:08 -05:00 committed by Ottatop
parent f6784da8a8
commit b1ee8e03c1
7 changed files with 269 additions and 172 deletions

View file

@ -51,17 +51,11 @@ require("pinnacle").setup(function(pinnacle)
process.spawn("nautilus")
end)
input.keybind({ mod_key }, keys.g, function()
local op = output.get_by_res(2560, 1440)
for _, v in pairs(op) do
print(v.name)
end
end)
-- Tags ---------------------------------------------------------------------------
output.connect_for_all(function(op)
tag.add(op, "1", "2", "3", "4", "5")
op:add_tags("1", "2", "3", "4", "5")
-- Same as tag.add(op, "1", "2", "3", "4", "5")
tag.toggle("1", op)
end)
@ -85,6 +79,14 @@ require("pinnacle").setup(function(pinnacle)
index = index + 1
end
end)
input.keybind({ mod_key, "Shift" }, keys.space, function()
tag.set_layout("1", layouts[index])
if index - 1 < 1 then
index = #layouts
else
index = index - 1
end
end)
input.keybind({ mod_key }, keys.KEY_1, function()
tag.switch_to("1")

View file

@ -39,6 +39,7 @@
---@field GetOutputByName { name: string }
---@field GetOutputsByModel { model: string }
---@field GetOutputsByRes { res: integer[] }
---@field GetTagsByOutput { output: string }
---@alias Request _Request | "GetWindowByFocus" | "GetAllWindows" | "GetOutputByFocus"
@ -54,6 +55,7 @@
---@field Window { window: WindowProperties }
---@field GetAllWindows { windows: WindowProperties[] }
---@field Outputs { names: string[] }
---@field Tags { tags: TagProperties[] }
---@class WindowProperties
---@field id integer
@ -62,3 +64,6 @@
---@field size integer[] A two element int array, \[1\] = w, \[2\] = h
---@field location integer[] A two element int array, \[1\] = x, \[2\] = y
---@field floating boolean
---@class TagProperties
---@field id integer

View file

@ -8,6 +8,24 @@
---@field name string The name of this output (or rather, of its connector).
local op = {}
---Get all tags on this output. See `tag.get_on_output`.
---@return Tag[]
function op:tags()
return require("tag").get_on_output(self)
end
---Add tags to this output. See `tag.add`.
---@param ... string The names of the tags you want to add.
function op:add_tags(...)
require("tag").add(self, ...)
end
---Add tags to this output as a table. See `tag.add_table`.
---@param names string[] The names of the tags you want to add, as a table.
function op:add_tags_table(names)
require("tag").add_table(self, names)
end
---Add methods to this output.
---@param props Output
---@return Output
@ -57,7 +75,7 @@ function output.get_by_name(name)
end
end
---NOTE: This may or may not be what is reported by other monitor listing utilities. Pinnacle currently fails to pick up one of my monitors' models when it is correctly picked up by tools like wlr-randr. I'll fix this in the future.
---Note: This may or may not be what is reported by other monitor listing utilities. Pinnacle currently fails to pick up one of my monitors' models when it is correctly picked up by tools like wlr-randr. I'll fix this in the future.
---
---Get outputs by their model.
---This is something like "DELL E2416H" or whatever gibberish monitor manufacturers call their displays.
@ -113,6 +131,24 @@ function output.get_by_res(width, height)
end
---Get the currently focused output. This is currently implemented as the one with the cursor on it.
---
---This function may return nil, which means you may get a warning if you try to use it without checking for nil.
---Usually this function will not be nil unless you unplug all monitors, so instead of checking,
---you can ignore the warning by either forcing the type to be non-nil with an inline comment:
---```lua
---local op = output.get_focused() --[[@as Output]]
---```
---or by disabling nil check warnings for the line:
---```lua
---local op = output.get_focused()
------@diagnostic disable-next-line:need-check-nil
---local tags_on_output = op:tags()
---```
---Type checking done by Lua LS isn't perfect.
---Note that directly using the result of this function inline will *not* raise a warning, so be careful.
---```lua
---local tags = output.get_focused():tags() -- will NOT warn for nil
---```
---@return Output|nil output The output, or nil if none are focused.
function output.get_focused()
SendMsg({

View file

@ -13,6 +13,21 @@
---| "CornerBottomLeft" # One main corner window in the bottom left with a column of windows on the right and a row on the top.
---| "CornerBottomRight" # One main corner window in the bottom right with a column of windows on the left and a row on the top.
---@class Tag
---@field private id integer The internal id of this tag.
local tg = {}
---@param props Tag
---@return Tag
local function new_tag(props)
-- Copy functions over
for k, v in pairs(tg) do
props[k] = v
end
return props
end
local tag = {}
---Add tags.
@ -53,12 +68,12 @@ end
---end
---```
---@param output Output The output you want these tags to be added to.
---@param tags string[] The names of the new tags you want to add, as a table.
function tag.add_table(output, tags)
---@param names string[] The names of the new tags you want to add, as a table.
function tag.add_table(output, names)
SendMsg({
AddTags = {
output_name = output.name,
tags = tags,
tags = names,
},
})
end
@ -156,4 +171,38 @@ function tag.set_layout(name, layout, output)
end
end
end
---Get all tags on the specified output.
---
---You can also use `output_obj:tags()`, which delegates to this function:
---```lua
---local tags_on_output = output.get_focused():tags()
----- This is the same as
----- local tags_on_output = tag.get_on_output(output.get_focused())
---```
---@param output Output
---@return Tag[]
function tag.get_on_output(output)
SendMsg({
Request = {
GetTagsByOutput = {
output = output.name,
},
},
})
local response = ReadMsg()
local tag_props = response.RequestResponse.response.Tags.tags
---@type Tag[]
local tags = {}
for _, prop in pairs(tag_props) do
table.insert(tags, new_tag({ id = prop.id }))
end
return tags
end
return tag

View file

@ -9,6 +9,7 @@
use crate::{
layout::Layout,
tag::TagProperties,
window::{window_state::WindowId, WindowProperties},
};
@ -109,6 +110,7 @@ pub enum Request {
GetOutputsByModel { model: String },
GetOutputsByRes { res: (u32, u32) },
GetOutputByFocus,
GetTagsByOutput { output: String },
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
@ -189,4 +191,5 @@ pub enum RequestResponse {
Window { window: WindowProperties },
GetAllWindows { windows: Vec<WindowProperties> },
Outputs { names: Vec<String> },
Tags { tags: Vec<TagProperties> },
}

View file

@ -21,7 +21,7 @@ use crate::{
},
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
tag::Tag,
tag::{Tag, TagProperties},
window::{window_state::WindowResizeState, WindowProperties},
};
use calloop::futures::Scheduler;
@ -310,12 +310,19 @@ impl<B: Backend> State<B> {
self.loop_signal.stop();
}
Msg::Request(request) => match request {
Request::GetWindowByAppId { app_id } => todo!(),
Request::GetWindowByTitle { title } => todo!(),
Request::GetWindowByFocus => {
let Some(current_focus) = self.focus_state.current_focus() else { return; };
let (app_id, title) =
Msg::Request(request) => {
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
match request {
Request::GetWindowByAppId { app_id } => todo!(),
Request::GetWindowByTitle { title } => todo!(),
Request::GetWindowByFocus => {
let Some(current_focus) = self.focus_state.current_focus() else { return; };
let (app_id, title) =
compositor::with_states(current_focus.toplevel().wl_surface(), |states| {
let lock = states
.data_map
@ -325,38 +332,32 @@ impl<B: Backend> State<B> {
.expect("Couldn't lock XdgToplevelSurfaceData");
(lock.app_id.clone(), lock.title.clone())
});
let (window_id, floating) =
let (window_id, floating) =
current_focus.with_state(|state| (state.id, state.floating.is_floating()));
// TODO: unwrap
let location = self.space.element_location(&current_focus).unwrap();
let props = WindowProperties {
id: window_id,
app_id,
title,
size: current_focus.geometry().size.into(),
location: location.into(),
floating,
};
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Window { window: props },
},
)
.expect("Send to client failed");
}
Request::GetAllWindows => {
let window_props = self
.space
.elements()
.map(|win| {
let (app_id, title) =
// TODO: unwrap
let location = self.space.element_location(&current_focus).unwrap();
let props = WindowProperties {
id: window_id,
app_id,
title,
size: current_focus.geometry().size.into(),
location: location.into(),
floating,
};
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Window { window: props },
},
)
.expect("Send to client failed");
}
Request::GetAllWindows => {
let window_props = self
.space
.elements()
.map(|win| {
let (app_id, title) =
compositor::with_states(win.toplevel().wl_surface(), |states| {
let lock = states
.data_map
@ -366,134 +367,130 @@ impl<B: Backend> State<B> {
.expect("Couldn't lock XdgToplevelSurfaceData");
(lock.app_id.clone(), lock.title.clone())
});
let (window_id, floating) =
let (window_id, floating) =
win.with_state(|state| (state.id, state.floating.is_floating()));
// TODO: unwrap
let location = self
.space
.element_location(win)
.expect("Window location doesn't exist");
WindowProperties {
id: window_id,
app_id,
title,
size: win.geometry().size.into(),
location: location.into(),
floating,
}
})
.collect::<Vec<_>>();
// TODO: unwrap
let location = self
.space
.element_location(win)
.expect("Window location doesn't exist");
WindowProperties {
id: window_id,
app_id,
title,
size: win.geometry().size.into(),
location: location.into(),
floating,
}
})
.collect::<Vec<_>>();
// FIXME: figure out what to do if error
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::GetAllWindows {
windows: window_props,
// FIXME: figure out what to do if error
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::GetAllWindows {
windows: window_props,
},
},
},
)
.expect("Couldn't send to client");
}
Request::GetOutputByName { name } => {
let names = self
.space
.outputs()
.filter(|output| output.name() == name)
.map(|output| output.name())
.collect::<Vec<_>>();
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetOutputsByModel { model } => {
let names = self
.space
.outputs()
.filter(|output| output.physical_properties().model == model)
.map(|output| output.name())
.collect::<Vec<_>>();
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetOutputsByRes { res } => {
let names = self
.space
.outputs()
.filter_map(|output| {
if let Some(mode) = output.current_mode() {
if mode.size == (res.0 as i32, res.1 as i32).into() {
Some(output.name())
)
.expect("Couldn't send to client");
}
Request::GetOutputByName { name } => {
// TODO: name better
let names = self
.space
.outputs()
.find(|output| output.name() == name)
.map(|output| output.name());
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs {
names: if let Some(name) = names {
vec![name]
} else {
vec![]
}
},
},
)
.unwrap();
}
Request::GetOutputsByModel { model } => {
let names = self
.space
.outputs()
.filter(|output| output.physical_properties().model == model)
.map(|output| output.name())
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetOutputsByRes { res } => {
let names = self
.space
.outputs()
.filter_map(|output| {
if let Some(mode) = output.current_mode() {
if mode.size == (res.0 as i32, res.1 as i32).into() {
Some(output.name())
} else {
None
}
} else {
None
}
} else {
None
}
})
.collect::<Vec<_>>();
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetOutputByFocus => {
let names = self
.focus_state
.focused_output
.as_ref()
.map(|output| output.name())
.into_iter()
.collect::<Vec<_>>();
let stream = self
.api_state
.stream
.as_ref()
.expect("Stream doesn't exist");
let mut stream = stream.lock().expect("Couldn't lock stream");
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
})
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetOutputByFocus => {
let names = self
.focus_state
.focused_output
.as_ref()
.map(|output| output.name())
.into_iter()
.collect::<Vec<_>>();
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
},
)
.unwrap();
}
Request::GetTagsByOutput { output } => {
let output = self
.space
.outputs()
.find(|op| op.name() == output);
if let Some(output) = output {
let tag_props = output.with_state(|state| {
state.tags
.iter()
.map(|tag| TagProperties { id: tag.id() })
.collect::<Vec<_>>()
});
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Tags { tags: tag_props }
}).unwrap();
}
}
}
},
}

View file

@ -90,6 +90,11 @@ impl Tag {
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TagProperties {
pub id: TagId,
}
impl<B: Backend> State<B> {
pub fn output_for_tag(&self, tag: &Tag) -> Option<Output> {
self.space