mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
347 lines
11 KiB
Lua
347 lines
11 KiB
Lua
-- neovim users be like
|
|
require("pinnacle").setup(function(Pinnacle)
|
|
local Input = Pinnacle.input
|
|
local Process = Pinnacle.process
|
|
local Output = Pinnacle.output
|
|
local Tag = Pinnacle.tag
|
|
local Window = Pinnacle.window
|
|
local Layout = Pinnacle.layout
|
|
local Util = Pinnacle.util
|
|
local Snowcap = Pinnacle.snowcap
|
|
|
|
-- `Snowcap` will be nil when the Snowcap API isn't installed or Snowcap isn't running
|
|
-- A normal installation of Pinnacle won't have this issue, so you can remove this cast if desired.
|
|
---@cast Snowcap +?
|
|
|
|
local key = Input.key
|
|
|
|
---@type Modifier
|
|
local mod_key = "super"
|
|
-- Change the mod key to "alt" when running as a nested window
|
|
if Pinnacle.backend() == "window" then
|
|
mod_key = "alt"
|
|
end
|
|
|
|
local terminal = "alacritty"
|
|
|
|
--------------------
|
|
-- Mousebinds --
|
|
--------------------
|
|
|
|
Input.mousebind({ mod_key }, "btn_left", "press", function()
|
|
Window.begin_move("btn_left")
|
|
end)
|
|
|
|
Input.mousebind({ mod_key }, "btn_right", "press", function()
|
|
Window.begin_resize("btn_right")
|
|
end)
|
|
|
|
--------------------
|
|
-- Keybinds --
|
|
--------------------
|
|
|
|
-- mod_key + s shows the keybind overlay
|
|
if Snowcap then
|
|
Input.keybind({ mod_key }, "s", function()
|
|
Snowcap.integration.keybind_overlay():show()
|
|
end, {
|
|
group = "Compositor",
|
|
description = "Show the keybind overlay",
|
|
})
|
|
end
|
|
|
|
-- mod_key + shift + q = Quit Pinnacle
|
|
Input.keybind({ mod_key, "shift" }, "q", function()
|
|
if Snowcap then
|
|
Snowcap.integration.quit_prompt():show()
|
|
else
|
|
Pinnacle.quit()
|
|
end
|
|
end, {
|
|
group = "Compositor",
|
|
description = "Quit Pinnacle",
|
|
})
|
|
|
|
-- mod_key + ctrl + r = Reload config
|
|
Input.keybind({ mod_key, "ctrl" }, "r", function()
|
|
Pinnacle.reload_config()
|
|
end, {
|
|
group = "Compositor",
|
|
description = "Reload the config",
|
|
})
|
|
|
|
-- mod_key + shift + c = Close window
|
|
Input.keybind({ mod_key, "shift" }, "c", function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:close()
|
|
end
|
|
end, {
|
|
group = "Window",
|
|
description = "Close the focused window",
|
|
})
|
|
|
|
-- mod_key + Return = Spawn `terminal`
|
|
Input.keybind({ mod_key }, key.Return, function()
|
|
Process.spawn(terminal)
|
|
end, {
|
|
group = "Process",
|
|
description = "Spawn `alacritty`",
|
|
})
|
|
|
|
-- mod_key + ctrl + space = Toggle floating
|
|
Input.keybind({ mod_key, "ctrl" }, key.space, function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:toggle_floating()
|
|
focused:raise()
|
|
end
|
|
end, {
|
|
group = "Window",
|
|
description = "Toggle floating on the focused window",
|
|
})
|
|
|
|
-- mod_key + f = Toggle fullscreen
|
|
Input.keybind({ mod_key }, "f", function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:toggle_fullscreen()
|
|
focused:raise()
|
|
end
|
|
end, {
|
|
group = "Window",
|
|
description = "Toggle fullscreen on the focused window",
|
|
})
|
|
|
|
-- mod_key + m = Toggle maximized
|
|
Input.keybind({ mod_key }, "m", function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:toggle_maximized()
|
|
focused:raise()
|
|
end
|
|
end, {
|
|
group = "Window",
|
|
description = "Toggle maximized on the focused window",
|
|
})
|
|
|
|
----------------------
|
|
-- Tags and Outputs --
|
|
----------------------
|
|
|
|
local tag_names = { "1", "2", "3", "4", "5" }
|
|
|
|
-- Setup outputs.
|
|
--
|
|
-- `Output.setup` allows you to declare things like mode, scale, and tags for outputs.
|
|
-- Here we give all outputs tags 1 through 5.
|
|
Output.setup({
|
|
-- "*" matches all outputs
|
|
["*"] = { tags = tag_names },
|
|
})
|
|
|
|
-- If you want to declare output locations as well, you can use `Output.setup_locs`.
|
|
-- This will additionally allow you to recalculate output locations on signals like
|
|
-- output connect, disconnect, and resize.
|
|
--
|
|
-- Read the admittedly scuffed docs for more.
|
|
|
|
-- Tag keybinds
|
|
for _, tag_name in ipairs(tag_names) do
|
|
-- nil-safety: tags are guaranteed to be on the outputs due to connect_for_all above
|
|
|
|
-- mod_key + 1-5 = Switch to tags 1-5
|
|
Input.keybind({ mod_key }, tag_name, function()
|
|
Tag.get(tag_name):switch_to()
|
|
end, {
|
|
group = "Tag",
|
|
description = "Switch to tag " .. tag_name,
|
|
})
|
|
|
|
-- mod_key + ctrl + 1-5 = Toggle tags 1-5
|
|
Input.keybind({ mod_key, "ctrl" }, tag_name, function()
|
|
Tag.get(tag_name):toggle_active()
|
|
end, {
|
|
group = "Tag",
|
|
description = "Toggle tag " .. tag_name,
|
|
})
|
|
|
|
-- mod_key + shift + 1-5 = Move window to tags 1-5
|
|
Input.keybind({ mod_key, "shift" }, tag_name, function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:move_to_tag(Tag.get(tag_name) --[[@as TagHandle]])
|
|
end
|
|
end, {
|
|
group = "Tag",
|
|
description = "Move the focused window to tag " .. tag_name,
|
|
})
|
|
|
|
-- mod_key + ctrl + shift + 1-5 = Toggle tags 1-5 on window
|
|
Input.keybind({ mod_key, "ctrl", "shift" }, tag_name, function()
|
|
local focused = Window.get_focused()
|
|
if focused then
|
|
focused:toggle_tag(Tag.get(tag_name) --[[@as TagHandle]])
|
|
end
|
|
end, {
|
|
group = "Tag",
|
|
description = "Toggle tag " .. tag_name .. " on the focused window",
|
|
})
|
|
end
|
|
|
|
--------------------
|
|
-- Layouts --
|
|
--------------------
|
|
|
|
-- Pinnacle does not manage layouts compositor-side.
|
|
-- Instead, it delegates computation of layouts to your config,
|
|
-- which provides an interface to calculate the size and location of
|
|
-- windows that the compositor will use to position windows.
|
|
--
|
|
-- If you're familiar with River's layout generators, you'll understand the system here
|
|
-- a bit better.
|
|
--
|
|
-- The Lua API provides two layout system abstractions:
|
|
-- 1. Layout managers, and
|
|
-- 2. Layout generators.
|
|
--
|
|
-- ### Layout Managers ###
|
|
-- A layout manager is a table that contains a `get_active` function
|
|
-- that returns some layout generator.
|
|
-- A manager is meant to keep track of and choose various layout generators
|
|
-- across your usage of the compositor.
|
|
--
|
|
-- ### Layout generators ###
|
|
-- A layout generator is a table that holds some state as well as
|
|
-- the `layout` function, which takes in layout arguments and computes
|
|
-- an array of geometries that will determine the size and position
|
|
-- of windows being laid out.
|
|
--
|
|
-- There is one built-in layout manager and five built-in layout generators,
|
|
-- as shown below.
|
|
--
|
|
-- Additionally, this system is designed to be user-extensible;
|
|
-- you are free to create your own layout managers and generators for
|
|
-- maximum customizability! Docs for doing so are in the works, so sit tight.
|
|
|
|
-- Create a cycling layout manager. This provides methods to cycle
|
|
-- between the given layout generators below.
|
|
local layout_manager = Layout.new_cycling_manager({
|
|
-- `Layout.builtins` contains functions that create various layout generators.
|
|
-- Each of these has settings that can be overridden by passing in a table with
|
|
-- overriding options.
|
|
Layout.builtins.master_stack(),
|
|
Layout.builtins.master_stack({ master_side = "right" }),
|
|
Layout.builtins.master_stack({ master_side = "top" }),
|
|
Layout.builtins.master_stack({ master_side = "bottom" }),
|
|
Layout.builtins.dwindle(),
|
|
Layout.builtins.spiral(),
|
|
Layout.builtins.corner(),
|
|
Layout.builtins.corner({ corner_loc = "top_right" }),
|
|
Layout.builtins.corner({ corner_loc = "bottom_left" }),
|
|
Layout.builtins.corner({ corner_loc = "bottom_right" }),
|
|
Layout.builtins.fair(),
|
|
Layout.builtins.fair({ direction = "horizontal" }),
|
|
})
|
|
|
|
-- Set the cycling layout manager as the layout manager that will be used.
|
|
-- This then allows you to call `Layout.request_layout` to manually layout windows.
|
|
Layout.set_manager(layout_manager)
|
|
|
|
-- mod_key + space = Cycle forward one layout on the focused output
|
|
--
|
|
-- Yes, this is a bit verbose for my liking.
|
|
-- You need to cycle the layout on the first active tag
|
|
-- because that is the one that decides which layout is used.
|
|
Input.keybind({ mod_key }, key.space, function()
|
|
local focused_op = Output.get_focused()
|
|
if focused_op then
|
|
local tags = focused_op:tags() or {}
|
|
local tag = nil
|
|
|
|
---@type (fun(): (boolean|nil))[]
|
|
local tag_actives = {}
|
|
for i, t in ipairs(tags) do
|
|
tag_actives[i] = function()
|
|
return t:active()
|
|
end
|
|
end
|
|
|
|
-- We are batching API calls here for better performance
|
|
tag_actives = Util.batch(tag_actives)
|
|
|
|
for i, active in ipairs(tag_actives) do
|
|
if active then
|
|
tag = tags[i]
|
|
break
|
|
end
|
|
end
|
|
|
|
if tag then
|
|
layout_manager:cycle_layout_forward(tag)
|
|
Layout.request_layout(focused_op)
|
|
end
|
|
end
|
|
end, {
|
|
group = "Layout",
|
|
description = "Cycle the layout forward on the first active tag",
|
|
})
|
|
|
|
-- mod_key + shift + space = Cycle backward one layout on the focused output
|
|
Input.keybind({ mod_key, "shift" }, key.space, function()
|
|
local focused_op = Output.get_focused()
|
|
if focused_op then
|
|
local tags = focused_op:tags() or {}
|
|
local tag = nil
|
|
|
|
---@type (fun(): (boolean|nil))[]
|
|
local tag_actives = {}
|
|
for i, t in ipairs(tags) do
|
|
tag_actives[i] = function()
|
|
return t:active()
|
|
end
|
|
end
|
|
|
|
tag_actives = Util.batch(tag_actives)
|
|
|
|
for i, active in ipairs(tag_actives) do
|
|
if active then
|
|
tag = tags[i]
|
|
break
|
|
end
|
|
end
|
|
|
|
if tag then
|
|
layout_manager:cycle_layout_backward(tag)
|
|
Layout.request_layout(focused_op)
|
|
end
|
|
end
|
|
end, {
|
|
group = "Layout",
|
|
description = "Cycle the layout backward on the first active tag",
|
|
})
|
|
|
|
Input.set_libinput_settings({
|
|
tap = true,
|
|
})
|
|
|
|
-- Enable sloppy focus
|
|
Window.connect_signal({
|
|
pointer_enter = function(window)
|
|
window:set_focused(true)
|
|
end,
|
|
})
|
|
|
|
-- Request all windows use client-side decorations
|
|
Window.add_window_rule({
|
|
cond = {
|
|
all = {},
|
|
},
|
|
rule = {
|
|
decoration_mode = "client_side",
|
|
},
|
|
})
|
|
|
|
-- Spawning should happen after you add tags, as Pinnacle currently doesn't render windows without tags.
|
|
Process.spawn_once(terminal)
|
|
end)
|