mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Add basic API batching for Lua
This commit is contained in:
parent
09e20e3a30
commit
20af3a116c
7 changed files with 140 additions and 21 deletions
|
@ -5,7 +5,7 @@ source = {
|
|||
}
|
||||
description = {
|
||||
homepage = "*** please enter a project homepage ***",
|
||||
license = "*** please specify a license ***",
|
||||
license = "MPL 2.0",
|
||||
}
|
||||
dependencies = {
|
||||
"lua ~> 5.4",
|
||||
|
@ -25,5 +25,6 @@ build = {
|
|||
["pinnacle.process"] = "pinnacle/process.lua",
|
||||
["pinnacle.tag"] = "pinnacle/tag.lua",
|
||||
["pinnacle.window"] = "pinnacle/window.lua",
|
||||
["pinnacle.util"] = "pinnacle/util.lua",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ local pb = require("pb")
|
|||
---Create appropriate headers for a gRPC request.
|
||||
---@param service string The desired service
|
||||
---@param method string The desired method within the service
|
||||
---@return HttpHeaders
|
||||
local function create_request_headers(service, method)
|
||||
local req_headers = headers.new()
|
||||
req_headers:append(":method", "POST")
|
||||
|
@ -34,6 +33,13 @@ local function new_conn()
|
|||
return conn
|
||||
end
|
||||
|
||||
---@class CqueuesLoop
|
||||
---@field loop function
|
||||
---@field wrap fun(self: self, fn: function)
|
||||
|
||||
---@class H2Connection
|
||||
---@field new_stream function
|
||||
|
||||
---@nodoc
|
||||
---@class Client
|
||||
---@field conn H2Connection
|
||||
|
@ -80,8 +86,8 @@ function client.unary_request(grpc_request_params)
|
|||
stream:write_headers(create_request_headers(service, method), false)
|
||||
stream:write_chunk(body, true)
|
||||
|
||||
local response_headers = stream:get_headers()
|
||||
-- TODO: check headers for errors
|
||||
-- TODO: check response headers for errors
|
||||
local _ = stream:get_headers()
|
||||
|
||||
local response_body = stream:get_next_chunk()
|
||||
|
||||
|
@ -95,6 +101,7 @@ function client.unary_request(grpc_request_params)
|
|||
stream:shutdown()
|
||||
|
||||
-- Skip the 1-byte compressed flag and the 4-byte message length
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local response_body = response_body:sub(6)
|
||||
local response = pb.decode(response_type, response_body)
|
||||
|
||||
|
@ -135,14 +142,16 @@ function client.server_streaming_request(grpc_request_params, callback)
|
|||
stream:write_headers(create_request_headers(service, method), false)
|
||||
stream:write_chunk(body, true)
|
||||
|
||||
local response_headers = stream:get_headers()
|
||||
-- TODO: check headers for errors
|
||||
-- TODO: check response headers for errors
|
||||
local _ = stream:get_headers()
|
||||
|
||||
client.loop:wrap(function()
|
||||
for response_body in stream:each_chunk() do
|
||||
-- Skip the 1-byte compressed flag and the 4-byte message length
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local response_body = response_body:sub(6)
|
||||
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local success, obj = pcall(pb.decode, response_type, response_body)
|
||||
if not success then
|
||||
print(obj)
|
||||
|
|
|
@ -103,11 +103,11 @@ local mouse_edge_values = {
|
|||
---
|
||||
---This module provides utilities to set key- and mousebinds as well as change keyboard settings.
|
||||
---@class Input
|
||||
---@field private btn table
|
||||
---@field private mouse_button_values table
|
||||
local input = {
|
||||
key = require("pinnacle.input.keys"),
|
||||
}
|
||||
input.btn = mouse_button_values
|
||||
input.mouse_button_values = mouse_button_values
|
||||
|
||||
---Set a keybind. If called with an already existing keybind, it gets replaced.
|
||||
---
|
||||
|
@ -188,6 +188,7 @@ end
|
|||
---@param edge MouseEdge "press" or "release" to trigger on button press or release
|
||||
---@param action fun() The function to run when the bind is triggered
|
||||
function input.mousebind(mods, button, edge, action)
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local edge = mouse_edge_values[edge]
|
||||
|
||||
local mod_values = {}
|
||||
|
|
|
@ -76,6 +76,8 @@ output.handle = output_handle
|
|||
---
|
||||
---@return OutputHandle[]
|
||||
function output.get_all()
|
||||
-- Not going to batch these because I doubt people would have that many monitors
|
||||
|
||||
local response = client.unary_request(build_grpc_request_params("Get", {}))
|
||||
|
||||
---@type OutputHandle[]
|
||||
|
@ -326,6 +328,7 @@ end
|
|||
function OutputHandle:props()
|
||||
local response = client.unary_request(build_grpc_request_params("GetProperties", { output_name = self.name }))
|
||||
|
||||
---@diagnostic disable-next-line: invisible
|
||||
local handles = require("pinnacle.tag").handle.new_from_table(response.tag_ids or {})
|
||||
|
||||
response.tags = handles
|
||||
|
|
|
@ -122,10 +122,20 @@ function tag.get(name, output)
|
|||
|
||||
local handles = tag.get_all()
|
||||
|
||||
for _, handle in ipairs(handles) do
|
||||
local props = handle:props()
|
||||
if props.output and props.output.name == output.name and props.name == name then
|
||||
return handle
|
||||
---@type (fun(): TagProperties)[]
|
||||
local requests = {}
|
||||
|
||||
for i, handle in ipairs(handles) do
|
||||
requests[i] = function()
|
||||
return handle:props()
|
||||
end
|
||||
end
|
||||
|
||||
local props = require("pinnacle.util").batch(requests)
|
||||
|
||||
for i, prop in ipairs(props) do
|
||||
if prop.output and prop.output.name == output.name and prop.name == name then
|
||||
return handles[i]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -248,12 +258,13 @@ function tag.new_layout_cycler(layouts)
|
|||
---@type LayoutCycler
|
||||
return {
|
||||
next = function(output)
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local output = output or require("pinnacle.output").get_focused()
|
||||
if not output then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = output:props().tags
|
||||
local tags = output:props().tags or {}
|
||||
|
||||
for _, tg in ipairs(tags) do
|
||||
if tg:props().active then
|
||||
|
@ -276,12 +287,13 @@ function tag.new_layout_cycler(layouts)
|
|||
end
|
||||
end,
|
||||
prev = function(output)
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local output = output or require("pinnacle.output").get_focused()
|
||||
if not output then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = output:props().tags
|
||||
local tags = output:props().tags or {}
|
||||
|
||||
for _, tg in ipairs(tags) do
|
||||
if tg:props().active then
|
||||
|
@ -321,7 +333,7 @@ function TagHandle:remove()
|
|||
client.unary_request(build_grpc_request_params("Remove", { tag_ids = { self.id } }))
|
||||
end
|
||||
|
||||
local _layouts = {
|
||||
local layout_name_to_code = {
|
||||
master_stack = 1,
|
||||
dwindle = 2,
|
||||
spiral = 3,
|
||||
|
@ -351,7 +363,8 @@ local _layouts = {
|
|||
---
|
||||
---@param layout Layout
|
||||
function TagHandle:set_layout(layout)
|
||||
local layout = _layouts[layout]
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
local layout = layout_name_to_code[layout]
|
||||
|
||||
client.unary_request(build_grpc_request_params("SetLayout", {
|
||||
tag_id = self.id,
|
||||
|
@ -421,6 +434,7 @@ function TagHandle:props()
|
|||
return {
|
||||
active = response.active,
|
||||
name = response.name,
|
||||
---@diagnostic disable-next-line: invisible
|
||||
output = response.output_name and require("pinnacle.output").handle.new(response.output_name),
|
||||
}
|
||||
end
|
||||
|
|
76
api/lua/pinnacle/util.lua
Normal file
76
api/lua/pinnacle/util.lua
Normal file
|
@ -0,0 +1,76 @@
|
|||
-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
-- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
---Utility functions.
|
||||
---@class Util
|
||||
local util = {}
|
||||
|
||||
---Batch a set of requests that will be sent to the compositor all at once.
|
||||
---
|
||||
---Normally, all API calls are blocking. For example, calling `Window.get_all`
|
||||
---then calling `WindowHandle.props` on each returned window handle will block
|
||||
---after each `props` call waiting for the compositor to respond:
|
||||
---
|
||||
---```
|
||||
---local handles = Window.get_all()
|
||||
---
|
||||
--- -- Collect all the props into this table
|
||||
---local props = {}
|
||||
---
|
||||
--- -- This for loop will block after each call. If the compositor is running slowly
|
||||
--- -- for whatever reason, this will take a long time to complete as it requests
|
||||
--- -- properties sequentially.
|
||||
---for i, handle in ipairs(handles) do
|
||||
--- props[i] = handle:props()
|
||||
---end
|
||||
---```
|
||||
---
|
||||
---In order to mitigate this issue, you can batch up a set of API calls using this function.
|
||||
---This will send all requests to the compositor at once without blocking, then wait for the compositor
|
||||
---to respond.
|
||||
---
|
||||
---You must wrap each request in a function, otherwise they would just get
|
||||
---evaluated at the callsite in a blocking manner.
|
||||
---
|
||||
---### Example
|
||||
---```lua
|
||||
---local handles = window.get_all()
|
||||
---
|
||||
--- ---@type (fun(): WindowProperties)[]
|
||||
---local requests = {}
|
||||
---
|
||||
--- -- Wrap each request to `props` in another function
|
||||
---for i, handle in ipairs(handles) do
|
||||
--- requests[i] = function()
|
||||
--- return handle:props()
|
||||
--- end
|
||||
---end
|
||||
---
|
||||
--- -- Batch send these requests
|
||||
---local props = require("pinnacle.util").batch(requests)
|
||||
--- -- `props` now contains the `WindowProperties` of all the windows above
|
||||
---```
|
||||
---
|
||||
---@generic T
|
||||
---
|
||||
---@param requests (fun(): T)[] The requests that you want to batch up, wrapped in a function.
|
||||
---
|
||||
---@return T[] responses The results of each request in the same order that they were in `requests`.
|
||||
function util.batch(requests)
|
||||
local loop = require("cqueues").new()
|
||||
|
||||
local responses = {}
|
||||
|
||||
for i, request in ipairs(requests) do
|
||||
loop:wrap(function()
|
||||
responses[i] = request()
|
||||
end)
|
||||
end
|
||||
|
||||
loop:loop()
|
||||
|
||||
return responses
|
||||
end
|
||||
|
||||
return util
|
|
@ -102,9 +102,20 @@ end
|
|||
function window.get_focused()
|
||||
local handles = window.get_all()
|
||||
|
||||
for _, handle in ipairs(handles) do
|
||||
if handle:props().focused then
|
||||
return handle
|
||||
---@type (fun(): WindowProperties)[]
|
||||
local requests = {}
|
||||
|
||||
for i, handle in ipairs(handles) do
|
||||
requests[i] = function()
|
||||
return handle:props()
|
||||
end
|
||||
end
|
||||
|
||||
local props = require("pinnacle.util").batch(requests)
|
||||
|
||||
for i, prop in ipairs(props) do
|
||||
if prop.focused then
|
||||
return handles[i]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -124,7 +135,8 @@ end
|
|||
---```
|
||||
---@param button MouseButton The button that will initiate the move
|
||||
function window.begin_move(button)
|
||||
local button = require("pinnacle.input").btn[button]
|
||||
---@diagnostic disable-next-line: redefined-local, invisible
|
||||
local button = require("pinnacle.input").mouse_button_values[button]
|
||||
client.unary_request(build_grpc_request_params("MoveGrab", { button = button }))
|
||||
end
|
||||
|
||||
|
@ -141,7 +153,8 @@ end
|
|||
---```
|
||||
---@param button MouseButton The button that will initiate the resize
|
||||
function window.begin_resize(button)
|
||||
local button = require("pinnacle.input").btn[button]
|
||||
---@diagnostic disable-next-line: redefined-local, invisible
|
||||
local button = require("pinnacle.input").mouse_button_values[button]
|
||||
client.unary_request(build_grpc_request_params("ResizeGrab", { button = button }))
|
||||
end
|
||||
|
||||
|
@ -274,6 +287,7 @@ function window.add_window_rule(rule)
|
|||
end
|
||||
|
||||
if rule.rule.output then
|
||||
---@diagnostic disable-next-line: assign-type-mismatch
|
||||
rule.rule.output = rule.rule.output.name
|
||||
end
|
||||
|
||||
|
@ -502,6 +516,7 @@ function WindowHandle:props()
|
|||
|
||||
response.fullscreen_or_maximized = _fullscreen_or_maximized_keys[response.fullscreen_or_maximized]
|
||||
|
||||
---@diagnostic disable-next-line: invisible
|
||||
response.tags = response.tag_ids and require("pinnacle.tag").handle.new_from_table(response.tag_ids)
|
||||
response.tag_ids = nil
|
||||
|
||||
|
|
Loading…
Reference in a new issue