Change default keybinds

This commit is contained in:
Ottatop 2024-08-07 11:37:54 -05:00
parent f5aabd5b60
commit 503cc06aa7
13 changed files with 237 additions and 498 deletions

View file

@ -172,13 +172,9 @@ just install run -- -c ./api/lua/examples/default
cargo run -- -c ./api/rust/examples/default_config
```
When running without compiled Snowcap integration,
use the following directories instead:
When running a Rust config without compiled Snowcap integration,
use the following directory instead (Lua users can use the same directory):
```sh
# Lua
just install run -- -c ./api/lua/examples/default_no_snowcap
# Rust
cargo run -- -c ./api/rust/examples/default_config_no_snowcap
```
@ -205,8 +201,8 @@ Note that this currently copies default configs *with* Snowcap integration.
Run `cargo run -- config gen --help` for information on the command.
## More on configuration
Pinnacle is configured purely through IPC using [gRPC](https://grpc.io/). This is done through
configuration clients that use the [Lua](api/lua) and [Rust](api/rust) interface libraries.
Pinnacle is configured mostly at runtime through IPC using [gRPC](https://grpc.io/). This is done through
configuration clients that use the [Lua](api/lua) and [Rust](api/rust) APIs.
As the compositor has no direct integration with these clients, it must know what it needs to run
through a separate file, aptly called the `metaconfig.toml` file.
@ -225,8 +221,8 @@ Rust config.
Additionally, if your config crashes, Pinnacle will also start the embedded Rust config.
> [!NOTE]
> If you have not run `eval $(luarocks path --lua-version <your-lua-version>)`, Pinnacle will fallback to the
> embedded Rust config.
> If you are using a Lua config and have not run `eval $(luarocks path --lua-version <your-lua-version>)`,
> Pinnacle will fallback to the embedded Rust config.
### The `metaconfig.toml` file
A `metaconfig.toml` file must contain the following entries:
@ -255,26 +251,29 @@ Rust: https://pinnacle-comp.github.io/rust-reference/main.</b>
> Other branches for Lua soon<sup>tm</sup>
# Controls
> Yes, ctrl is a bad mod key I know, this will be changed to Awesome keybinds soon
The following are the default controls in the [`default_config`](api/rust/examples/default_config/main.rs).
| Binding | Action |
|----------------------------------------------|------------------------------------|
| <kbd>Ctrl</kbd> + <kbd>s</kbd> | Show the keybind overlay |
| <kbd>Ctrl</kbd> + <kbd>Mouse left drag</kbd> | Move window |
| <kbd>Ctrl</kbd> + <kbd>Mouse right drag</kbd>| Resize window |
| <kbd>Ctrl</kbd><kbd>Alt</kbd> + <kbd>q</kbd> | Quit Pinnacle |
| <kbd>Ctrl</kbd><kbd>Alt</kbd> + <kbd>c</kbd> | Close window |
| <kbd>Ctrl</kbd> + <kbd>Return</kbd> | Spawn [Alacritty](https://github.com/alacritty/alacritty) (you can change this in the config)|
| <kbd>Ctrl</kbd><kbd>Alt</kbd> + <kbd>Space</kbd> | Toggle between floating and tiled |
| <kbd>Ctrl</kbd> + <kbd>f</kbd> | Toggle fullscreen |
| <kbd>Ctrl</kbd> + <kbd>m</kbd> | Toggle maximized |
| <kbd>Ctrl</kbd> + <kbd>Space</kbd> | Cycle to the next layout |
| <kbd>Ctrl</kbd><kbd>Shift</kbd> + <kbd>Space</kbd> | Cycle to the previous layout |
| <kbd>Ctrl</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Switch to tag `1` to `5` |
| <kbd>Ctrl</kbd><kbd>Shift</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Toggle tag `1` to `5` |
| <kbd>Ctrl</kbd><kbd>Alt</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Move a window to tag `1` to `5` |
| <kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>Shift</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Toggle tag `1` to `5` on a window |
The following are the default controls, mirroring Awesome's defaults.
<kbd>Mod</kbd> is <kbd>Super</kbd> when running in a tty and <kbd>Alt</kbd> when running as a nested window.
| Binding | Action |
|------------------------------------------------------------------------------|-----------------------------------|
| <kbd>Mod</kbd> + <kbd>s</kbd> | Show the keybind overlay |
| <kbd>Mod</kbd> + <kbd>Mouse left drag</kbd> | Move window |
| <kbd>Mod</kbd> + <kbd>Mouse right drag</kbd> | Resize window |
| <kbd>Mod</kbd><kbd>Shift</kbd> + <kbd>q</kbd> | Quit Pinnacle |
| <kbd>Mod</kbd><kbd>Ctrl</kbd> + <kbd>r</kbd> | Reload the config |
| <kbd>Mod</kbd><kbd>Shift</kbd> + <kbd>c</kbd> | Close window |
| <kbd>Mod</kbd> + <kbd>Return</kbd> | Spawn [Alacritty](https://github.com/alacritty/alacritty) (you can change this in the config) |
| <kbd>Mod</kbd><kbd>Ctrl</kbd> + <kbd>Space</kbd> | Toggle floating |
| <kbd>Mod</kbd> + <kbd>f</kbd> | Toggle fullscreen |
| <kbd>Mod</kbd> + <kbd>m</kbd> | Toggle maximized |
| <kbd>Mod</kbd> + <kbd>Space</kbd> | Cycle to the next layout |
| <kbd>Mod</kbd><kbd>Shift</kbd> + <kbd>Space</kbd> | Cycle to the previous layout |
| <kbd>Mod</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Switch to tag `1` to `5` |
| <kbd>Mod</kbd><kbd>Ctrl</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Toggle tag `1` to `5` |
| <kbd>Mod</kbd><kbd>Shift</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Move a window to tag `1` to `5` |
| <kbd>Mod</kbd><kbd>Ctrl</kbd><kbd>Shift</kbd> + <kbd>1</kbd> to <kbd>5</kbd> | Toggle tag `1` to `5` on a window |
# Feature Requests, Bug Reports, Contributions, and Questions
See [`CONTRIBUTING.md`](CONTRIBUTING.md).
@ -283,4 +282,5 @@ See [`CONTRIBUTING.md`](CONTRIBUTING.md).
See [`CHANGELOG.md`](CHANGELOG.md).
# With Special Thanks To
- [Smithay](https://github.com/Smithay/smithay): For being a great compositor library and also allowing me not to deal with all the graphics stuff I still don't understand
- [Niri](https://github.com/YaLTeR/niri): For all that rendering and protocol stuff I, ahem, *took inspiration* from

View file

@ -9,20 +9,21 @@ require("pinnacle").setup(function(Pinnacle)
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 = "ctrl"
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"
Input.keybind({ mod_key }, "s", function()
Snowcap.integration.keybind_overlay():show()
end, {
group = "Compositor",
description = "Show the keybind overlay",
})
--------------------
-- Mousebinds --
--------------------
@ -39,24 +40,38 @@ require("pinnacle").setup(function(Pinnacle)
-- Keybinds --
--------------------
-- mod_key + alt + q = Quit Pinnacle
Input.keybind({ mod_key, "alt" }, "q", function()
Snowcap.integration.quit_prompt():show()
-- 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 + alt + r = Reload config
Input.keybind({ mod_key, "alt" }, "r", function()
-- 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 + alt + c = Close window
Input.keybind({ mod_key, "alt" }, "c", function()
-- mod_key + shift + c = Close window
Input.keybind({ mod_key, "shift" }, "c", function()
local focused = Window.get_focused()
if focused then
focused:close()
@ -66,7 +81,7 @@ require("pinnacle").setup(function(Pinnacle)
description = "Close the focused window",
})
-- mod_key + alt + Return = Spawn `terminal`
-- mod_key + Return = Spawn `terminal`
Input.keybind({ mod_key }, key.Return, function()
Process.spawn(terminal)
end, {
@ -74,8 +89,8 @@ require("pinnacle").setup(function(Pinnacle)
description = "Spawn `alacritty`",
})
-- mod_key + alt + space = Toggle floating
Input.keybind({ mod_key, "alt" }, key.space, function()
-- 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()
@ -143,16 +158,16 @@ require("pinnacle").setup(function(Pinnacle)
description = "Switch to tag " .. tag_name,
})
-- mod_key + shift + 1-5 = Toggle tags 1-5
Input.keybind({ mod_key, "shift" }, tag_name, function()
-- 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 + alt + 1-5 = Move window to tags 1-5
Input.keybind({ mod_key, "alt" }, tag_name, function()
-- 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]])
@ -162,8 +177,8 @@ require("pinnacle").setup(function(Pinnacle)
description = "Move the focused window to tag " .. tag_name,
})
-- mod_key + shift + alt + 1-5 = Toggle tags 1-5 on window
Input.keybind({ mod_key, "shift", "alt" }, tag_name, function()
-- 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]])
@ -317,7 +332,7 @@ require("pinnacle").setup(function(Pinnacle)
end,
})
-- Request all windows to use client-side decorations
-- Request all windows use client-side decorations
Window.add_window_rule({
cond = {
all = {},

View file

@ -1,10 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"workspace.library": [
"~/.luarocks/share/lua/5.4/pinnacle.lua",
"~/.luarocks/share/lua/5.4/pinnacle",
"~/.luarocks/share/lua/5.4/snowcap.lua",
"~/.luarocks/share/lua/5.4/snowcap"
],
"runtime.version": "Lua 5.4",
}

View file

@ -1,314 +0,0 @@
-- 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 key = Input.key
---@type Modifier
local mod_key = "ctrl"
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 + alt + q = Quit Pinnacle
Input.keybind({ mod_key, "alt" }, "q", function()
Pinnacle.quit()
end, {
group = "Compositor",
description = "Quit Pinnacle",
})
-- mod_key + alt + r = Reload config
Input.keybind({ mod_key, "alt" }, "r", function()
Pinnacle.reload_config()
end, {
group = "Compositor",
description = "Reload the config",
})
-- mod_key + alt + c = Close window
Input.keybind({ mod_key, "alt" }, "c", function()
local focused = Window.get_focused()
if focused then
focused:close()
end
end, {
group = "Window",
description = "Close the focused window",
})
-- mod_key + alt + Return = Spawn `terminal`
Input.keybind({ mod_key }, key.Return, function()
Process.spawn(terminal)
end, {
group = "Process",
description = "Spawn `alacritty`",
})
-- mod_key + alt + space = Toggle floating
Input.keybind({ mod_key, "alt" }, 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 + shift + 1-5 = Toggle tags 1-5
Input.keybind({ mod_key, "shift" }, tag_name, function()
Tag.get(tag_name):toggle_active()
end, {
group = "Tag",
description = "Toggle tag " .. tag_name,
})
-- mod_key + alt + 1-5 = Move window to tags 1-5
Input.keybind({ mod_key, "alt" }, 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 + shift + alt + 1-5 = Toggle tags 1-5 on window
Input.keybind({ mod_key, "shift", "alt" }, 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,
})
-- Spawning should happen after you add tags, as Pinnacle currently doesn't render windows without tags.
Process.spawn_once(terminal)
end)

View file

@ -1,46 +0,0 @@
# This metaconfig.toml file dictates what config Pinnacle will run.
#
# When running Pinnacle, the compositor will look in the following directories for a metaconfig.toml file,
# in order from top to bottom:
# $PINNACLE_CONFIG_DIR
# $XDG_CONFIG_HOME/pinnacle/
# ~/.config/pinnacle/
#
# When Pinnacle finds a metaconfig.toml file, it will execute the command provided to `command`.
# To use a Rust config, this should be changed to something like ["cargo", "run"].
#
# Because configuration is done using an external process, if it ever crashes, you lose all of your keybinds.
# The compositor will load the default config if that happens, but in the event that you don't have
# the necessary dependencies for it to run, you may get softlocked.
# In order prevent you from getting stuck in the compositor, you must define keybinds to reload your config
# and kill Pinnacle.
#
# More details on each setting can be found below.
# The command Pinnacle will run on startup and when you reload your config.
# Paths are relative to the directory the metaconfig.toml file is in.
# This must be an array.
command = ["lua", "default_config.lua"]
### Keybinds ###
# Each keybind takes in a table with two fields: `modifiers` and `key`.
# - `modifiers` can be one of "Ctrl", "Alt", "Shift", or "Super".
# - `key` can be a string of any lowercase letter, number,
# "numN" where N is a number for numpad keys, or "esc"/"escape".
# Support for any xkbcommon key is planned for a future update.
# The keybind that will reload your config.
reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" }
# The keybind that will kill Pinnacle.
kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" }
### Socket directory ###
# Pinnacle will open a Unix socket at `$XDG_RUNTIME_DIR` by default, falling back to `/tmp` if it doesn't exist.
# If you want/need to change this, use the `socket_dir` setting set to the directory of your choosing.
#
# socket_dir = "/your/dir/here/"
### Environment Variables ###
# If you need to spawn your config with any environment variables, list them here.
[envs]
# key = "value"

View file

@ -49,6 +49,32 @@ function pinnacle.reload_config()
end
end
---Gets the currently running backend.
---
---@return "tty" | "window" `"tty"` if Pinnacle is running in a tty, or `"window"` if it's running in a nested window
function pinnacle.backend()
local response, err = client:unary_request(pinnacle_service.Backend, {})
if err then
log:error(err)
-- TODO: possibly panic here; a nil index error will be thrown after this anyway
end
---@cast response pinnacle.v0alpha1.BackendResponse
local defs = require("pinnacle.grpc.defs")
if response.backend == defs.pinnacle.v0alpha1.Backend.BACKEND_WINDOW then
return "window"
else
return "tty"
end
end
---Initializes the protobuf backend and connects to Pinnacle's gRPC socket.
---
---If the Snowcap Lua API is installed and Snowcap is running, this will also setup Snowcap and
---connect to its socket as well.
function pinnacle.init()
require("pinnacle.grpc.protobuf").build_protos()

View file

@ -61,6 +61,13 @@ local pinnacle_v0alpha1_SetOrToggle = {
SET_OR_TOGGLE_TOGGLE = 3,
}
---@enum pinnacle.v0alpha1.Backend
local pinnacle_v0alpha1_Backend = {
BACKEND_UNSPECIFIED = 0,
BACKEND_WINDOW = 1,
BACKEND_TTY = 2,
}
---@enum pinnacle.window.v0alpha1.FullscreenOrMaximized
local pinnacle_window_v0alpha1_FullscreenOrMaximized = {
FULLSCREEN_OR_MAXIMIZED_UNSPECIFIED = 0,
@ -232,6 +239,11 @@ local pinnacle_output_v0alpha1_Transform = {
---@class pinnacle.v0alpha1.ShutdownWatchResponse
---@class pinnacle.v0alpha1.BackendRequest
---@class pinnacle.v0alpha1.BackendResponse
---@field backend pinnacle.v0alpha1.Backend?
---@class pinnacle.layout.v0alpha1.LayoutRequest
---@field geometries pinnacle.layout.v0alpha1.LayoutRequest.Geometries?
---@field layout pinnacle.layout.v0alpha1.LayoutRequest.ExplicitLayout?
@ -501,6 +513,8 @@ pinnacle.v0alpha1.PingRequest = {}
pinnacle.v0alpha1.PingResponse = {}
pinnacle.v0alpha1.ShutdownWatchRequest = {}
pinnacle.v0alpha1.ShutdownWatchResponse = {}
pinnacle.v0alpha1.BackendRequest = {}
pinnacle.v0alpha1.BackendResponse = {}
pinnacle.layout = {}
pinnacle.layout.v0alpha1 = {}
pinnacle.layout.v0alpha1.LayoutRequest = {}
@ -569,6 +583,7 @@ pinnacle.input.v0alpha1.SetLibinputSettingRequest.ClickMethod = pinnacle_input_v
pinnacle.input.v0alpha1.SetLibinputSettingRequest.ScrollMethod = pinnacle_input_v0alpha1_SetLibinputSettingRequest_ScrollMethod
pinnacle.input.v0alpha1.SetLibinputSettingRequest.TapButtonMap = pinnacle_input_v0alpha1_SetLibinputSettingRequest_TapButtonMap
pinnacle.v0alpha1.SetOrToggle = pinnacle_v0alpha1_SetOrToggle
pinnacle.v0alpha1.Backend = pinnacle_v0alpha1_Backend
pinnacle.window.v0alpha1.FullscreenOrMaximized = pinnacle_window_v0alpha1_FullscreenOrMaximized
pinnacle.window.v0alpha1.WindowState = pinnacle_window_v0alpha1_WindowState
pinnacle.render.v0alpha1.Filter = pinnacle_render_v0alpha1_Filter
@ -667,6 +682,11 @@ pinnacle.v0alpha1.PinnacleService.ShutdownWatch.service = "pinnacle.v0alpha1.Pin
pinnacle.v0alpha1.PinnacleService.ShutdownWatch.method = "ShutdownWatch"
pinnacle.v0alpha1.PinnacleService.ShutdownWatch.request = ".pinnacle.v0alpha1.ShutdownWatchRequest"
pinnacle.v0alpha1.PinnacleService.ShutdownWatch.response = ".pinnacle.v0alpha1.ShutdownWatchResponse"
pinnacle.v0alpha1.PinnacleService.Backend = {}
pinnacle.v0alpha1.PinnacleService.Backend.service = "pinnacle.v0alpha1.PinnacleService"
pinnacle.v0alpha1.PinnacleService.Backend.method = "Backend"
pinnacle.v0alpha1.PinnacleService.Backend.request = ".pinnacle.v0alpha1.BackendRequest"
pinnacle.v0alpha1.PinnacleService.Backend.response = ".pinnacle.v0alpha1.BackendResponse"
pinnacle.layout.v0alpha1.LayoutService = {}
pinnacle.layout.v0alpha1.LayoutService.Layout = {}
pinnacle.layout.v0alpha1.LayoutService.Layout.service = "pinnacle.layout.v0alpha1.LayoutService"

View file

@ -11,7 +11,6 @@ message Geometry {
optional int32 height = 4;
}
// NOTE TO SELF: If you change this you MUST change the mappings in the Lua API
enum SetOrToggle {
SET_OR_TOGGLE_UNSPECIFIED = 0;
SET_OR_TOGGLE_SET = 1;
@ -39,9 +38,22 @@ message PingResponse {
message ShutdownWatchRequest {}
message ShutdownWatchResponse {}
enum Backend {
BACKEND_UNSPECIFIED = 0;
BACKEND_WINDOW = 1;
BACKEND_TTY = 2;
}
message BackendRequest {}
message BackendResponse {
optional Backend backend = 1;
}
service PinnacleService {
rpc Quit(QuitRequest) returns (google.protobuf.Empty);
rpc ReloadConfig(ReloadConfigRequest) returns (google.protobuf.Empty);
rpc Ping(PingRequest) returns (PingResponse);
rpc ShutdownWatch(ShutdownWatchRequest) returns (stream ShutdownWatchResponse);
// Gets the currently running backend.
rpc Backend(BackendRequest) returns (BackendResponse);
}

View file

@ -1,14 +1,15 @@
use pinnacle_api::input::libinput::LibinputSetting;
use pinnacle_api::input::KeybindInfo;
use pinnacle_api::input::Keysym;
use pinnacle_api::layout::{
CornerLayout, CornerLocation, CyclingLayoutManager, DwindleLayout, FairLayout, MasterSide,
MasterStackLayout, SpiralLayout,
};
use pinnacle_api::output::OutputSetup;
use pinnacle_api::pinnacle::Backend;
use pinnacle_api::signal::WindowSignal;
use pinnacle_api::util::{Axis, Batch};
use pinnacle_api::window::rules::{DecorationMode, WindowRule, WindowRuleCondition};
use pinnacle_api::xkbcommon::xkb::Keysym;
use pinnacle_api::{
input::{Mod, MouseButton, MouseEdge},
ApiModules,
@ -16,6 +17,10 @@ use pinnacle_api::{
// Pinnacle needs to perform some setup before and after your config,
// which is what this macro does.
//
// By default, logging is disabled here because this config is embedded inside Pinnacle
// and that would cause a panic. Remove `internal_tracing = false` if you want to
// enable logging for debugging.
#[pinnacle_api::config(internal_tracing = false)]
async fn main() {
// Deconstruct to get all the APIs.
@ -34,7 +39,11 @@ async fn main() {
..
} = ApiModules::new();
let mod_key = Mod::Ctrl;
// Change the mod key to `Alt` when running as a nested window.
let mod_key = match pinnacle.backend() {
Backend::Tty => Mod::Super,
Backend::Window => Mod::Alt,
};
let terminal = "alacritty";
@ -70,9 +79,9 @@ async fn main() {
},
);
// `mod_key + alt + q` quits Pinnacle
// `mod_key + shift + q` quits Pinnacle
input.keybind(
[mod_key, Mod::Alt],
[mod_key, Mod::Shift],
'q',
|| {
#[cfg(feature = "snowcap")]
@ -86,9 +95,9 @@ async fn main() {
},
);
// `mod_key + alt + r` reloads the config
// `mod_key + ctrl + r` reloads the config
input.keybind(
[mod_key, Mod::Alt],
[mod_key, Mod::Ctrl],
'r',
|| {
pinnacle.reload_config();
@ -99,9 +108,9 @@ async fn main() {
},
);
// `mod_key + alt + c` closes the focused window
// `mod_key + shift + c` closes the focused window
input.keybind(
[mod_key, Mod::Alt],
[mod_key, Mod::Shift],
'c',
|| {
if let Some(window) = window.get_focused() {
@ -127,9 +136,9 @@ async fn main() {
},
);
// `mod_key + alt + space` toggles floating
// `mod_key + ctrl + space` toggles floating
input.keybind(
[mod_key, Mod::Alt],
[mod_key, Mod::Ctrl],
Keysym::space,
|| {
if let Some(window) = window.get_focused() {
@ -324,9 +333,9 @@ async fn main() {
},
);
// `mod_key + shift + 1-5` toggles tag "1" to "5"
// `mod_key + ctrl + 1-5` toggles tag "1" to "5"
input.keybind(
[mod_key, Mod::Shift],
[mod_key, Mod::Ctrl],
tag_name,
move || {
if let Some(tg) = tag.get(tag_name) {
@ -339,9 +348,9 @@ async fn main() {
},
);
// `mod_key + alt + 1-5` moves the focused window to tag "1" to "5"
// `mod_key + shift + 1-5` moves the focused window to tag "1" to "5"
input.keybind(
[mod_key, Mod::Alt],
[mod_key, Mod::Shift],
tag_name,
move || {
if let Some(tg) = tag.get(tag_name) {
@ -356,9 +365,9 @@ async fn main() {
},
);
// `mod_key + shift + alt + 1-5` toggles tag "1" to "5" on the focused window
// `mod_key + ctrl + shift + 1-5` toggles tag "1" to "5" on the focused window
input.keybind(
[mod_key, Mod::Shift, Mod::Alt],
[mod_key, Mod::Ctrl, Mod::Shift],
tag_name,
move || {
if let Some(tg) = tag.get(tag_name) {
@ -376,7 +385,7 @@ async fn main() {
input.set_libinput_setting(LibinputSetting::Tap(true));
// Request all windows to use client-side decorations.
// Request all windows use client-side decorations.
window.add_window_rule(
WindowRuleCondition::new().all([]),
WindowRule::new().decoration_mode(DecorationMode::ClientSide),

View file

@ -19,7 +19,6 @@ use pinnacle_api_defs::pinnacle::input::{
};
use tokio_stream::StreamExt;
use tracing::error;
use xkbcommon::xkb::Keysym;
use crate::block_on_tokio;
@ -27,6 +26,8 @@ use self::libinput::LibinputSetting;
pub mod libinput;
pub use xkbcommon::xkb::Keysym;
/// A mouse button.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum MouseButton {

View file

@ -130,7 +130,6 @@ pub use pinnacle_api_macros::config;
#[cfg(feature = "snowcap")]
pub use snowcap_api;
pub use tokio;
pub use xkbcommon;
// These are all `RwLock<Option>` instead of `OnceLock` purely for the fact that
// tonic doesn't like it when you use clients across tokio runtimes, and these are static

View file

@ -8,20 +8,34 @@
use std::time::Duration;
use pinnacle_api_defs::pinnacle::v0alpha1::{
PingRequest, QuitRequest, ReloadConfigRequest, ShutdownWatchRequest, ShutdownWatchResponse,
use pinnacle_api_defs::pinnacle::{
self,
v0alpha1::{
BackendRequest, PingRequest, QuitRequest, ReloadConfigRequest, ShutdownWatchRequest,
ShutdownWatchResponse,
},
};
use rand::RngCore;
use tonic::{Request, Streaming};
use crate::{block_on_tokio, pinnacle};
use crate::block_on_tokio;
/// A struct that allows you to quit the compositor.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Pinnacle;
/// A backend that Pinnacle runs with.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Backend {
/// Pinnacle is running in a tty, possibly started through a display manager.
Tty,
/// Pinnacle is running in a window inside another compositor, window manager,
/// or desktop environment.
Window,
}
impl Pinnacle {
/// Quit Pinnacle.
/// Quits Pinnacle.
///
/// # Examples
///
@ -31,17 +45,31 @@ impl Pinnacle {
/// ```
pub fn quit(&self) {
// Ignore errors here, the config is meant to be killed
let _ = block_on_tokio(pinnacle().quit(QuitRequest {}));
let _ = block_on_tokio(crate::pinnacle().quit(QuitRequest {}));
}
/// Reload the currently active config.
pub fn reload_config(&self) {
// Ignore errors here, the config is meant to be killed
let _ = block_on_tokio(pinnacle().reload_config(ReloadConfigRequest {}));
let _ = block_on_tokio(crate::pinnacle().reload_config(ReloadConfigRequest {}));
}
/// Gets the currently running [`Backend`].
pub fn backend(&self) -> Backend {
let backend = block_on_tokio(crate::pinnacle().backend(BackendRequest {}))
.unwrap()
.into_inner()
.backend();
match backend {
pinnacle::v0alpha1::Backend::Unspecified => panic!("received unspecified backend"),
pinnacle::v0alpha1::Backend::Window => Backend::Window,
pinnacle::v0alpha1::Backend::Tty => Backend::Tty,
}
}
pub(crate) async fn shutdown_watch(&self) -> Streaming<ShutdownWatchResponse> {
pinnacle()
crate::pinnacle()
.shutdown_watch(ShutdownWatchRequest {})
.await
.unwrap()
@ -58,7 +86,7 @@ impl Pinnacle {
});
request.set_timeout(Duration::from_secs(10));
let response = pinnacle()
let response = crate::pinnacle()
.ping(request)
.await
.map_err(|status| status.to_string())?;

