mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Add per tag layouts
This commit is contained in:
parent
92b10121a3
commit
a6a62be446
9 changed files with 170 additions and 51 deletions
|
@ -69,22 +69,46 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
"CornerBottomLeft",
|
"CornerBottomLeft",
|
||||||
"CornerBottomRight",
|
"CornerBottomRight",
|
||||||
}
|
}
|
||||||
local index = 1
|
local indices = {}
|
||||||
|
|
||||||
|
-- Layout cycling
|
||||||
|
-- Yes, this is very complicated and yes, I'll cook up a way to make it less complicated.
|
||||||
input.keybind({ mod_key }, keys.space, function()
|
input.keybind({ mod_key }, keys.space, function()
|
||||||
tag.set_layout("1", layouts[index])
|
local tags = output.get_focused():tags()
|
||||||
if index + 1 > #layouts then
|
for _, tg in pairs(tags) do
|
||||||
index = 1
|
if tg:active() then
|
||||||
else
|
local name = tg:name()
|
||||||
index = index + 1
|
tg:set_layout(layouts[indices[name] or 1])
|
||||||
|
if indices[name] == nil then
|
||||||
|
indices[name] = 2
|
||||||
|
else
|
||||||
|
if indices[name] + 1 > #layouts then
|
||||||
|
indices[name] = 1
|
||||||
|
else
|
||||||
|
indices[name] = indices[name] + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
input.keybind({ mod_key, "Shift" }, keys.space, function()
|
input.keybind({ mod_key, "Shift" }, keys.space, function()
|
||||||
tag.set_layout("1", layouts[index])
|
local tags = output.get_focused():tags()
|
||||||
if index - 1 < 1 then
|
for _, tg in pairs(tags) do
|
||||||
index = #layouts
|
if tg:active() then
|
||||||
else
|
local name = tg:name()
|
||||||
index = index - 1
|
tg:set_layout(layouts[indices[name] or #layouts])
|
||||||
|
if indices[name] == nil then
|
||||||
|
indices[name] = #layouts - 1
|
||||||
|
else
|
||||||
|
if indices[name] - 1 < 1 then
|
||||||
|
indices[name] = #layouts
|
||||||
|
else
|
||||||
|
indices[name] = indices[name] - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
---@field GetOutputsByModel { model: string }
|
---@field GetOutputsByModel { model: string }
|
||||||
---@field GetOutputsByRes { res: integer[] }
|
---@field GetOutputsByRes { res: integer[] }
|
||||||
---@field GetTagsByOutput { output: string }
|
---@field GetTagsByOutput { output: string }
|
||||||
|
---@field GetTagActive { tag_id: integer }
|
||||||
|
---@field GetTagName { tag_id: integer }
|
||||||
|
|
||||||
---@alias Request _Request | "GetWindowByFocus" | "GetAllWindows" | "GetOutputByFocus"
|
---@alias Request _Request | "GetWindowByFocus" | "GetAllWindows" | "GetOutputByFocus"
|
||||||
|
|
||||||
|
@ -56,6 +58,8 @@
|
||||||
---@field GetAllWindows { windows: WindowProperties[] }
|
---@field GetAllWindows { windows: WindowProperties[] }
|
||||||
---@field Outputs { names: string[] }
|
---@field Outputs { names: string[] }
|
||||||
---@field Tags { tags: TagProperties[] }
|
---@field Tags { tags: TagProperties[] }
|
||||||
|
---@field TagActive { active: boolean }
|
||||||
|
---@field TagName { name: string }
|
||||||
|
|
||||||
---@class WindowProperties
|
---@class WindowProperties
|
||||||
---@field id integer
|
---@field id integer
|
||||||
|
|
|
@ -56,11 +56,9 @@ local output = {}
|
||||||
---@param name string The name of the output.
|
---@param name string The name of the output.
|
||||||
---@return Output|nil
|
---@return Output|nil
|
||||||
function output.get_by_name(name)
|
function output.get_by_name(name)
|
||||||
SendMsg({
|
SendRequest({
|
||||||
Request = {
|
GetOutputByName = {
|
||||||
GetOutputByName = {
|
name = name,
|
||||||
name = name,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -82,11 +80,9 @@ end
|
||||||
---@param model string The model of the output(s).
|
---@param model string The model of the output(s).
|
||||||
---@return Output[] outputs All outputs with this model. If there are none, the returned table will be empty.
|
---@return Output[] outputs All outputs with this model. If there are none, the returned table will be empty.
|
||||||
function output.get_by_model(model)
|
function output.get_by_model(model)
|
||||||
SendMsg({
|
SendRequest({
|
||||||
Request = {
|
GetOutputsByModel = {
|
||||||
GetOutputsByModel = {
|
model = model,
|
||||||
model = model,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -109,11 +105,9 @@ end
|
||||||
---@param height integer The height of the outputs, in pixels.
|
---@param height integer The height of the outputs, in pixels.
|
||||||
---@return Output[] outputs All outputs with this resolution. If there are none, the returned table will be empty.
|
---@return Output[] outputs All outputs with this resolution. If there are none, the returned table will be empty.
|
||||||
function output.get_by_res(width, height)
|
function output.get_by_res(width, height)
|
||||||
SendMsg({
|
SendRequest({
|
||||||
Request = {
|
GetOutputsByRes = {
|
||||||
GetOutputsByRes = {
|
res = { width, height },
|
||||||
res = { width, height },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ local function read_exact(socket_fd, count)
|
||||||
local len_to_read = count
|
local len_to_read = count
|
||||||
local data = ""
|
local data = ""
|
||||||
while len_to_read > 0 do
|
while len_to_read > 0 do
|
||||||
|
-- print("need to read " .. tostring(len_to_read) .. " bytes")
|
||||||
local bytes, err_msg, errnum = socket.recv(socket_fd, len_to_read)
|
local bytes, err_msg, errnum = socket.recv(socket_fd, len_to_read)
|
||||||
|
|
||||||
if bytes == nil then
|
if bytes == nil then
|
||||||
|
@ -105,9 +106,11 @@ function pinnacle.setup(config_func)
|
||||||
|
|
||||||
---@type integer
|
---@type integer
|
||||||
local msg_len = string.unpack("=I4", msg_len_bytes)
|
local msg_len = string.unpack("=I4", msg_len_bytes)
|
||||||
|
-- print(msg_len)
|
||||||
|
|
||||||
local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len)
|
local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len)
|
||||||
assert(msg_bytes)
|
assert(msg_bytes)
|
||||||
|
-- print(msg_bytes)
|
||||||
|
|
||||||
---@type IncomingMsg
|
---@type IncomingMsg
|
||||||
local tb = msgpack.decode(msg_bytes)
|
local tb = msgpack.decode(msg_bytes)
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
--
|
--
|
||||||
-- SPDX-License-Identifier: MPL-2.0
|
-- SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
local tag = {}
|
||||||
|
|
||||||
---@alias Layout
|
---@alias Layout
|
||||||
---| "MasterStack" # One master window on the left with all other windows stacked to the right.
|
---| "MasterStack" # One master window on the left with all other windows stacked to the right.
|
||||||
---| "Dwindle" # Windows split in half towards the bottom right corner.
|
---| "Dwindle" # Windows split in half towards the bottom right corner.
|
||||||
|
@ -28,7 +30,39 @@ local function new_tag(props)
|
||||||
return props
|
return props
|
||||||
end
|
end
|
||||||
|
|
||||||
local tag = {}
|
---Get this tag's active status.
|
||||||
|
---@return boolean active True if the tag is active, otherwise false.
|
||||||
|
function tg:active()
|
||||||
|
SendRequest({
|
||||||
|
GetTagActive = {
|
||||||
|
tag_id = self.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local response = ReadMsg()
|
||||||
|
local active = response.RequestResponse.response.TagActive.active
|
||||||
|
return active
|
||||||
|
end
|
||||||
|
|
||||||
|
function tg:name()
|
||||||
|
SendRequest({
|
||||||
|
GetTagName = {
|
||||||
|
tag_id = self.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
local response = ReadMsg()
|
||||||
|
local name = response.RequestResponse.response.TagName.name
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
---Set this tag's layout.
|
||||||
|
---@param layout Layout
|
||||||
|
function tg:set_layout(layout) -- TODO: output param
|
||||||
|
tag.set_layout(self:name(), layout)
|
||||||
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
---Add tags.
|
---Add tags.
|
||||||
---
|
---
|
||||||
|
@ -183,11 +217,9 @@ end
|
||||||
---@param output Output
|
---@param output Output
|
||||||
---@return Tag[]
|
---@return Tag[]
|
||||||
function tag.get_on_output(output)
|
function tag.get_on_output(output)
|
||||||
SendMsg({
|
SendRequest({
|
||||||
Request = {
|
GetTagsByOutput = {
|
||||||
GetTagsByOutput = {
|
output = output.name,
|
||||||
output = output.name,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ pub fn send_to_client(
|
||||||
stream: &mut UnixStream,
|
stream: &mut UnixStream,
|
||||||
msg: &OutgoingMsg,
|
msg: &OutgoingMsg,
|
||||||
) -> Result<(), rmp_serde::encode::Error> {
|
) -> Result<(), rmp_serde::encode::Error> {
|
||||||
|
// tracing::debug!("Sending {msg:?}");
|
||||||
let msg = rmp_serde::to_vec_named(msg)?;
|
let msg = rmp_serde::to_vec_named(msg)?;
|
||||||
let msg_len = msg.len() as u32;
|
let msg_len = msg.len() as u32;
|
||||||
let bytes = msg_len.to_ne_bytes();
|
let bytes = msg_len.to_ne_bytes();
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::Layout,
|
layout::Layout,
|
||||||
tag::TagProperties,
|
tag::{TagId, TagProperties},
|
||||||
window::{window_state::WindowId, WindowProperties},
|
window::{window_state::WindowId, WindowProperties},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +111,8 @@ pub enum Request {
|
||||||
GetOutputsByRes { res: (u32, u32) },
|
GetOutputsByRes { res: (u32, u32) },
|
||||||
GetOutputByFocus,
|
GetOutputByFocus,
|
||||||
GetTagsByOutput { output: String },
|
GetTagsByOutput { output: String },
|
||||||
|
GetTagActive { tag_id: TagId },
|
||||||
|
GetTagName { tag_id: TagId },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -192,4 +194,6 @@ pub enum RequestResponse {
|
||||||
GetAllWindows { windows: Vec<WindowProperties> },
|
GetAllWindows { windows: Vec<WindowProperties> },
|
||||||
Outputs { names: Vec<String> },
|
Outputs { names: Vec<String> },
|
||||||
Tags { tags: Vec<TagProperties> },
|
Tags { tags: Vec<TagProperties> },
|
||||||
|
TagActive { active: bool },
|
||||||
|
TagName { name: String },
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,8 +167,10 @@ impl Layout {
|
||||||
Slice::Right => {
|
Slice::Right => {
|
||||||
let width_partition = win1_size.w / 2;
|
let width_partition = win1_size.w / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w - width_partition, win1_size.h).into());
|
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -177,7 +179,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((width_partition, win1_size.h).into());
|
state.size =
|
||||||
|
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -190,8 +193,10 @@ impl Layout {
|
||||||
Slice::Below => {
|
Slice::Below => {
|
||||||
let height_partition = win1_size.h / 2;
|
let height_partition = win1_size.h / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w, win1_size.h - height_partition).into());
|
(win1_size.w, i32::max(win1_size.h - height_partition, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -200,7 +205,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((win1_size.w, height_partition).into());
|
state.size =
|
||||||
|
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -273,8 +279,10 @@ impl Layout {
|
||||||
Slice::Above => {
|
Slice::Above => {
|
||||||
let height_partition = win1_size.h / 2;
|
let height_partition = win1_size.h / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w, win1_size.h - height_partition).into());
|
(win1_size.w, i32::max(win1_size.h - height_partition, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -283,7 +291,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((win1_size.w, height_partition).into());
|
state.size =
|
||||||
|
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -295,8 +304,10 @@ impl Layout {
|
||||||
Slice::Below => {
|
Slice::Below => {
|
||||||
let height_partition = win1_size.h / 2;
|
let height_partition = win1_size.h / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w, win1_size.h - height_partition).into());
|
(win1_size.w, win1_size.h - i32::max(height_partition, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -305,7 +316,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((win1_size.w, height_partition).into());
|
state.size =
|
||||||
|
Some((win1_size.w, i32::max(height_partition, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -318,8 +330,10 @@ impl Layout {
|
||||||
Slice::Left => {
|
Slice::Left => {
|
||||||
let width_partition = win1_size.w / 2;
|
let width_partition = win1_size.w / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w - width_partition, win1_size.h).into());
|
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -328,7 +342,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((width_partition, win1_size.h).into());
|
state.size =
|
||||||
|
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -340,8 +355,10 @@ impl Layout {
|
||||||
Slice::Right => {
|
Slice::Right => {
|
||||||
let width_partition = win1_size.w / 2;
|
let width_partition = win1_size.w / 2;
|
||||||
win1.toplevel().with_pending_state(|state| {
|
win1.toplevel().with_pending_state(|state| {
|
||||||
state.size =
|
state.size = Some(
|
||||||
Some((win1_size.w - width_partition, win1_size.h).into());
|
(win1_size.w - width_partition, i32::max(win1_size.h, 40))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
win1.with_state(|state| {
|
win1.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
@ -350,7 +367,8 @@ impl Layout {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
win2.toplevel().with_pending_state(|state| {
|
win2.toplevel().with_pending_state(|state| {
|
||||||
state.size = Some((width_partition, win1_size.h).into());
|
state.size =
|
||||||
|
Some((width_partition, i32::max(win1_size.h, 40)).into());
|
||||||
});
|
});
|
||||||
win2.with_state(|state| {
|
win2.with_state(|state| {
|
||||||
state.resize_state = WindowResizeState::Requested(
|
state.resize_state = WindowResizeState::Requested(
|
||||||
|
|
39
src/state.rs
39
src/state.rs
|
@ -105,6 +105,7 @@ pub struct State<B: Backend> {
|
||||||
|
|
||||||
impl<B: Backend> State<B> {
|
impl<B: Backend> State<B> {
|
||||||
pub fn handle_msg(&mut self, msg: Msg) {
|
pub fn handle_msg(&mut self, msg: Msg) {
|
||||||
|
// tracing::debug!("Got {msg:?}");
|
||||||
match msg {
|
match msg {
|
||||||
Msg::SetKeybind {
|
Msg::SetKeybind {
|
||||||
key,
|
key,
|
||||||
|
@ -491,6 +492,44 @@ impl<B: Backend> State<B> {
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Request::GetTagActive { tag_id } => {
|
||||||
|
let tag = self
|
||||||
|
.space
|
||||||
|
.outputs()
|
||||||
|
.flat_map(|op| {
|
||||||
|
op.with_state(|state| state.tags.clone())
|
||||||
|
})
|
||||||
|
.find(|tag| tag.id() == tag_id);
|
||||||
|
if let Some(tag) = tag {
|
||||||
|
crate::api::send_to_client(
|
||||||
|
&mut stream,
|
||||||
|
&OutgoingMsg::RequestResponse {
|
||||||
|
response: RequestResponse::TagActive {
|
||||||
|
active: tag.active()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Request::GetTagName { tag_id } => {
|
||||||
|
let tag = self
|
||||||
|
.space
|
||||||
|
.outputs()
|
||||||
|
.flat_map(|op| {
|
||||||
|
op.with_state(|state| state.tags.clone())
|
||||||
|
})
|
||||||
|
.find(|tag| tag.id() == tag_id);
|
||||||
|
if let Some(tag) = tag {
|
||||||
|
crate::api::send_to_client(
|
||||||
|
&mut stream,
|
||||||
|
&OutgoingMsg::RequestResponse {
|
||||||
|
response: RequestResponse::TagName {
|
||||||
|
name: tag.name()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue