Streamline API naming

This commit is contained in:
Ottatop 2023-07-18 15:12:23 -05:00
parent ffa847bf36
commit b2521f949c
10 changed files with 169 additions and 263 deletions

View file

@ -31,7 +31,15 @@ require("pinnacle").setup(function(pinnacle)
input.keybind({ mod_key, "Alt" }, keys.q, pinnacle.quit)
input.keybind({ mod_key, "Alt" }, keys.c, window.close_window)
input.keybind({ mod_key, "Alt" }, keys.c, function()
-- The commented out line may crash the config process if you have no windows open.
-- There is no nil warning here due to limitations in Lua LS type checking, so check for nil as shown below.
-- window.get_focused():close()
local win = window.get_focused()
if win ~= nil then
win:close()
end
end)
input.keybind({ mod_key, "Alt" }, keys.space, window.toggle_floating)
@ -72,7 +80,7 @@ require("pinnacle").setup(function(pinnacle)
local indices = {}
-- Layout cycling
-- Yes, this is very complicated and yes, I'll cook up a way to make it less complicated.
-- Yes, this is overly complicated and yes, I'll cook up a way to make it less so.
input.keybind({ mod_key }, keys.space, function()
local tags = output.get_focused():tags()
for _, tg in pairs(tags) do

View file

@ -10,7 +10,7 @@ local input = {
---Set a keybind. If called with an already existing keybind, it gets replaced.
---
---# Example
---### Example
---
---```lua
----- The following sets Super + Return to open Alacritty

View file

@ -10,9 +10,9 @@
---@field SetKeybind { key: Keys, modifiers: Modifier[], callback_id: integer }
---@field SetMousebind { button: integer }
--Windows
---@field CloseWindow { client_id: integer? }
---@field ToggleFloating { client_id: integer? }
---@field SetWindowSize { window_id: integer, size: { w: integer, h: integer } }
---@field CloseWindow { window_id: integer }
---@field ToggleFloating { window_id: integer }
---@field SetWindowSize { window_id: integer, size: integer[] }
---@field MoveWindowToTag { window_id: integer, tag_id: string }
---@field ToggleTagOnWindow { window_id: integer, tag_id: string }
--
@ -21,8 +21,8 @@
--Tags
---@field ToggleTag { output_name: string, tag_name: string }
---@field SwitchToTag { output_name: string, tag_name: string }
---@field AddTags { output_name: string, tags: string[] }
---@field RemoveTags { output_name: string, tags: string[] }
---@field AddTags { output_name: string, tag_names: string[] }
---@field RemoveTags { output_name: string, tag_names: string[] }
---@field SetLayout { output_name: string, tag_name: string, layout: Layout }
--Outputs
---@field ConnectForAllOutputs { callback_id: integer }
@ -36,12 +36,12 @@
---@field GetWindowByAppId { app_id: string }
---@field GetWindowByTitle { title: string }
--Outputs
---@field GetOutputByName { name: string }
---@field GetOutputByName { output_name: OutputName }
---@field GetOutputsByModel { model: string }
---@field GetOutputsByRes { res: integer[] }
---@field GetTagsByOutput { output: string }
---@field GetTagActive { tag_id: integer }
---@field GetTagName { tag_id: integer }
---@field GetTagsByOutput { output_name: string }
---@field GetTagActive { tag_id: TagId }
---@field GetTagName { tag_id: TagId }
---@alias Request _Request | "GetWindowByFocus" | "GetAllWindows" | "GetOutputByFocus"
@ -53,21 +53,14 @@
---@field Spawn { stdout: string?, stderr: string?, exit_code: integer?, exit_msg: string? }
---@field ConnectForAllOutputs { output_name: string }
---@alias WindowId integer
---@alias TagId integer
---@alias OutputName string
---@class RequestResponse
---@field Window { window: WindowProperties }
---@field GetAllWindows { windows: WindowProperties[] }
---@field Outputs { names: string[] }
---@field Tags { tags: TagProperties[] }
---@field Window { window_id: WindowId|nil }
---@field Windows { window_ids: WindowId[] }
---@field Outputs { output_names: OutputName[] }
---@field Tags { tag_ids: TagId[] }
---@field TagActive { active: boolean }
---@field TagName { name: string }
---@class WindowProperties
---@field id integer
---@field app_id string?
---@field title string?
---@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

@ -48,7 +48,7 @@ local output = {}
---rather, "name" is the name of the connector the output is connected to.
---This should be something like "HDMI-A-0", "eDP-1", or similar.
---
---# Examples
---### Example
---```lua
---local monitor = output.get_by_name("DP-1")
---print(monitor.name) -- should print `DP-1`
@ -58,16 +58,16 @@ local output = {}
function output.get_by_name(name)
SendRequest({
GetOutputByName = {
name = name,
output_name = name,
},
})
local response = ReadMsg()
local names = response.RequestResponse.response.Outputs.names
local output_names = response.RequestResponse.response.Outputs.output_names
if names[1] ~= nil then
return new_output({ name = names[1] })
if output_names[1] ~= nil then
return new_output({ name = output_names[1] })
else
return nil
end
@ -88,11 +88,11 @@ function output.get_by_model(model)
local response = ReadMsg()
local names = response.RequestResponse.response.Outputs.names
local output_names = response.RequestResponse.response.Outputs.output_names
---@type Output
local outputs = {}
for _, v in pairs(names) do
for _, v in pairs(output_names) do
table.insert(outputs, new_output({ name = v }))
end
@ -113,12 +113,12 @@ function output.get_by_res(width, height)
local response = ReadMsg()
local names = response.RequestResponse.response.Outputs.names
local output_names = response.RequestResponse.response.Outputs.output_names
---@type Output
local outputs = {}
for _, v in pairs(names) do
table.insert(outputs, new_output({ name = v }))
for _, output_name in pairs(output_names) do
table.insert(outputs, new_output({ name = output_name }))
end
return outputs
@ -145,16 +145,14 @@ end
---```
---@return Output|nil output The output, or nil if none are focused.
function output.get_focused()
SendMsg({
Request = "GetOutputByFocus",
})
SendRequest("GetOutputByFocus")
local response = ReadMsg()
local names = response.RequestResponse.response.Outputs.names
local output_names = response.RequestResponse.response.Outputs.output_names
if names[1] ~= nil then
return new_output({ name = names[1] })
if output_names[1] ~= nil then
return new_output({ name = output_names[1] })
else
return nil
end

View file

@ -68,7 +68,7 @@ end
---
---If you need to add the names as a table, use `tag.add_table` instead.
---
---# Example
---### Example
---
---```lua
---local output = output.get_by_name("DP-1")
@ -79,20 +79,20 @@ end
---@param output Output The output you want these tags to be added to.
---@param ... string The names of the new tags you want to add.
function tag.add(output, ...)
local tags = table.pack(...)
tags["n"] = nil
local tag_names = table.pack(...)
tag_names["n"] = nil -- remove the length to make it a true array for serializing
SendMsg({
AddTags = {
output_name = output.name,
tags = tags,
tag_names = tag_names,
},
})
end
---Like `tag.add`, but with a table of strings instead.
---
---# Example
---### Example
---
---```lua
---local tags = { "Terminal", "Browser", "Mail", "Gaming", "Potato" }
@ -107,14 +107,14 @@ function tag.add_table(output, names)
SendMsg({
AddTags = {
output_name = output.name,
tags = names,
tag_names = names,
},
})
end
---Toggle a tag on the specified output. If `output` isn't specified, toggle it on the currently focused output instead.
---
---# Example
---### Example
---
---```lua
----- Assuming all tags are toggled off...
@ -151,7 +151,7 @@ end
---
---This is used to replicate what a traditional workspace is on some other Wayland compositors.
---
---# Example
---### Example
---
---```lua
---tag.switch_to("3") -- Switches to and displays *only* windows on tag 3
@ -219,19 +219,19 @@ end
function tag.get_on_output(output)
SendRequest({
GetTagsByOutput = {
output = output.name,
output_name = output.name,
},
})
local response = ReadMsg()
local tag_props = response.RequestResponse.response.Tags.tags
local tag_ids = response.RequestResponse.response.Tags.tag_ids
---@type Tag[]
local tags = {}
for _, prop in pairs(tag_props) do
table.insert(tags, new_tag({ id = prop.id }))
for _, tag_id in pairs(tag_ids) do
table.insert(tags, new_tag({ id = tag_id }))
end
return tags

View file

@ -61,6 +61,24 @@ function win:toggle_tag(name)
})
end
---Close this window.
function win:close()
SendMsg({
CloseWindow = {
window_id = self.id,
},
})
end
---Toggle this window's floating status.
function win:toggle_floating()
SendMsg({
ToggleFloating = {
window_id = self.id,
},
})
end
---Get a window's size.
---@return { w: integer, h: integer }
function win:get_size()
@ -71,31 +89,11 @@ end
local window = {}
---Close a window.
---@param client_id integer? The id of the window you want closed, or nil to close the currently focused window, if any.
function window.close_window(client_id)
SendMsg({
CloseWindow = {
client_id = client_id,
},
})
end
---Toggle a window's floating status.
---@param client_id integer? The id of the window you want to toggle, or nil to toggle the currently focused window, if any.
function window.toggle_floating(client_id)
SendMsg({
ToggleFloating = {
client_id = client_id,
},
})
end
---TODO: This function is not implemented yet.
---
---Get a window by its app id (aka its X11 class).
---@param app_id string The window's app id. For example, Alacritty's app id is "Alacritty".
---@return Window window -- TODO: nil
---@return Window|nil
function window.get_by_app_id(app_id)
SendRequest({
GetWindowByAppId = {
@ -105,22 +103,15 @@ function window.get_by_app_id(app_id)
local response = ReadMsg()
local props = response.RequestResponse.response.Window.window
local window_id = response.RequestResponse.response.Window.window_id
if window_id == nil then
return nil
end
---@type Window
local wind = {
id = props.id,
app_id = props.app_id or "",
title = props.title or "",
size = {
w = props.size[1],
h = props.size[2],
},
location = {
x = props.location[1],
y = props.location[2],
},
floating = props.floating,
id = window_id,
}
return new_window(wind)
@ -130,7 +121,7 @@ end
---
---Get a window by its title.
---@param title string The window's title.
---@return Window
---@return Window|nil
function window.get_by_title(title)
SendRequest({
GetWindowByTitle = {
@ -140,50 +131,36 @@ function window.get_by_title(title)
local response = ReadMsg()
local props = response.RequestResponse.response.Window.window
local window_id = response.RequestResponse.response.Window.window_id
if window_id == nil then
return nil
end
---@type Window
local wind = {
id = props.id,
app_id = props.app_id or "",
title = props.title or "",
size = {
w = props.size[1],
h = props.size[2],
},
location = {
x = props.location[1],
y = props.location[2],
},
floating = props.floating,
id = window_id,
}
return new_window(wind)
end
---Get the currently focused window.
---@return Window
---@return Window|nil
function window.get_focused()
SendRequest("GetWindowByFocus")
local response = ReadMsg()
local props = response.RequestResponse.response.Window.window
local window_id = response.RequestResponse.response.Window.window_id
if window_id == nil then
return nil
end
---@type Window
local wind = {
id = props.id,
app_id = props.app_id or "",
title = props.title or "",
size = {
w = props.size[1],
h = props.size[2],
},
location = {
x = props.location[1],
y = props.location[2],
},
floating = props.floating,
id = window_id,
}
return new_window(wind)
@ -194,26 +171,11 @@ end
function window.get_all()
SendRequest("GetAllWindows")
-- INFO: these read synchronously so this should always work IF the server works correctly
local window_props = ReadMsg().RequestResponse.response.GetAllWindows.windows
local window_ids = ReadMsg().RequestResponse.response.Windows.window_ids
---@type Window[]
local windows = {}
for i, v in ipairs(window_props) do
windows[i] = {
id = v.id,
app_id = v.app_id or "",
title = v.title or "",
size = {
w = v.size[1],
h = v.size[2],
},
location = {
x = v.location[1],
y = v.location[2],
},
floating = v.floating,
}
for i, window_id in ipairs(window_ids) do
windows[i] = new_window({ id = window_id })
end
return windows
end

View file

@ -7,11 +7,7 @@
// The MessagePack format for these is a one-element map where the element's key is the enum name and its
// value is a map of the enum's values
use crate::{
layout::Layout,
tag::{TagId, TagProperties},
window::{window_state::WindowId, WindowProperties},
};
use crate::{layout::Layout, tag::TagId, window::window_state::WindowId};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32);
@ -30,12 +26,10 @@ pub enum Msg {
// Window management
CloseWindow {
#[serde(default)]
client_id: Option<u32>,
window_id: WindowId,
},
ToggleFloating {
#[serde(default)]
client_id: Option<u32>,
window_id: WindowId,
},
SetWindowSize {
window_id: WindowId,
@ -62,12 +56,12 @@ pub enum Msg {
AddTags {
/// The name of the output you want these tags on.
output_name: String,
tags: Vec<String>,
tag_names: Vec<String>,
},
RemoveTags {
/// The name of the output you want these tags removed from.
output_name: String,
tags: Vec<String>,
tag_names: Vec<String>,
},
SetLayout {
output_name: String,
@ -106,11 +100,11 @@ pub enum Request {
GetWindowByTitle { title: String },
GetWindowByFocus,
GetAllWindows,
GetOutputByName { name: String },
GetOutputByName { output_name: String },
GetOutputsByModel { model: String },
GetOutputsByRes { res: (u32, u32) },
GetOutputByFocus,
GetTagsByOutput { output: String },
GetTagsByOutput { output_name: String },
GetTagActive { tag_id: TagId },
GetTagName { tag_id: TagId },
}
@ -139,6 +133,7 @@ impl<T: IntoIterator<Item = Modifier>> From<T> for ModifierMask {
}
impl ModifierMask {
#[allow(dead_code)]
pub fn values(self) -> Vec<Modifier> {
let mut res = Vec::<Modifier>::new();
if self.0 & Modifier::Shift as u8 == Modifier::Shift as u8 {
@ -190,10 +185,10 @@ pub enum Args {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum RequestResponse {
Window { window: WindowProperties },
GetAllWindows { windows: Vec<WindowProperties> },
Outputs { names: Vec<String> },
Tags { tags: Vec<TagProperties> },
Window { window_id: Option<WindowId> },
Windows { window_ids: Vec<WindowId> },
Outputs { output_names: Vec<String> },
Tags { tag_ids: Vec<TagId> },
TagActive { active: bool },
TagName { name: String },
}

View file

@ -21,8 +21,8 @@ use crate::{
},
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
tag::{Tag, TagProperties},
window::{window_state::WindowResizeState, WindowProperties},
tag::Tag,
window::window_state::WindowResizeState,
};
use calloop::futures::Scheduler;
use futures_lite::AsyncBufReadExt;
@ -55,7 +55,7 @@ use smithay::{
dmabuf::DmabufFeedback,
fractional_scale::FractionalScaleManagerState,
output::OutputManagerState,
shell::xdg::{XdgShellState, XdgToplevelSurfaceData},
shell::xdg::XdgShellState,
shm::ShmState,
socket::ListeningSocketSource,
viewporter::ViewporterState,
@ -117,17 +117,22 @@ impl<B: Backend> State<B> {
.keybinds
.insert((modifiers.into(), key), callback_id);
}
Msg::SetMousebind { button } => todo!(),
Msg::CloseWindow { client_id } => {
// TODO: client_id
tracing::info!("CloseWindow {:?}", client_id);
if let Some(window) = self.focus_state.current_focus() {
Msg::SetMousebind { button: _ } => todo!(),
Msg::CloseWindow { window_id } => {
if let Some(window) = self
.windows
.iter()
.find(|win| win.with_state(|state| state.id == window_id))
{
window.toplevel().send_close();
}
}
Msg::ToggleFloating { client_id } => {
// TODO: add client_ids
if let Some(window) = self.focus_state.current_focus() {
Msg::ToggleFloating { window_id } => {
if let Some(window) = self
.windows
.iter()
.find(|win| win.with_state(|state| state.id == window_id)).cloned()
{
crate::window::toggle_floating(self, &window);
}
}
@ -247,7 +252,7 @@ impl<B: Backend> State<B> {
}
}
// TODO: add output
Msg::AddTags { output_name, tags } => {
Msg::AddTags { output_name, tag_names } => {
if let Some(output) = self
.space
.outputs()
@ -256,19 +261,19 @@ impl<B: Backend> State<B> {
output.with_state(|state| {
state
.tags
.extend(tags.iter().cloned().map(Tag::new));
.extend(tag_names.iter().cloned().map(Tag::new));
tracing::debug!("tags added, are now {:?}", state.tags);
});
}
}
Msg::RemoveTags { output_name, tags } => {
Msg::RemoveTags { output_name, tag_names } => {
if let Some(output) = self
.space
.outputs()
.find(|output| output.name() == output_name)
{
output.with_state(|state| {
state.tags.retain(|tag| !tags.contains(&tag.name()));
state.tags.retain(|tag| !tag_names.contains(&tag.name()));
});
}
}
@ -319,70 +324,38 @@ impl<B: Backend> State<B> {
.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::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
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData doesn't exist")
.lock()
.expect("Couldn't lock XdgToplevelSurfaceData");
(lock.app_id.clone(), lock.title.clone())
});
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,
};
match self.focus_state.current_focus() {
Some(current_focus) => {
let window_id =
current_focus.with_state(|state| state.id);
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Window { window: props },
response: RequestResponse::Window { window_id: Some(window_id) },
},
)
.expect("Send to client failed");
},
None => {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Window { window_id: None },
},
)
.expect("Send to client failed");
},
}
}
Request::GetAllWindows => {
let window_props = self
.space
.elements()
let window_ids = self
.windows
.iter()
.map(|win| {
let (app_id, title) =
compositor::with_states(win.toplevel().wl_surface(), |states| {
let lock = states
.data_map
.get::<XdgToplevelSurfaceData>()
.expect("XdgToplevelSurfaceData doesn't exist")
.lock()
.expect("Couldn't lock XdgToplevelSurfaceData");
(lock.app_id.clone(), lock.title.clone())
});
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,
}
win.with_state(|state| state.id)
})
.collect::<Vec<_>>();
@ -390,25 +363,25 @@ impl<B: Backend> State<B> {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::GetAllWindows {
windows: window_props,
response: RequestResponse::Windows {
window_ids,
},
},
)
.expect("Couldn't send to client");
}
Request::GetOutputByName { name } => {
Request::GetOutputByName { output_name } => {
// TODO: name better
let names = self
.space
.outputs()
.find(|output| output.name() == name)
.find(|output| output.name() == output_name)
.map(|output| output.name());
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs {
names: if let Some(name) = names {
output_names: if let Some(name) = names {
vec![name]
} else {
vec![]
@ -428,7 +401,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
response: RequestResponse::Outputs { output_names: names },
},
)
.unwrap();
@ -452,7 +425,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
response: RequestResponse::Outputs { output_names: names },
},
)
.unwrap();
@ -468,27 +441,27 @@ impl<B: Backend> State<B> {
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Outputs { names },
response: RequestResponse::Outputs { output_names: names },
},
)
.unwrap();
}
Request::GetTagsByOutput { output } => {
Request::GetTagsByOutput { output_name } => {
let output = self
.space
.outputs()
.find(|op| op.name() == output);
.find(|op| op.name() == output_name);
if let Some(output) = output {
let tag_props = output.with_state(|state| {
let tag_ids = output.with_state(|state| {
state.tags
.iter()
.map(|tag| TagProperties { id: tag.id() })
.map(|tag| tag.id())
.collect::<Vec<_>>()
});
crate::api::send_to_client(
&mut stream,
&OutgoingMsg::RequestResponse {
response: RequestResponse::Tags { tags: tag_props }
response: RequestResponse::Tags { tag_ids }
}).unwrap();
}
}
@ -747,15 +720,9 @@ pub fn schedule_on_commit<F, B: Backend>(data: &mut CalloopData<B>, windows: Vec
where
F: FnOnce(&mut CalloopData<B>) + 'static,
{
// tracing::debug!("scheduling on_commit");
// tracing::debug!("win len is {}", windows.len());
for window in windows.iter() {
window.with_state(|state| {
// tracing::debug!("win state is {:?}", state.resize_state);
});
if window.with_state(|state| !matches!(state.resize_state, WindowResizeState::Idle))
{
// tracing::debug!("some windows not idle");
data.state.loop_handle.insert_idle(|data| {
schedule_on_commit(data, windows, on_commit);
});

View file

@ -90,11 +90,6 @@ 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

View file

@ -23,7 +23,7 @@ use crate::{
state::{State, WithState},
};
use self::window_state::{Float, WindowId};
use self::window_state::Float;
pub mod window_state;
@ -116,18 +116,6 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &Window) {
});
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct WindowProperties {
pub id: WindowId,
pub app_id: Option<String>,
pub title: Option<String>,
/// Width and height
pub size: (i32, i32),
/// x and y
pub location: (i32, i32),
pub floating: bool,
}
pub struct WindowBlocker;
pub static BLOCKER_COUNTER: AtomicU32 = AtomicU32::new(0);