View file

@ -5,6 +5,7 @@ pub mod window;
use std::{ffi::OsString, pin::Pin, process::Stdio};
use pinnacle_api_defs::pinnacle::{
self,
input::v0alpha1::{
input_service_server,
set_libinput_setting_request::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
@ -33,8 +34,8 @@ use pinnacle_api_defs::pinnacle::{
},
},
v0alpha1::{
pinnacle_service_server, PingRequest, PingResponse, QuitRequest, ReloadConfigRequest,
SetOrToggle, ShutdownWatchRequest, ShutdownWatchResponse,
pinnacle_service_server, BackendRequest, BackendResponse, PingRequest, PingResponse,
QuitRequest, ReloadConfigRequest, SetOrToggle, ShutdownWatchRequest, ShutdownWatchResponse,
},
};
use smithay::{
@ -236,6 +237,26 @@ impl pinnacle_service_server::PinnacleService for PinnacleService {
state.pinnacle.config.shutdown_sender.replace(sender);
})
}
async fn backend(
&self,
_request: Request<BackendRequest>,
) -> Result<Response<BackendResponse>, Status> {
run_unary(&self.sender, |state| {
let backend = match &state.backend {
crate::backend::Backend::Winit(_) => pinnacle::v0alpha1::Backend::Window,
crate::backend::Backend::Udev(_) => pinnacle::v0alpha1::Backend::Tty,
#[cfg(feature = "testing")]
crate::backend::Backend::Dummy(_) => pinnacle::v0alpha1::Backend::Tty, // unused
};
let mut response = BackendResponse::default();
response.set_backend(backend);
response
})
.await
}
}
pub struct InputService {
@ -263,19 +284,11 @@ impl input_service_server::InputService for InputService {
let modifiers = request
.modifiers()
.fold(ModifierMask::empty(), |acc, modifier| match modifier {
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Unspecified => acc,
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Shift => {
acc | ModifierMask::SHIFT
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Ctrl => {
acc | ModifierMask::CTRL
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Alt => {
acc | ModifierMask::ALT
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Super => {
acc | ModifierMask::SUPER
}
pinnacle::input::v0alpha1::Modifier::Unspecified => acc,
pinnacle::input::v0alpha1::Modifier::Shift => acc | ModifierMask::SHIFT,
pinnacle::input::v0alpha1::Modifier::Ctrl => acc | ModifierMask::CTRL,
pinnacle::input::v0alpha1::Modifier::Alt => acc | ModifierMask::ALT,
pinnacle::input::v0alpha1::Modifier::Super => acc | ModifierMask::SUPER,
});
let key = request
.key
@ -331,19 +344,11 @@ impl input_service_server::InputService for InputService {
let modifiers = request
.modifiers()
.fold(ModifierMask::empty(), |acc, modifier| match modifier {
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Unspecified => acc,
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Shift => {
acc | ModifierMask::SHIFT
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Ctrl => {
acc | ModifierMask::CTRL
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Alt => {
acc | ModifierMask::ALT
}
pinnacle_api_defs::pinnacle::input::v0alpha1::Modifier::Super => {
acc | ModifierMask::SUPER
}
pinnacle::input::v0alpha1::Modifier::Unspecified => acc,
pinnacle::input::v0alpha1::Modifier::Shift => acc | ModifierMask::SHIFT,
pinnacle::input::v0alpha1::Modifier::Ctrl => acc | ModifierMask::CTRL,
pinnacle::input::v0alpha1::Modifier::Alt => acc | ModifierMask::ALT,
pinnacle::input::v0alpha1::Modifier::Super => acc | ModifierMask::SUPER,
});
let button = request
.button
@ -847,12 +852,10 @@ impl tag_service_server::TagService for TagService {
if tag.set_active(active) {
state.pinnacle.signal_state.tag_active.signal(|buf| {
buf.push_back(
pinnacle_api_defs::pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(tag.id().to_inner()),
active: Some(active),
},
);
buf.push_back(pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(tag.id().to_inner()),
active: Some(active),
});
});
}
@ -888,23 +891,19 @@ impl tag_service_server::TagService for TagService {
for op_tag in op_state.tags.iter() {
if op_tag.set_active(false) {
state.pinnacle.signal_state.tag_active.signal(|buf| {
buf.push_back(
pinnacle_api_defs::pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(op_tag.id().to_inner()),
active: Some(false),
},
);
buf.push_back(pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(op_tag.id().to_inner()),
active: Some(false),
});
});
}
}
if tag.set_active(true) {
state.pinnacle.signal_state.tag_active.signal(|buf| {
buf.push_back(
pinnacle_api_defs::pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(tag.id().to_inner()),
active: Some(true),
},
);
buf.push_back(pinnacle::signal::v0alpha1::TagActiveResponse {
tag_id: Some(tag.id().to_inner()),
active: Some(true),
});
});
}
});