diff --git a/README.md b/README.md index 39aab1539..df4246ba1 100644 --- a/README.md +++ b/README.md @@ -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 )`, Pinnacle will fallback to the -> embedded Rust config. +> If you are using a Lua config and have not run `eval $(luarocks path --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. > Other branches for Lua soontm # 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 | -|----------------------------------------------|------------------------------------| -| Ctrl + s | Show the keybind overlay | -| Ctrl + Mouse left drag | Move window | -| Ctrl + Mouse right drag| Resize window | -| CtrlAlt + q | Quit Pinnacle | -| CtrlAlt + c | Close window | -| Ctrl + Return | Spawn [Alacritty](https://github.com/alacritty/alacritty) (you can change this in the config)| -| CtrlAlt + Space | Toggle between floating and tiled | -| Ctrl + f | Toggle fullscreen | -| Ctrl + m | Toggle maximized | -| Ctrl + Space | Cycle to the next layout | -| CtrlShift + Space | Cycle to the previous layout | -| Ctrl + 1 to 5 | Switch to tag `1` to `5` | -| CtrlShift + 1 to 5 | Toggle tag `1` to `5` | -| CtrlAlt + 1 to 5 | Move a window to tag `1` to `5` | -| CtrlAltShift + 1 to 5 | Toggle tag `1` to `5` on a window | + +The following are the default controls, mirroring Awesome's defaults. + +Mod is Super when running in a tty and Alt when running as a nested window. + +| Binding | Action | +|------------------------------------------------------------------------------|-----------------------------------| +| Mod + s | Show the keybind overlay | +| Mod + Mouse left drag | Move window | +| Mod + Mouse right drag | Resize window | +| ModShift + q | Quit Pinnacle | +| ModCtrl + r | Reload the config | +| ModShift + c | Close window | +| Mod + Return | Spawn [Alacritty](https://github.com/alacritty/alacritty) (you can change this in the config) | +| ModCtrl + Space | Toggle floating | +| Mod + f | Toggle fullscreen | +| Mod + m | Toggle maximized | +| Mod + Space | Cycle to the next layout | +| ModShift + Space | Cycle to the previous layout | +| Mod + 1 to 5 | Switch to tag `1` to `5` | +| ModCtrl + 1 to 5 | Toggle tag `1` to `5` | +| ModShift + 1 to 5 | Move a window to tag `1` to `5` | +| ModCtrlShift + 1 to 5 | 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 diff --git a/api/lua/examples/default/default_config.lua b/api/lua/examples/default/default_config.lua index d49b6d420..fab8bcca9 100644 --- a/api/lua/examples/default/default_config.lua +++ b/api/lua/examples/default/default_config.lua @@ -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 = {}, diff --git a/api/lua/examples/default_no_snowcap/.luarc.json b/api/lua/examples/default_no_snowcap/.luarc.json deleted file mode 100644 index a1dde7119..000000000 --- a/api/lua/examples/default_no_snowcap/.luarc.json +++ /dev/null @@ -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", -} diff --git a/api/lua/examples/default_no_snowcap/default_config.lua b/api/lua/examples/default_no_snowcap/default_config.lua deleted file mode 100644 index 07f670231..000000000 --- a/api/lua/examples/default_no_snowcap/default_config.lua +++ /dev/null @@ -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) diff --git a/api/lua/examples/default_no_snowcap/metaconfig.toml b/api/lua/examples/default_no_snowcap/metaconfig.toml deleted file mode 100644 index 68239ce15..000000000 --- a/api/lua/examples/default_no_snowcap/metaconfig.toml +++ /dev/null @@ -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" diff --git a/api/lua/pinnacle.lua b/api/lua/pinnacle.lua index c1ad3ffee..c1cb4c871 100644 --- a/api/lua/pinnacle.lua +++ b/api/lua/pinnacle.lua @@ -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() diff --git a/api/lua/pinnacle/grpc/defs.lua b/api/lua/pinnacle/grpc/defs.lua index d4c8518d4..2233d879a 100644 --- a/api/lua/pinnacle/grpc/defs.lua +++ b/api/lua/pinnacle/grpc/defs.lua @@ -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" diff --git a/api/protocol/pinnacle/v0alpha1/pinnacle.proto b/api/protocol/pinnacle/v0alpha1/pinnacle.proto index 43cb543a3..a70df3609 100644 --- a/api/protocol/pinnacle/v0alpha1/pinnacle.proto +++ b/api/protocol/pinnacle/v0alpha1/pinnacle.proto @@ -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); } diff --git a/api/rust/examples/default_config/main.rs b/api/rust/examples/default_config/main.rs index e5199404d..fc38bd96e 100644 --- a/api/rust/examples/default_config/main.rs +++ b/api/rust/examples/default_config/main.rs @@ -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), diff --git a/api/rust/src/input.rs b/api/rust/src/input.rs index 3d868e5f0..cb722ef37 100644 --- a/api/rust/src/input.rs +++ b/api/rust/src/input.rs @@ -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 { diff --git a/api/rust/src/lib.rs b/api/rust/src/lib.rs index 6e303c5aa..5436c253d 100644 --- a/api/rust/src/lib.rs +++ b/api/rust/src/lib.rs @@ -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