mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-02-09 22:00:47 +01:00
Merge pull request #74 from Ottatop/improve_api
Improve api and fix stuff
This commit is contained in:
commit
b719ad4a3c
15 changed files with 332 additions and 177 deletions
|
@ -31,6 +31,7 @@ shellexpand = "3.1.0"
|
||||||
toml = "0.7.7"
|
toml = "0.7.7"
|
||||||
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
anyhow = { version = "1.0.75", features = ["backtrace"] }
|
||||||
clap = { version = "4.4.2", features = ["derive"] }
|
clap = { version = "4.4.2", features = ["derive"] }
|
||||||
|
xkbcommon = "0.6.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["egl", "winit", "udev", "xwayland"]
|
default = ["egl", "winit", "udev", "xwayland"]
|
||||||
|
|
|
@ -18,7 +18,6 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
local output = pinnacle.output -- Output management
|
local output = pinnacle.output -- Output management
|
||||||
|
|
||||||
-- Every key supported by xkbcommon.
|
-- Every key supported by xkbcommon.
|
||||||
-- Support for just putting in a string of a key is intended.
|
|
||||||
local keys = input.keys
|
local keys = input.keys
|
||||||
|
|
||||||
---@type Modifier
|
---@type Modifier
|
||||||
|
@ -85,10 +84,12 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
|
|
||||||
-- Tags ---------------------------------------------------------------------------
|
-- Tags ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local tags = { "1", "2", "3", "4", "5" }
|
||||||
|
|
||||||
output.connect_for_all(function(op)
|
output.connect_for_all(function(op)
|
||||||
-- Add tags 1, 2, 3, 4 and 5 on all monitors, and toggle tag 1 active by default
|
-- Add tags 1, 2, 3, 4 and 5 on all monitors, and toggle tag 1 active by default
|
||||||
|
|
||||||
op:add_tags("1", "2", "3", "4", "5")
|
op:add_tags(tags)
|
||||||
-- Same as tag.add(op, "1", "2", "3", "4", "5")
|
-- Same as tag.add(op, "1", "2", "3", "4", "5")
|
||||||
tag.toggle({ name = "1", output = op })
|
tag.toggle({ name = "1", output = op })
|
||||||
|
|
||||||
|
@ -112,8 +113,11 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
-- })
|
-- })
|
||||||
end)
|
end)
|
||||||
|
|
||||||
---@type Layout[]
|
-- Layout cycling
|
||||||
local layouts = {
|
|
||||||
|
-- Create a layout cycler to cycle your tag layouts. This will store which layout each tag has
|
||||||
|
-- and change to the next or previous one in the array when the respective function is called.
|
||||||
|
local layout_cycler = tag.layout_cycler({
|
||||||
"MasterStack",
|
"MasterStack",
|
||||||
"Dwindle",
|
"Dwindle",
|
||||||
"Spiral",
|
"Spiral",
|
||||||
|
@ -121,118 +125,29 @@ require("pinnacle").setup(function(pinnacle)
|
||||||
"CornerTopRight",
|
"CornerTopRight",
|
||||||
"CornerBottomLeft",
|
"CornerBottomLeft",
|
||||||
"CornerBottomRight",
|
"CornerBottomRight",
|
||||||
}
|
})
|
||||||
local indices = {}
|
|
||||||
|
|
||||||
-- Layout cycling
|
input.keybind({ mod_key }, keys.space, layout_cycler.next)
|
||||||
-- Yes, this is overly complicated and yes, I'll cook up a way to make it less so.
|
input.keybind({ mod_key, "Shift" }, keys.space, layout_cycler.prev)
|
||||||
input.keybind({ mod_key }, keys.space, function()
|
|
||||||
local tags = output.get_focused():tags()
|
|
||||||
for _, tg in pairs(tags) do
|
|
||||||
if tg:active() then
|
|
||||||
local name = tg:name()
|
|
||||||
if name == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
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)
|
|
||||||
input.keybind({ mod_key, "Shift" }, keys.space, function()
|
|
||||||
local tags = output.get_focused():tags()
|
|
||||||
for _, tg in pairs(tags) do
|
|
||||||
if tg:active() then
|
|
||||||
local name = tg:name()
|
|
||||||
if name == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
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)
|
|
||||||
|
|
||||||
input.keybind({ mod_key }, keys.KEY_1, function()
|
-- Tag manipulation
|
||||||
tag.switch_to("1")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key }, keys.KEY_2, function()
|
|
||||||
tag.switch_to("2")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key }, keys.KEY_3, function()
|
|
||||||
tag.switch_to("3")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key }, keys.KEY_4, function()
|
|
||||||
tag.switch_to("4")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key }, keys.KEY_5, function()
|
|
||||||
tag.switch_to("5")
|
|
||||||
end)
|
|
||||||
|
|
||||||
input.keybind({ mod_key, "Shift" }, keys.KEY_1, function()
|
for _, tag_name in pairs(tags) do
|
||||||
tag.toggle("1")
|
-- mod_key + 1-5 switches tags
|
||||||
end)
|
input.keybind({ mod_key }, tag_name, function()
|
||||||
input.keybind({ mod_key, "Shift" }, keys.KEY_2, function()
|
tag.switch_to(tag_name)
|
||||||
tag.toggle("2")
|
end)
|
||||||
end)
|
-- mod_key + Shift + 1-5 toggles tags
|
||||||
input.keybind({ mod_key, "Shift" }, keys.KEY_3, function()
|
input.keybind({ mod_key, "Shift" }, tag_name, function()
|
||||||
tag.toggle("3")
|
tag.toggle(tag_name)
|
||||||
end)
|
end)
|
||||||
input.keybind({ mod_key, "Shift" }, keys.KEY_4, function()
|
-- mod_key + Alt + 1-5 moves windows to tags
|
||||||
tag.toggle("4")
|
input.keybind({ mod_key, "Alt" }, tag_name, function()
|
||||||
end)
|
local _ = window.get_focused() and window:get_focused():move_to_tag(tag_name)
|
||||||
input.keybind({ mod_key, "Shift" }, keys.KEY_5, function()
|
end)
|
||||||
tag.toggle("5")
|
-- mod_key + Shift + Alt + 1-5 toggles tags on windows
|
||||||
end)
|
input.keybind({ mod_key, "Shift", "Alt" }, tag_name, function()
|
||||||
|
local _ = window.get_focused() and window.get_focused():toggle_tag(tag_name)
|
||||||
-- I check for nil this way because I don't want stylua to take up like 80 lines on `if win ~= nil`
|
end)
|
||||||
input.keybind({ mod_key, "Alt" }, keys.KEY_1, function()
|
end
|
||||||
local _ = window.get_focused() and window:get_focused():move_to_tag("1")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Alt" }, keys.KEY_2, function()
|
|
||||||
local _ = window.get_focused() and window:get_focused():move_to_tag("2")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Alt" }, keys.KEY_3, function()
|
|
||||||
local _ = window.get_focused() and window:get_focused():move_to_tag("3")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Alt" }, keys.KEY_4, function()
|
|
||||||
local _ = window.get_focused() and window:get_focused():move_to_tag("4")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Alt" }, keys.KEY_5, function()
|
|
||||||
local _ = window.get_focused() and window:get_focused():move_to_tag("5")
|
|
||||||
end)
|
|
||||||
|
|
||||||
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_1, function()
|
|
||||||
local _ = window.get_focused() and window.get_focused():toggle_tag("1")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_2, function()
|
|
||||||
local _ = window.get_focused() and window.get_focused():toggle_tag("2")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_3, function()
|
|
||||||
local _ = window.get_focused() and window.get_focused():toggle_tag("3")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_4, function()
|
|
||||||
local _ = window.get_focused() and window.get_focused():toggle_tag("4")
|
|
||||||
end)
|
|
||||||
input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_5, function()
|
|
||||||
local _ = window.get_focused() and window.get_focused():toggle_tag("5")
|
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -1,12 +1,37 @@
|
||||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
---Input management.
|
||||||
|
---
|
||||||
|
---This module provides utilities to set keybinds.
|
||||||
---@class InputModule
|
---@class InputModule
|
||||||
local input_module = {
|
local input_module = {
|
||||||
|
--- A table with every key provided by xkbcommon.
|
||||||
keys = require("keys"),
|
keys = require("keys"),
|
||||||
}
|
}
|
||||||
|
|
||||||
---Set a keybind. If called with an already existing keybind, it gets replaced.
|
---Set a keybind. If called with an already existing keybind, it gets replaced.
|
||||||
---
|
---
|
||||||
|
---You must provide three arguments:
|
||||||
|
---
|
||||||
|
--- - `modifiers`: An array of `Modifier`s. If you don't want any, provide an empty table.
|
||||||
|
--- - `key`: The key that will trigger `action`. You can provide three types of key:
|
||||||
|
--- - Something from the `Keys` table in `input.keys`, which lists every xkbcommon key. The naming pattern is the xkbcommon key without the `KEY_` prefix, unless that would make it start with a number or the reserved lua keyword `function`, in which case the `KEY_` prefix is included.
|
||||||
|
--- - A single character representing your key. This can be something like "g", "$", "~", "1", and so on.
|
||||||
|
--- - A string of the key's name. This is the name of the xkbcommon key without the `KEY_` prefix.
|
||||||
|
--- - `action`: The function that will be run when the keybind is pressed.
|
||||||
|
---
|
||||||
|
---It is important to note that `"a"` is different than `"A"`. Similarly, `keys.a` is different than `keys.A`.
|
||||||
|
---Usually, it's best to use the non-modified key to prevent confusion and unintended behavior.
|
||||||
|
---
|
||||||
|
---```lua
|
||||||
|
---input.keybind({ "Shift" }, "a", function() end) -- This is preferred
|
||||||
|
---input.keybind({ "Shift" }, "A", function() end) -- over this
|
||||||
|
---
|
||||||
|
--- -- And in fact, this keybind won't work at all because it expects no modifiers,
|
||||||
|
--- -- but you can't get "A" without using `Shift`.
|
||||||
|
---input.keybind({}, "A", function() end)
|
||||||
|
---```
|
||||||
|
---
|
||||||
---### Example
|
---### Example
|
||||||
---
|
---
|
||||||
---```lua
|
---```lua
|
||||||
|
@ -15,15 +40,24 @@ local input_module = {
|
||||||
--- process.spawn("Alacritty")
|
--- process.spawn("Alacritty")
|
||||||
---end)
|
---end)
|
||||||
---```
|
---```
|
||||||
---@param key Keys The key for the keybind.
|
---@param key Keys|string The key for the keybind.
|
||||||
---@param modifiers (Modifier)[] Which modifiers need to be pressed for the keybind to trigger.
|
---@param modifiers (Modifier)[] Which modifiers need to be pressed for the keybind to trigger.
|
||||||
---@param action fun() What to do.
|
---@param action fun() What to do.
|
||||||
function input_module.keybind(modifiers, key, action)
|
function input_module.keybind(modifiers, key, action)
|
||||||
table.insert(CallbackTable, action)
|
table.insert(CallbackTable, action)
|
||||||
|
|
||||||
|
local k = {}
|
||||||
|
|
||||||
|
if type(key) == "string" then
|
||||||
|
k.String = key
|
||||||
|
else
|
||||||
|
k.Int = key
|
||||||
|
end
|
||||||
|
|
||||||
SendMsg({
|
SendMsg({
|
||||||
SetKeybind = {
|
SetKeybind = {
|
||||||
modifiers = modifiers,
|
modifiers = modifiers,
|
||||||
key = key,
|
key = k,
|
||||||
callback_id = #CallbackTable,
|
callback_id = #CallbackTable,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
---@meta _
|
---@meta _
|
||||||
|
|
||||||
---@class _Msg
|
---@class _Msg
|
||||||
---@field SetKeybind { key: Keys, modifiers: Modifier[], callback_id: integer }?
|
---@field SetKeybind { key: { Int: Keys?, String: string? }, modifiers: Modifier[], callback_id: integer }?
|
||||||
---@field SetMousebind { button: integer }?
|
---@field SetMousebind { button: integer }?
|
||||||
--Windows
|
--Windows
|
||||||
---@field CloseWindow { window_id: WindowId }?
|
---@field CloseWindow { window_id: WindowId }?
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
---@diagnostic disable: redefined-local
|
---@diagnostic disable: redefined-local
|
||||||
|
|
||||||
|
---Process management.
|
||||||
|
---
|
||||||
|
---This module provides utilities to spawn processes and capture their output.
|
||||||
---@class ProcessModule
|
---@class ProcessModule
|
||||||
local process_module = {}
|
local process_module = {}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
indent_type = "Spaces"
|
indent_type = "Spaces"
|
||||||
column_width = 80
|
column_width = 120
|
||||||
|
|
127
api/lua/tag.lua
127
api/lua/tag.lua
|
@ -168,14 +168,9 @@ end
|
||||||
---local op = output.get_by_name("DP-1")
|
---local op = output.get_by_name("DP-1")
|
||||||
---
|
---
|
||||||
---tag.toggle("1") -- Toggle tag 1 on the focused output
|
---tag.toggle("1") -- Toggle tag 1 on the focused output
|
||||||
---tag.toggle({ "1" }) -- Same as above
|
|
||||||
---
|
---
|
||||||
---tag.toggle({ "1", "DP-1" }) -- Toggle tag 1 on DP-1
|
---tag.toggle({ name = "1", output = "DP-1" }) -- Toggle tag 1 on "DP-1"
|
||||||
---tag.toggle({ "1", op }) -- Same as above
|
---tag.toggle({ name = "1", output = op }) -- Same as above
|
||||||
---
|
|
||||||
--- -- Verbose versions of the two above
|
|
||||||
---tag.toggle({ name = "1", output = "DP-1" })
|
|
||||||
---tag.toggle({ name = "1", output = op })
|
|
||||||
---
|
---
|
||||||
--- -- Using a tag object
|
--- -- Using a tag object
|
||||||
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
||||||
|
@ -205,14 +200,9 @@ end
|
||||||
---local op = output.get_by_name("DP-1")
|
---local op = output.get_by_name("DP-1")
|
||||||
---
|
---
|
||||||
---tag.switch_to("1") -- Switch to tag 1 on the focused output
|
---tag.switch_to("1") -- Switch to tag 1 on the focused output
|
||||||
---tag.switch_to({ "1" }) -- Same as above
|
|
||||||
---
|
---
|
||||||
---tag.switch_to({ "1", "DP-1" }) -- Switch to tag 1 on DP-1
|
---tag.switch_to({ name = "1", output = "DP-1" }) -- Switch to tag 1 on "DP-1"
|
||||||
---tag.switch_to({ "1", op }) -- Same as above
|
---tag.switch_to({ name = "1", output = op }) -- Same as above
|
||||||
---
|
|
||||||
--- -- Verbose versions of the two above
|
|
||||||
---tag.switch_to({ name = "1", output = "DP-1" })
|
|
||||||
---tag.switch_to({ name = "1", output = op })
|
|
||||||
---
|
---
|
||||||
--- -- Using a tag object
|
--- -- Using a tag object
|
||||||
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
---local t = tag.get_by_name("1")[1] -- `t` is the first tag with the name "1"
|
||||||
|
@ -472,4 +462,113 @@ function tag_module.output(t)
|
||||||
return require("output").get_for_tag(t)
|
return require("output").get_for_tag(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@class LayoutCycler
|
||||||
|
---@field next fun(output: (Output|OutputName)?) Change the first active tag on `output` to its next layout. If `output` is empty, the focused output is used.
|
||||||
|
---@field prev fun(output: (Output|OutputName)?) Change the first active tag on `output` to its previous layout. If `output` is empty, the focused output is used.
|
||||||
|
|
||||||
|
---Given an array of layouts, this will create two functions; one will cycle forward the layout
|
||||||
|
---for the provided tag, and one will cycle backward.
|
||||||
|
---
|
||||||
|
--- ### Example
|
||||||
|
---```lua
|
||||||
|
---local layout_cycler = tag.layout_cycler({ "Dwindle", "Spiral", "MasterStack" })
|
||||||
|
---
|
||||||
|
---layout_cycler.next() -- Go to the next layout on the first tag of the focused output
|
||||||
|
---layout_cycler.prev() -- Go to the previous layout on the first tag of the focused output
|
||||||
|
---
|
||||||
|
---layout_cycler.next("DP-1") -- Do the above but on "DP-1" instead
|
||||||
|
---layout_cycler.prev(output.get_by_name("DP-1")) -- With an output object
|
||||||
|
---```
|
||||||
|
---@param layouts Layout[] The available layouts.
|
||||||
|
---@return LayoutCycler layout_cycler A table with the functions `next` and `prev`, which will cycle layouts for the given tag.
|
||||||
|
function tag_module.layout_cycler(layouts)
|
||||||
|
local indices = {}
|
||||||
|
|
||||||
|
-- Return empty functions if layouts is empty
|
||||||
|
if #layouts == 0 then
|
||||||
|
return {
|
||||||
|
next = function(_) end,
|
||||||
|
prev = function(_) end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
---@param output (Output|OutputName)?
|
||||||
|
next = function(output)
|
||||||
|
if type(output) == "string" then
|
||||||
|
output = require("output").get_by_name(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
output = output or require("output").get_focused()
|
||||||
|
|
||||||
|
if output == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = output:tags()
|
||||||
|
for _, tg in pairs(tags) do
|
||||||
|
if tg:active() then
|
||||||
|
local id = tg:id()
|
||||||
|
if id == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #layouts == 1 then
|
||||||
|
indices[id] = 1
|
||||||
|
elseif indices[id] == nil then
|
||||||
|
indices[id] = 2
|
||||||
|
else
|
||||||
|
if indices[id] + 1 > #layouts then
|
||||||
|
indices[id] = 1
|
||||||
|
else
|
||||||
|
indices[id] = indices[id] + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tg:set_layout(layouts[indices[id]])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
---@param output (Output|OutputName)?
|
||||||
|
prev = function(output)
|
||||||
|
if type(output) == "string" then
|
||||||
|
output = require("output").get_by_name(output)
|
||||||
|
end
|
||||||
|
|
||||||
|
output = output or require("output").get_focused()
|
||||||
|
|
||||||
|
if output == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local tags = output:tags()
|
||||||
|
for _, tg in pairs(tags) do
|
||||||
|
if tg:active() then
|
||||||
|
local id = tg:id()
|
||||||
|
if id == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if #layouts == 1 then
|
||||||
|
indices[id] = 1
|
||||||
|
elseif indices[id] == nil then
|
||||||
|
indices[id] = #layouts - 1
|
||||||
|
else
|
||||||
|
if indices[id] - 1 < 1 then
|
||||||
|
indices[id] = #layouts
|
||||||
|
else
|
||||||
|
indices[id] = indices[id] - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tg:set_layout(layouts[indices[id]])
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
return tag_module
|
return tag_module
|
||||||
|
|
|
@ -17,11 +17,17 @@ use self::window_rules::{WindowRule, WindowRuleCondition};
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
|
||||||
pub struct CallbackId(pub u32);
|
pub struct CallbackId(pub u32);
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone)]
|
||||||
|
pub enum KeyIntOrString {
|
||||||
|
Int(u32),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
// Input
|
// Input
|
||||||
SetKeybind {
|
SetKeybind {
|
||||||
key: u32,
|
key: KeyIntOrString,
|
||||||
modifiers: Vec<Modifier>,
|
modifiers: Vec<Modifier>,
|
||||||
callback_id: CallbackId,
|
callback_id: CallbackId,
|
||||||
},
|
},
|
||||||
|
|
|
@ -72,7 +72,7 @@ use smithay::{
|
||||||
backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle,
|
backend::GlobalId, protocol::wl_surface::WlSurface, Display, DisplayHandle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
utils::{Clock, DeviceFd, Logical, Monotonic, Physical, Point, Rectangle, Transform},
|
utils::{Clock, DeviceFd, IsAlive, Logical, Monotonic, Physical, Point, Rectangle, Transform},
|
||||||
wayland::{
|
wayland::{
|
||||||
dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
|
dmabuf::{DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufState},
|
||||||
input_method::{InputMethodHandle, InputMethodSeat},
|
input_method::{InputMethodHandle, InputMethodSeat},
|
||||||
|
@ -86,7 +86,9 @@ use smithay_drm_extras::{
|
||||||
use crate::{
|
use crate::{
|
||||||
api::msg::{Args, OutgoingMsg},
|
api::msg::{Args, OutgoingMsg},
|
||||||
render::{pointer::PointerElement, CustomRenderElements},
|
render::{pointer::PointerElement, CustomRenderElements},
|
||||||
state::{take_presentation_feedback, Backend, CalloopData, State, SurfaceDmabufFeedback},
|
state::{
|
||||||
|
take_presentation_feedback, Backend, CalloopData, State, SurfaceDmabufFeedback, WithState,
|
||||||
|
},
|
||||||
window::WindowElement,
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -151,14 +153,10 @@ pub fn run_udev() -> anyhow::Result<()> {
|
||||||
let mut event_loop = EventLoop::try_new().unwrap();
|
let mut event_loop = EventLoop::try_new().unwrap();
|
||||||
let mut display = Display::new().unwrap();
|
let mut display = Display::new().unwrap();
|
||||||
|
|
||||||
/*
|
// Initialize session
|
||||||
* Initialize session
|
|
||||||
*/
|
|
||||||
let (session, notifier) = LibSeatSession::new()?;
|
let (session, notifier) = LibSeatSession::new()?;
|
||||||
|
|
||||||
/*
|
// Initialize the compositor
|
||||||
* Initialize the compositor
|
|
||||||
*/
|
|
||||||
let primary_gpu = if let Ok(var) = std::env::var("ANVIL_DRM_DEVICE") {
|
let primary_gpu = if let Ok(var) = std::env::var("ANVIL_DRM_DEVICE") {
|
||||||
DrmNode::from_path(var).expect("Invalid drm device path")
|
DrmNode::from_path(var).expect("Invalid drm device path")
|
||||||
} else {
|
} else {
|
||||||
|
@ -202,9 +200,7 @@ pub fn run_udev() -> anyhow::Result<()> {
|
||||||
event_loop.handle(),
|
event_loop.handle(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
/*
|
// Initialize the udev backend
|
||||||
* Initialize the udev backend
|
|
||||||
*/
|
|
||||||
let udev_backend = UdevBackend::new(state.seat.name())?;
|
let udev_backend = UdevBackend::new(state.seat.name())?;
|
||||||
|
|
||||||
// Create DrmNodes from already connected GPUs
|
// Create DrmNodes from already connected GPUs
|
||||||
|
@ -245,9 +241,7 @@ pub fn run_udev() -> anyhow::Result<()> {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
/*
|
// Initialize libinput backend
|
||||||
* Initialize libinput backend
|
|
||||||
*/
|
|
||||||
let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(
|
let mut libinput_context = Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(
|
||||||
backend.session.clone().into(),
|
backend.session.clone().into(),
|
||||||
);
|
);
|
||||||
|
@ -256,9 +250,7 @@ pub fn run_udev() -> anyhow::Result<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
|
let libinput_backend = LibinputInputBackend::new(libinput_context.clone());
|
||||||
|
|
||||||
/*
|
// Bind all our objects that get driven by the event loop
|
||||||
* Bind all our objects that get driven by the event loop
|
|
||||||
*/
|
|
||||||
let insert_ret = event_loop
|
let insert_ret = event_loop
|
||||||
.handle()
|
.handle()
|
||||||
.insert_source(libinput_backend, move |event, _, data| {
|
.insert_source(libinput_backend, move |event, _, data| {
|
||||||
|
@ -1332,10 +1324,18 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let windows = self
|
||||||
|
.focus_state
|
||||||
|
.focus_stack
|
||||||
|
.iter()
|
||||||
|
.filter(|win| win.alive())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let result = render_surface(
|
let result = render_surface(
|
||||||
&mut self.cursor_status,
|
&mut self.cursor_status,
|
||||||
&self.space,
|
&self.space,
|
||||||
&self.windows,
|
&windows,
|
||||||
self.dnd_icon.as_ref(),
|
self.dnd_icon.as_ref(),
|
||||||
&self.focus_state.focus_stack,
|
&self.focus_state.focus_stack,
|
||||||
surface,
|
surface,
|
||||||
|
@ -1452,6 +1452,31 @@ fn render_surface<'a>(
|
||||||
pointer_location: Point<f64, Logical>,
|
pointer_location: Point<f64, Logical>,
|
||||||
clock: &Clock<Monotonic>,
|
clock: &Clock<Monotonic>,
|
||||||
) -> Result<bool, SwapBuffersError> {
|
) -> Result<bool, SwapBuffersError> {
|
||||||
|
let pending_win_count = windows
|
||||||
|
.iter()
|
||||||
|
.filter(|win| win.alive())
|
||||||
|
.filter(|win| win.with_state(|state| !state.loc_request_state.is_idle()))
|
||||||
|
.count() as u32;
|
||||||
|
|
||||||
|
tracing::debug!("pending_win_count is {pending_win_count}");
|
||||||
|
|
||||||
|
if pending_win_count > 0 {
|
||||||
|
for win in windows.iter() {
|
||||||
|
win.send_frame(output, clock.now(), Some(Duration::ZERO), |_, _| {
|
||||||
|
Some(output.clone())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
surface
|
||||||
|
.compositor
|
||||||
|
.queue_frame(None, None, None)
|
||||||
|
.map_err(Into::<SwapBuffersError>::into)?;
|
||||||
|
|
||||||
|
// TODO: still draw the cursor here
|
||||||
|
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
let output_render_elements = crate::render::generate_render_elements(
|
let output_render_elements = crate::render::generate_render_elements(
|
||||||
space,
|
space,
|
||||||
windows,
|
windows,
|
||||||
|
|
|
@ -12,7 +12,10 @@ use smithay::{
|
||||||
},
|
},
|
||||||
winit::{WinitError, WinitEvent, WinitGraphicsBackend},
|
winit::{WinitError, WinitEvent, WinitGraphicsBackend},
|
||||||
},
|
},
|
||||||
desktop::{layer_map_for_output, utils::send_frames_surface_tree},
|
desktop::{
|
||||||
|
layer_map_for_output,
|
||||||
|
utils::{send_frames_surface_tree, surface_primary_scanout_output},
|
||||||
|
},
|
||||||
input::pointer::CursorImageStatus,
|
input::pointer::CursorImageStatus,
|
||||||
output::{Output, Subpixel},
|
output::{Output, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
|
@ -32,7 +35,7 @@ use smithay::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::pointer::PointerElement,
|
render::pointer::PointerElement,
|
||||||
state::{take_presentation_feedback, Backend, CalloopData, State},
|
state::{take_presentation_feedback, Backend, CalloopData, State, WithState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::BackendData;
|
use super::BackendData;
|
||||||
|
@ -232,13 +235,35 @@ pub fn run_winit() -> anyhow::Result<()> {
|
||||||
|
|
||||||
pointer_element.set_status(state.cursor_status.clone());
|
pointer_element.set_status(state.cursor_status.clone());
|
||||||
|
|
||||||
if state.pause_rendering {
|
// TODO: make a pending_windows state, when pending_windows increases,
|
||||||
|
// | pause rendering.
|
||||||
|
// | If it goes down, push a frame, then repeat until no pending_windows are left.
|
||||||
|
|
||||||
|
let pending_win_count = state
|
||||||
|
.windows
|
||||||
|
.iter()
|
||||||
|
.filter(|win| win.alive())
|
||||||
|
.filter(|win| win.with_state(|state| !state.loc_request_state.is_idle()))
|
||||||
|
.count() as u32;
|
||||||
|
|
||||||
|
if pending_win_count > 0 {
|
||||||
|
for win in state.windows.iter() {
|
||||||
|
win.send_frame(
|
||||||
|
&output,
|
||||||
|
state.clock.now(),
|
||||||
|
Some(Duration::ZERO),
|
||||||
|
surface_primary_scanout_output,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
state.space.refresh();
|
state.space.refresh();
|
||||||
state.popup_manager.cleanup();
|
state.popup_manager.cleanup();
|
||||||
display
|
display
|
||||||
.flush_clients()
|
.flush_clients()
|
||||||
.expect("failed to flush client buffers");
|
.expect("failed to flush client buffers");
|
||||||
|
|
||||||
|
// TODO: still draw the cursor here
|
||||||
|
|
||||||
return TimeoutAction::ToDuration(Duration::from_millis(1));
|
return TimeoutAction::ToDuration(Duration::from_millis(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,9 +273,17 @@ pub fn run_winit() -> anyhow::Result<()> {
|
||||||
|
|
||||||
state.focus_state.fix_up_focus(&mut state.space);
|
state.focus_state.fix_up_focus(&mut state.space);
|
||||||
|
|
||||||
|
let windows = state
|
||||||
|
.focus_state
|
||||||
|
.focus_stack
|
||||||
|
.iter()
|
||||||
|
.filter(|win| win.alive())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let output_render_elements = crate::render::generate_render_elements(
|
let output_render_elements = crate::render::generate_render_elements(
|
||||||
&state.space,
|
&state.space,
|
||||||
&state.windows,
|
&windows,
|
||||||
state.pointer_location,
|
state.pointer_location,
|
||||||
&mut state.cursor_status,
|
&mut state.cursor_status,
|
||||||
state.dnd_icon.as_ref(),
|
state.dnd_icon.as_ref(),
|
||||||
|
|
|
@ -333,7 +333,9 @@ impl XdgShellHandler for State {
|
||||||
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
||||||
window.toggle_maximized();
|
window.toggle_maximized();
|
||||||
}
|
}
|
||||||
// TODO: might need to update_windows here
|
|
||||||
|
let Some(output) = window.output(self) else { return };
|
||||||
|
self.update_windows(&output);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
|
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
|
||||||
|
@ -344,6 +346,9 @@ impl XdgShellHandler for State {
|
||||||
if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
||||||
window.toggle_maximized();
|
window.toggle_maximized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Some(output) = window.output(self) else { return };
|
||||||
|
self.update_windows(&output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn minimize_request(&mut self, surface: ToplevelSurface) {
|
// fn minimize_request(&mut self, surface: ToplevelSurface) {
|
||||||
|
|
31
src/input.rs
31
src/input.rs
|
@ -183,21 +183,30 @@ impl State {
|
||||||
modifier_mask.push(Modifier::Super);
|
modifier_mask.push(Modifier::Super);
|
||||||
}
|
}
|
||||||
let modifier_mask = ModifierMask::from(modifier_mask);
|
let modifier_mask = ModifierMask::from(modifier_mask);
|
||||||
let raw_sym = if keysym.raw_syms().len() == 1 {
|
let raw_sym = keysym.raw_syms().iter().next();
|
||||||
keysym.raw_syms()[0]
|
let mod_sym = keysym.modified_sym();
|
||||||
} else {
|
|
||||||
keysyms::KEY_NoSymbol
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(callback_id) = state
|
let cb_id_mod = state
|
||||||
.input_state
|
.input_state
|
||||||
.keybinds
|
.keybinds
|
||||||
.get(&(modifier_mask, raw_sym))
|
.get(&(modifier_mask, mod_sym));
|
||||||
{
|
|
||||||
return FilterResult::Intercept(KeyAction::CallCallback(*callback_id));
|
let cb_id_raw = if let Some(raw_sym) = raw_sym {
|
||||||
} else if (modifier_mask, raw_sym) == kill_keybind {
|
state.input_state.keybinds.get(&(modifier_mask, *raw_sym))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
match (cb_id_mod, cb_id_raw) {
|
||||||
|
(Some(cb_id), _) | (None, Some(cb_id)) => {
|
||||||
|
return FilterResult::Intercept(KeyAction::CallCallback(*cb_id));
|
||||||
|
}
|
||||||
|
(None, None) => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifier_mask, mod_sym) == kill_keybind {
|
||||||
return FilterResult::Intercept(KeyAction::Quit);
|
return FilterResult::Intercept(KeyAction::Quit);
|
||||||
} else if (modifier_mask, raw_sym) == reload_keybind {
|
} else if (modifier_mask, mod_sym) == reload_keybind {
|
||||||
return FilterResult::Intercept(KeyAction::ReloadConfig);
|
return FilterResult::Intercept(KeyAction::ReloadConfig);
|
||||||
} else if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 =
|
} else if let mut vt @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 =
|
||||||
keysym.modified_sym() {
|
keysym.modified_sym() {
|
||||||
|
|
|
@ -161,24 +161,32 @@ impl State {
|
||||||
//
|
//
|
||||||
// This *will* cause everything to freeze for a few frames, but it shouldn't impact
|
// This *will* cause everything to freeze for a few frames, but it shouldn't impact
|
||||||
// anything meaningfully.
|
// anything meaningfully.
|
||||||
self.pause_rendering = true;
|
// self.pause_rendering = true;
|
||||||
|
|
||||||
// schedule on all idle
|
// schedule on all idle
|
||||||
self.schedule(
|
self.schedule(
|
||||||
move |_dt| {
|
move |_dt| {
|
||||||
// tracing::debug!("Waiting for all to be idle");
|
tracing::debug!("Waiting for all to be idle");
|
||||||
let all_idle = pending_wins
|
let all_idle = pending_wins
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, win)| win.alive())
|
.filter(|(_, win)| win.alive())
|
||||||
.all(|(_, win)| win.with_state(|state| state.loc_request_state.is_idle()));
|
.all(|(_, win)| win.with_state(|state| state.loc_request_state.is_idle()));
|
||||||
|
|
||||||
|
let num_not_idle = pending_wins
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, win)| win.alive())
|
||||||
|
.filter(|(_, win)| !win.with_state(|state| state.loc_request_state.is_idle()))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
tracing::debug!("{num_not_idle} not idle");
|
||||||
|
|
||||||
all_idle
|
all_idle
|
||||||
},
|
},
|
||||||
move |dt| {
|
move |dt| {
|
||||||
for (loc, win) in non_pending_wins {
|
for (loc, win) in non_pending_wins {
|
||||||
dt.state.space.map_element(win, loc, false);
|
dt.state.space.map_element(win, loc, false);
|
||||||
}
|
}
|
||||||
dt.state.pause_rendering = false;
|
// dt.state.pause_rendering = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,8 +138,6 @@ pub struct State {
|
||||||
pub xwayland: XWayland,
|
pub xwayland: XWayland,
|
||||||
pub xwm: Option<X11Wm>,
|
pub xwm: Option<X11Wm>,
|
||||||
pub xdisplay: Option<u32>,
|
pub xdisplay: Option<u32>,
|
||||||
|
|
||||||
pub pause_rendering: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -359,8 +357,6 @@ impl State {
|
||||||
xwayland,
|
xwayland,
|
||||||
xwm: None,
|
xwm: None,
|
||||||
xdisplay: None,
|
xdisplay: None,
|
||||||
|
|
||||||
pause_rendering: false,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestId, RequestResponse},
|
api::msg::{
|
||||||
|
Args, CallbackId, KeyIntOrString, Msg, OutgoingMsg, Request, RequestId, RequestResponse,
|
||||||
|
},
|
||||||
tag::Tag,
|
tag::Tag,
|
||||||
window::WindowElement,
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
@ -25,7 +27,26 @@ impl State {
|
||||||
modifiers,
|
modifiers,
|
||||||
callback_id,
|
callback_id,
|
||||||
} => {
|
} => {
|
||||||
tracing::info!("set keybind: {:?}, {}", modifiers, key);
|
let key = match key {
|
||||||
|
KeyIntOrString::Int(num) => num,
|
||||||
|
KeyIntOrString::String(s) => {
|
||||||
|
if s.chars().count() == 1 {
|
||||||
|
let Some(ch) = s.chars().next() else { unreachable!() };
|
||||||
|
let raw = xkbcommon::xkb::Keysym::from_char(ch).raw();
|
||||||
|
tracing::info!("set keybind: {:?}, {:?} (raw {})", modifiers, ch, raw);
|
||||||
|
raw
|
||||||
|
} else {
|
||||||
|
let raw = xkbcommon::xkb::keysym_from_name(
|
||||||
|
&s,
|
||||||
|
xkbcommon::xkb::KEYSYM_NO_FLAGS,
|
||||||
|
)
|
||||||
|
.raw();
|
||||||
|
tracing::info!("set keybind: {:?}, {:?}", modifiers, raw);
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.input_state
|
self.input_state
|
||||||
.keybinds
|
.keybinds
|
||||||
.insert((modifiers.into(), key), callback_id);
|
.insert((modifiers.into(), key), callback_id);
|
||||||
|
|
Loading…
Add table
Reference in a new issue