mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-25 09:59:21 +01:00
Re-add lost API functionality, fix msg ordering
This commit is contained in:
parent
3ddd57c63f
commit
fb63e7ada3
8 changed files with 444 additions and 253 deletions
|
@ -31,7 +31,7 @@
|
|||
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
---@class _Request
|
||||
---@class __Request
|
||||
--Windows
|
||||
---@field GetWindowProps { window_id: WindowId }
|
||||
--Outputs
|
||||
|
@ -39,11 +39,12 @@
|
|||
--Tags
|
||||
---@field GetTagProps { tag_id: TagId }
|
||||
|
||||
---@alias Request _Request | "GetWindows" | "GetOutputs" | "GetTags"
|
||||
---@alias _Request __Request | "GetWindows" | "GetOutputs" | "GetTags"
|
||||
---@alias Request { request_id: integer, request: _Request }
|
||||
|
||||
---@class IncomingMsg
|
||||
---@field CallCallback { callback_id: integer, args: Args }
|
||||
---@field RequestResponse { response: RequestResponse }
|
||||
---@field CallCallback { callback_id: integer, args: Args? }
|
||||
---@field RequestResponse { request_id: integer, response: RequestResponse }
|
||||
|
||||
---@class Args
|
||||
---@field Spawn { stdout: string?, stderr: string?, exit_code: integer?, exit_msg: string? }
|
||||
|
@ -51,6 +52,7 @@
|
|||
|
||||
---@alias WindowId integer
|
||||
---@alias TagId integer
|
||||
---@alias RequestId integer
|
||||
---@alias OutputName string
|
||||
|
||||
---@class RequestResponse
|
||||
|
|
|
@ -4,67 +4,64 @@
|
|||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
---@class OutputGlobal
|
||||
local output_global = {}
|
||||
|
||||
---@class Output A display.
|
||||
---@field private _name string The name of this output (or rather, of its connector).
|
||||
local op = {}
|
||||
local output = {}
|
||||
|
||||
---Get this output's name. This is something like "eDP-1" or "HDMI-A-0".
|
||||
---@return string
|
||||
function op:name()
|
||||
function output:name()
|
||||
return self._name
|
||||
end
|
||||
|
||||
---Get all tags on this output. See `tag.get_on_output`.
|
||||
---@return Tag[]
|
||||
function op:tags()
|
||||
function output:tags()
|
||||
return require("tag").get_on_output(self)
|
||||
end
|
||||
|
||||
---Add tags to this output. See `tag.add`.
|
||||
---@param ... string The names of the tags you want to add.
|
||||
---@overload fun(self: self, tag_names: string[])
|
||||
function op:add_tags(...)
|
||||
function output:add_tags(...)
|
||||
require("tag").add(self, ...)
|
||||
end
|
||||
|
||||
---Get this output's make.
|
||||
---@return string|nil
|
||||
function op:make()
|
||||
SendRequest({
|
||||
function output:make()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
return props.make
|
||||
end
|
||||
|
||||
---Get this output's model.
|
||||
---@return string|nil
|
||||
function op:model()
|
||||
SendRequest({
|
||||
function output:model()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
return props.model
|
||||
end
|
||||
|
||||
---Get this output's location in the global space.
|
||||
---@return { x: integer, y: integer }|nil
|
||||
function op:loc()
|
||||
SendRequest({
|
||||
function output:loc()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
if props.loc == nil then
|
||||
return nil
|
||||
|
@ -75,14 +72,12 @@ end
|
|||
|
||||
---Get this output's resolution in pixels.
|
||||
---@return { w: integer, h: integer }|nil
|
||||
function op:res()
|
||||
SendRequest({
|
||||
function output:res()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
if props.res == nil then
|
||||
return nil
|
||||
|
@ -94,28 +89,24 @@ end
|
|||
---Get this output's refresh rate in millihertz.
|
||||
---For example, 60Hz will be returned as 60000.
|
||||
---@return integer|nil
|
||||
function op:refresh_rate()
|
||||
SendRequest({
|
||||
function output:refresh_rate()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
return props.refresh_rate
|
||||
end
|
||||
|
||||
---Get this output's physical size in millimeters.
|
||||
---@return { w: integer, h: integer }|nil
|
||||
function op:physical_size()
|
||||
SendRequest({
|
||||
function output:physical_size()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
if props.physical_size == nil then
|
||||
return nil
|
||||
|
@ -126,14 +117,12 @@ end
|
|||
|
||||
---Get whether or not this output is focused. This is currently defined as having the cursor on it.
|
||||
---@return boolean|nil
|
||||
function op:focused()
|
||||
SendRequest({
|
||||
function output:focused()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = self._name,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local props = response.RequestResponse.response.OutputProps
|
||||
return props.focused
|
||||
end
|
||||
|
@ -145,7 +134,7 @@ local function new_output(output_name)
|
|||
---@type Output
|
||||
local o = { _name = output_name }
|
||||
-- Copy functions over
|
||||
for k, v in pairs(op) do
|
||||
for k, v in pairs(output) do
|
||||
o[k] = v
|
||||
end
|
||||
|
||||
|
@ -154,9 +143,6 @@ end
|
|||
|
||||
------------------------------------------------------
|
||||
|
||||
---@class OutputGlobal
|
||||
local output = {}
|
||||
|
||||
---Get an output by its name.
|
||||
---
|
||||
---"Name" in this sense does not mean its model or manufacturer;
|
||||
|
@ -170,9 +156,8 @@ local output = {}
|
|||
---```
|
||||
---@param name string The name of the output.
|
||||
---@return Output|nil output The output, or nil if none have the provided name.
|
||||
function output.get_by_name(name)
|
||||
SendRequest("GetOutputs")
|
||||
local response = ReadMsg()
|
||||
function output_global.get_by_name(name)
|
||||
local response = ReadMsg(SendRequest("GetOutputs"))
|
||||
local output_names = response.RequestResponse.response.Outputs.output_names
|
||||
|
||||
for _, output_name in pairs(output_names) do
|
||||
|
@ -190,9 +175,8 @@ end
|
|||
---This is something like "DELL E2416H" or whatever gibberish monitor manufacturers call their displays.
|
||||
---@param model string The model of the output(s).
|
||||
---@return Output[] outputs All outputs with this model.
|
||||
function output.get_by_model(model)
|
||||
SendRequest("GetOutputs")
|
||||
local response = ReadMsg()
|
||||
function output_global.get_by_model(model)
|
||||
local response = ReadMsg(SendRequest("GetOutputs"))
|
||||
local output_names = response.RequestResponse.response.Outputs.output_names
|
||||
|
||||
---@type Output[]
|
||||
|
@ -212,10 +196,8 @@ end
|
|||
---@param width integer The width of the outputs, in pixels.
|
||||
---@param height integer The height of the outputs, in pixels.
|
||||
---@return Output[] outputs All outputs with this resolution.
|
||||
function output.get_by_res(width, height)
|
||||
SendRequest("GetOutputs")
|
||||
|
||||
local response = ReadMsg()
|
||||
function output_global.get_by_res(width, height)
|
||||
local response = ReadMsg(SendRequest("GetOutputs"))
|
||||
|
||||
local output_names = response.RequestResponse.response.Outputs.output_names
|
||||
|
||||
|
@ -251,9 +233,8 @@ end
|
|||
---local tags = output.get_focused():tags() -- will NOT warn for nil
|
||||
---```
|
||||
---@return Output|nil output The output, or nil if none are focused.
|
||||
function output.get_focused()
|
||||
SendRequest("GetOutputs")
|
||||
local response = ReadMsg()
|
||||
function output_global.get_focused()
|
||||
local response = ReadMsg(SendRequest("GetOutputs"))
|
||||
local output_names = response.RequestResponse.response.Outputs.output_names
|
||||
|
||||
for _, output_name in pairs(output_names) do
|
||||
|
@ -274,7 +255,7 @@ end
|
|||
---Please note: this function will be run *after* Pinnacle processes your entire config.
|
||||
---For example, if you define tags in `func` but toggle them directly after `connect_for_all`, nothing will happen as the tags haven't been added yet.
|
||||
---@param func fun(output: Output) The function that will be run.
|
||||
function output.connect_for_all(func)
|
||||
function output_global.connect_for_all(func)
|
||||
---@param args Args
|
||||
table.insert(CallbackTable, function(args)
|
||||
local args = args.ConnectForAllOutputs
|
||||
|
@ -290,14 +271,12 @@ end
|
|||
---Get the output the specified tag is on.
|
||||
---@param tag Tag
|
||||
---@return Output|nil
|
||||
function output.get_for_tag(tag)
|
||||
SendRequest({
|
||||
function output_global.get_for_tag(tag)
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetTagProps = {
|
||||
tag_id = tag:id(),
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local output_name = response.RequestResponse.response.TagProps.output_name
|
||||
|
||||
if output_name == nil then
|
||||
|
@ -307,4 +286,4 @@ function output.get_for_tag(tag)
|
|||
end
|
||||
end
|
||||
|
||||
return output
|
||||
return output_global
|
||||
|
|
|
@ -9,6 +9,33 @@ local msgpack = require("msgpack")
|
|||
|
||||
local SOCKET_PATH = "/tmp/pinnacle_socket"
|
||||
|
||||
--[[ rPrint(struct, [limit], [indent]) Recursively print arbitrary data.
|
||||
Set limit (default 100) to stanch infinite loops.
|
||||
Indents tables as [KEY] VALUE, nested tables as [KEY] [KEY]...[KEY] VALUE
|
||||
Set indent ("") to prefix each line: Mytable [KEY] [KEY]...[KEY] VALUE
|
||||
--]]
|
||||
function RPrint(s, l, i) -- recursive Print (structure, limit, indent)
|
||||
l = l or 100
|
||||
i = i or "" -- default item limit, indent string
|
||||
if l < 1 then
|
||||
print("ERROR: Item limit reached.")
|
||||
return l - 1
|
||||
end
|
||||
local ts = type(s)
|
||||
if ts ~= "table" then
|
||||
print(i, ts, s)
|
||||
return l - 1
|
||||
end
|
||||
print(i, ts) -- print "table"
|
||||
for k, v in pairs(s) do -- print "[KEY] VALUE"
|
||||
l = RPrint(v, l, i .. "\t[" .. tostring(k) .. "]")
|
||||
if l < 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
return l
|
||||
end
|
||||
|
||||
---Read the specified number of bytes.
|
||||
---@param socket_fd integer The socket file descriptor
|
||||
---@param count integer The amount of bytes to read
|
||||
|
@ -85,6 +112,7 @@ function pinnacle.setup(config_func)
|
|||
---This is an internal global function used to send serialized messages to the Pinnacle server.
|
||||
---@param data Msg
|
||||
function SendMsg(data)
|
||||
-- RPrint(data)
|
||||
local encoded = msgpack.encode(data)
|
||||
assert(encoded)
|
||||
-- print(encoded)
|
||||
|
@ -93,54 +121,98 @@ function pinnacle.setup(config_func)
|
|||
socket.send(socket_fd, encoded)
|
||||
end
|
||||
|
||||
local request_id = 0
|
||||
---Get the next request id.
|
||||
---@return integer
|
||||
local function next_request_id()
|
||||
local ret = request_id
|
||||
request_id = request_id + 1
|
||||
return ret
|
||||
end
|
||||
|
||||
---@type table<integer, IncomingMsg>
|
||||
local unread_req_msgs = {}
|
||||
---@type table<integer, IncomingMsg>
|
||||
local unread_cb_msgs = {}
|
||||
|
||||
---This is an internal global function used to send requests to the Pinnacle server for information.
|
||||
---@param data Request
|
||||
---@param data _Request
|
||||
---@return RequestId
|
||||
function SendRequest(data)
|
||||
local req_id = next_request_id()
|
||||
SendMsg({
|
||||
Request = data,
|
||||
Request = {
|
||||
request_id = req_id,
|
||||
request = data,
|
||||
},
|
||||
})
|
||||
return req_id
|
||||
end
|
||||
|
||||
---This is an internal global function used to read messages sent from the server.
|
||||
---These are used to call user-defined functions and provide requested information.
|
||||
function ReadMsg()
|
||||
local msg_len_bytes, err_msg, err_num = read_exact(socket_fd, 4)
|
||||
assert(msg_len_bytes)
|
||||
---@return IncomingMsg
|
||||
---@param req_id integer? A request id if you're looking for that specific message.
|
||||
function ReadMsg(req_id)
|
||||
while true do
|
||||
if req_id then
|
||||
if unread_req_msgs[req_id] then
|
||||
local msg = unread_req_msgs[req_id]
|
||||
unread_req_msgs[req_id] = nil -- INFO: is this a reference?
|
||||
return msg
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: break here if error in read_exact
|
||||
local msg_len_bytes, err_msg, err_num = read_exact(socket_fd, 4)
|
||||
assert(msg_len_bytes)
|
||||
|
||||
---@type integer
|
||||
local msg_len = string.unpack("=I4", msg_len_bytes)
|
||||
-- print(msg_len)
|
||||
-- TODO: break here if error in read_exact
|
||||
|
||||
local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len)
|
||||
assert(msg_bytes)
|
||||
-- print(msg_bytes)
|
||||
---@type integer
|
||||
local msg_len = string.unpack("=I4", msg_len_bytes)
|
||||
-- print(msg_len)
|
||||
|
||||
---@type IncomingMsg
|
||||
local tb = msgpack.decode(msg_bytes)
|
||||
-- print(msg_bytes)
|
||||
local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len)
|
||||
assert(msg_bytes)
|
||||
-- print(msg_bytes)
|
||||
|
||||
return tb
|
||||
---@type IncomingMsg
|
||||
local inc_msg = msgpack.decode(msg_bytes)
|
||||
-- print(msg_bytes)
|
||||
|
||||
if req_id then
|
||||
if inc_msg.CallCallback then
|
||||
unread_cb_msgs[inc_msg.CallCallback.callback_id] = inc_msg
|
||||
elseif inc_msg.RequestResponse.request_id ~= req_id then
|
||||
unread_req_msgs[inc_msg.RequestResponse.request_id] = inc_msg
|
||||
else
|
||||
return inc_msg
|
||||
end
|
||||
else
|
||||
return inc_msg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
config_func(pinnacle)
|
||||
|
||||
while true do
|
||||
local tb = ReadMsg()
|
||||
|
||||
if tb.CallCallback and tb.CallCallback.callback_id then
|
||||
if tb.CallCallback.args then -- TODO: can just inline
|
||||
CallbackTable[tb.CallCallback.callback_id](tb.CallCallback.args)
|
||||
else
|
||||
CallbackTable[tb.CallCallback.callback_id](nil)
|
||||
end
|
||||
for cb_id, inc_msg in pairs(unread_cb_msgs) do
|
||||
CallbackTable[inc_msg.CallCallback.callback_id](inc_msg.CallCallback.args)
|
||||
unread_cb_msgs[cb_id] = nil -- INFO: does this shift the table and frick everything up?
|
||||
end
|
||||
|
||||
-- if tb.RequestResponse then
|
||||
-- local req_id = tb.RequestResponse.request_id
|
||||
-- Requests[req_id] = tb.RequestResponse.response
|
||||
-- end
|
||||
local inc_msg = ReadMsg()
|
||||
|
||||
assert(inc_msg.CallCallback) -- INFO: is this gucci or no
|
||||
|
||||
if inc_msg.CallCallback and inc_msg.CallCallback.callback_id then
|
||||
if inc_msg.CallCallback.args then -- TODO: can just inline
|
||||
CallbackTable[inc_msg.CallCallback.callback_id](inc_msg.CallCallback.args)
|
||||
else
|
||||
CallbackTable[inc_msg.CallCallback.callback_id](nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
206
api/lua/tag.lua
206
api/lua/tag.lua
|
@ -4,7 +4,8 @@
|
|||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
local tag = {}
|
||||
---@class TagGlobal
|
||||
local tag_global = {}
|
||||
|
||||
---@alias Layout
|
||||
---| "MasterStack" # One master window on the left with all other windows stacked to the right.
|
||||
|
@ -17,7 +18,7 @@ local tag = {}
|
|||
|
||||
---@class Tag
|
||||
---@field private _id integer The internal id of this tag.
|
||||
local tg = {}
|
||||
local tag = {}
|
||||
|
||||
---@param tag_id integer
|
||||
---@return Tag
|
||||
|
@ -25,67 +26,66 @@ local function new_tag(tag_id)
|
|||
---@type Tag
|
||||
local t = { _id = tag_id }
|
||||
-- Copy functions over
|
||||
for k, v in pairs(tg) do
|
||||
for k, v in pairs(tag) do
|
||||
t[k] = v
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
---Switch to this tag.
|
||||
function tg:switch_to()
|
||||
tag.switch_to(self)
|
||||
end
|
||||
|
||||
---Toggle this tag.
|
||||
function tg:toggle()
|
||||
tag.toggle(self)
|
||||
end
|
||||
|
||||
---Get this tag's internal id.
|
||||
---@return integer
|
||||
function tg:id()
|
||||
function tag:id()
|
||||
return self._id
|
||||
end
|
||||
|
||||
---Get this tag's active status.
|
||||
---@return boolean|nil active `true` if the tag is active, `false` if not, and `nil` if the tag doesn't exist.
|
||||
function tg:active()
|
||||
SendRequest({
|
||||
function tag:active()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetTagProps = {
|
||||
tag_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local active = response.RequestResponse.response.TagProps.active
|
||||
return active
|
||||
end
|
||||
|
||||
---Get this tag's name.
|
||||
---@return string|nil name The name of this tag, or nil if it doesn't exist.
|
||||
function tg:name()
|
||||
SendRequest({
|
||||
function tag:name()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetTagProps = {
|
||||
tag_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local name = response.RequestResponse.response.TagProps.name
|
||||
return name
|
||||
end
|
||||
|
||||
---Get this tag's output.
|
||||
---@return Output|nil output The output this tag is on, or nil if the tag doesn't exist.
|
||||
function tg:output()
|
||||
function tag:output()
|
||||
return require("output").get_for_tag(self)
|
||||
end
|
||||
|
||||
---Switch to this tag.
|
||||
function tag:switch_to()
|
||||
tag_global.switch_to(self)
|
||||
end
|
||||
|
||||
---Toggle this tag.
|
||||
function tag:toggle()
|
||||
tag_global.toggle(self)
|
||||
end
|
||||
|
||||
---Set this tag's layout.
|
||||
---@param layout Layout
|
||||
function tg:set_layout(layout)
|
||||
tag.set_layout(self, layout)
|
||||
function tag:set_layout(layout)
|
||||
local name = self:name()
|
||||
if name ~= nil then
|
||||
tag_global.set_layout(name, layout)
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------
|
||||
|
@ -110,7 +110,7 @@ end
|
|||
---@param output Output The output you want these tags to be added to.
|
||||
---@param ... string The names of the new tags you want to add.
|
||||
---@overload fun(output: Output, tag_names: string[])
|
||||
function tag.add(output, ...)
|
||||
function tag_global.add(output, ...)
|
||||
local varargs = { ... }
|
||||
if type(varargs[1]) == "string" then
|
||||
local tag_names = varargs
|
||||
|
@ -145,17 +145,43 @@ end
|
|||
---tag.toggle("2", op)
|
||||
----- will cause windows on both tags 1 and 2 to be displayed at the same time.
|
||||
---```
|
||||
---@param t Tag
|
||||
function tag.toggle(t)
|
||||
SendMsg({
|
||||
ToggleTag = {
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
---@param name string The name of the tag.
|
||||
---@param output Output? The output.
|
||||
---@overload fun(t: Tag)
|
||||
function tag_global.toggle(name, output)
|
||||
if type(name) == "table" then
|
||||
SendMsg({
|
||||
ToggleTag = {
|
||||
tag_id = name--[[@as Tag]]:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local output = output or require("output").get_focused()
|
||||
|
||||
if output == nil then
|
||||
return
|
||||
end
|
||||
|
||||
print("before tag_global.get_by_name")
|
||||
local tags = tag_global.get_by_name(name)
|
||||
print("after tag_global.get_by_name")
|
||||
for _, t in pairs(tags) do
|
||||
if t:output() and t:output():name() == output:name() then
|
||||
SendMsg({
|
||||
ToggleTag = {
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Switch to a tag on the specified output, deactivating any other active tags on it.
|
||||
---If `output` is not specified, this uses the currently focused output instead.
|
||||
---Alternatively, provide a tag object instead of a name and output.
|
||||
---
|
||||
---This is used to replicate what a traditional workspace is on some other Wayland compositors.
|
||||
---
|
||||
|
@ -164,25 +190,73 @@ end
|
|||
---```lua
|
||||
---tag.switch_to("3") -- Switches to and displays *only* windows on tag 3
|
||||
---```
|
||||
---@param t Tag
|
||||
function tag.switch_to(t)
|
||||
SendMsg({
|
||||
SwitchToTag = {
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
---@param name string The name of the tag.
|
||||
---@param output Output? The output.
|
||||
---@overload fun(t: Tag)
|
||||
function tag_global.switch_to(name, output)
|
||||
if type(name) == "table" then
|
||||
SendMsg({
|
||||
SwitchToTag = {
|
||||
tag_id = name--[[@as Tag]]:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local output = output or require("output").get_focused()
|
||||
|
||||
if output == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = tag_global.get_by_name(name)
|
||||
for _, t in pairs(tags) do
|
||||
if t:output() and t:output():name() == output:name() then
|
||||
SendMsg({
|
||||
SwitchToTag = {
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Set a layout for the specified tag.
|
||||
---@param t Tag
|
||||
---@param layout Layout
|
||||
function tag.set_layout(t, layout)
|
||||
SendMsg({
|
||||
SetLayout = {
|
||||
tag_id = t:id(),
|
||||
layout = layout,
|
||||
},
|
||||
})
|
||||
---Set a layout for the tag on the specified output. If no output is provided, set it for the tag on the currently focused one.
|
||||
---Alternatively, provide a tag object instead of a name and output.
|
||||
---@param name string The name of the tag.
|
||||
---@param layout Layout The layout.
|
||||
---@param output Output? The output.
|
||||
---@overload fun(t: Tag, layout: Layout)
|
||||
function tag_global.set_layout(name, layout, output)
|
||||
if type(name) == "table" then
|
||||
SendMsg({
|
||||
SetLayout = {
|
||||
tag_id = name--[[@as Tag]]:id(),
|
||||
layout = layout,
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local output = output or require("output").get_focused()
|
||||
|
||||
if output == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = tag_global.get_by_name(name)
|
||||
for _, t in pairs(tags) do
|
||||
if t:output() and t:output():name() == output:name() then
|
||||
SendMsg({
|
||||
SetLayout = {
|
||||
tag_id = t:id(),
|
||||
layout = layout,
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Get all tags on the specified output.
|
||||
|
@ -195,14 +269,12 @@ end
|
|||
---```
|
||||
---@param output Output
|
||||
---@return Tag[]
|
||||
function tag.get_on_output(output)
|
||||
SendRequest({
|
||||
function tag_global.get_on_output(output)
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetOutputProps = {
|
||||
output_name = output:name(),
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
|
||||
local tag_ids = response.RequestResponse.response.OutputProps.tag_ids
|
||||
|
||||
|
@ -223,18 +295,13 @@ end
|
|||
---Get all tags with this name across all outputs.
|
||||
---@param name string The name of the tags you want.
|
||||
---@return Tag[]
|
||||
function tag.get_by_name(name)
|
||||
SendRequest("GetTags")
|
||||
|
||||
local response = ReadMsg()
|
||||
|
||||
local tag_ids = response.RequestResponse.response.Tags.tag_ids
|
||||
function tag_global.get_by_name(name)
|
||||
local t_s = tag_global.get_all()
|
||||
|
||||
---@type Tag[]
|
||||
local tags = {}
|
||||
|
||||
for _, tag_id in pairs(tag_ids) do
|
||||
local t = new_tag(tag_id)
|
||||
for _, t in pairs(t_s) do
|
||||
if t:name() == name then
|
||||
table.insert(tags, t)
|
||||
end
|
||||
|
@ -245,10 +312,9 @@ end
|
|||
|
||||
---Get all tags across all ouptuts.
|
||||
---@return Tag[]
|
||||
function tag.get_all()
|
||||
SendRequest("GetTags")
|
||||
|
||||
local response = ReadMsg()
|
||||
function tag_global.get_all()
|
||||
local response = ReadMsg(SendRequest("GetTags"))
|
||||
RPrint(response)
|
||||
|
||||
local tag_ids = response.RequestResponse.response.Tags.tag_ids
|
||||
|
||||
|
@ -262,4 +328,4 @@ function tag.get_all()
|
|||
return tags
|
||||
end
|
||||
|
||||
return tag
|
||||
return tag_global
|
||||
|
|
|
@ -4,19 +4,24 @@
|
|||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
---@class Window
|
||||
---@field private id integer The internal id of this window
|
||||
local win = {}
|
||||
---@class WindowGlobal
|
||||
local window_global = {}
|
||||
|
||||
---@param props Window
|
||||
---@class Window
|
||||
---@field private _id integer The internal id of this window
|
||||
local window = {}
|
||||
|
||||
---@param window_id WindowId
|
||||
---@return Window
|
||||
local function new_window(props)
|
||||
local function new_window(window_id)
|
||||
---@type Window
|
||||
local w = { _id = window_id }
|
||||
-- Copy functions over
|
||||
for k, v in pairs(win) do
|
||||
props[k] = v
|
||||
for k, v in pairs(window) do
|
||||
w[k] = v
|
||||
end
|
||||
|
||||
return props
|
||||
return w
|
||||
end
|
||||
|
||||
---Set a window's size.
|
||||
|
@ -28,10 +33,10 @@ end
|
|||
---window.get_focused():set_size({}) -- do absolutely nothing useful
|
||||
---```
|
||||
---@param size { w: integer?, h: integer? }
|
||||
function win:set_size(size)
|
||||
function window:set_size(size)
|
||||
SendMsg({
|
||||
SetWindowSize = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
width = size.w,
|
||||
height = size.h,
|
||||
},
|
||||
|
@ -46,14 +51,11 @@ end
|
|||
---window.get_focused():move_to_tag("5")
|
||||
----- ...will make the window only appear on tag 5.
|
||||
---```
|
||||
---@param t Tag
|
||||
function win:move_to_tag(t)
|
||||
SendMsg({
|
||||
MoveWindowToTag = {
|
||||
window_id = self.id,
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
---@param name string
|
||||
---@param output Output?
|
||||
---@overload fun(self: self, t: Tag)
|
||||
function window:move_to_tag(name, output)
|
||||
window_global.move_to_tag(self, name, output)
|
||||
end
|
||||
|
||||
---Toggle the specified tag for this window.
|
||||
|
@ -66,14 +68,11 @@ end
|
|||
---window.get_focused():toggle_tag("2")
|
||||
----- ...will also make the window appear on tag 2.
|
||||
---```
|
||||
---@param t Tag
|
||||
function win:toggle_tag(t)
|
||||
SendMsg({
|
||||
ToggleTagOnWindow = {
|
||||
window_id = self.id,
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
---@param name string
|
||||
---@param output Output?
|
||||
---@overload fun(self: self, t: Tag)
|
||||
function window:toggle_tag(name, output)
|
||||
window_global.toggle_tag(self, name, output)
|
||||
end
|
||||
|
||||
---Close this window.
|
||||
|
@ -85,10 +84,10 @@ end
|
|||
---```lua
|
||||
---window.get_focused():close() -- close the currently focused window
|
||||
---```
|
||||
function win:close()
|
||||
function window:close()
|
||||
SendMsg({
|
||||
CloseWindow = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
@ -99,10 +98,10 @@ end
|
|||
---```lua
|
||||
---window.get_focused():toggle_floating() -- toggles the focused window between tiled and floating
|
||||
---```
|
||||
function win:toggle_floating()
|
||||
function window:toggle_floating()
|
||||
SendMsg({
|
||||
ToggleFloating = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
@ -116,14 +115,12 @@ end
|
|||
----- ...should have size equal to `{ w = 3840, h = 2160 }`.
|
||||
---```
|
||||
---@return { w: integer, h: integer }|nil size The size of the window, or nil if it doesn't exist.
|
||||
function win:size()
|
||||
SendRequest({
|
||||
function window:size()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local size = response.RequestResponse.response.WindowProps.size
|
||||
if size == nil then
|
||||
return nil
|
||||
|
@ -149,14 +146,12 @@ end
|
|||
----- ...should have loc equal to `{ x = 1920, y = 0 }`.
|
||||
---```
|
||||
---@return { x: integer, y: integer }|nil loc The location of the window, or nil if it's not on-screen or alive.
|
||||
function win:loc()
|
||||
SendRequest({
|
||||
function window:loc()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local loc = response.RequestResponse.response.WindowProps.loc
|
||||
if loc == nil then
|
||||
return nil
|
||||
|
@ -177,14 +172,12 @@ end
|
|||
----- ...should print "Alacritty".
|
||||
---```
|
||||
---@return string|nil class This window's class, or nil if it doesn't exist.
|
||||
function win:class()
|
||||
SendRequest({
|
||||
function window:class()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local class = response.RequestResponse.response.WindowProps.class
|
||||
return class
|
||||
end
|
||||
|
@ -198,14 +191,12 @@ end
|
|||
----- ...should print the directory Alacritty is in or what it's running (what's in its title bar).
|
||||
---```
|
||||
---@return string|nil title This window's title, or nil if it doesn't exist.
|
||||
function win:title()
|
||||
SendRequest({
|
||||
function window:title()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local title = response.RequestResponse.response.WindowProps.title
|
||||
return title
|
||||
end
|
||||
|
@ -219,14 +210,12 @@ end
|
|||
----- ...should print `true`.
|
||||
---```
|
||||
---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist.
|
||||
function win:floating()
|
||||
SendRequest({
|
||||
function window:floating()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local floating = response.RequestResponse.response.WindowProps.floating
|
||||
return floating
|
||||
end
|
||||
|
@ -238,28 +227,28 @@ end
|
|||
---print(window.get_focused():focused()) -- should print `true`.
|
||||
---```
|
||||
---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist.
|
||||
function win:focused()
|
||||
SendRequest({
|
||||
function window:focused()
|
||||
local response = ReadMsg(SendRequest({
|
||||
GetWindowProps = {
|
||||
window_id = self.id,
|
||||
window_id = self._id,
|
||||
},
|
||||
})
|
||||
|
||||
local response = ReadMsg()
|
||||
}))
|
||||
local focused = response.RequestResponse.response.WindowProps.focused
|
||||
return focused
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
---@return WindowId
|
||||
function window:id()
|
||||
return self._id
|
||||
end
|
||||
|
||||
---@class WindowGlobal
|
||||
local window = {}
|
||||
-------------------------------------------------------------------
|
||||
|
||||
---Get all windows with the specified class (usually the name of the application).
|
||||
---@param class string The class. For example, Alacritty's class is "Alacritty".
|
||||
---@return Window[]
|
||||
function window.get_by_class(class)
|
||||
local windows = window.get_all()
|
||||
function window_global.get_by_class(class)
|
||||
local windows = window_global.get_all()
|
||||
|
||||
---@type Window[]
|
||||
local windows_ret = {}
|
||||
|
@ -275,8 +264,8 @@ end
|
|||
---Get all windows with the specified title.
|
||||
---@param title string The title.
|
||||
---@return Window[]
|
||||
function window.get_by_title(title)
|
||||
local windows = window.get_all()
|
||||
function window_global.get_by_title(title)
|
||||
local windows = window_global.get_all()
|
||||
|
||||
---@type Window[]
|
||||
local windows_ret = {}
|
||||
|
@ -291,8 +280,8 @@ end
|
|||
|
||||
---Get the currently focused window.
|
||||
---@return Window|nil
|
||||
function window.get_focused()
|
||||
local windows = window.get_all()
|
||||
function window_global.get_focused()
|
||||
local windows = window_global.get_all()
|
||||
|
||||
for _, w in pairs(windows) do
|
||||
if w:focused() then
|
||||
|
@ -305,16 +294,85 @@ end
|
|||
|
||||
---Get all windows.
|
||||
---@return Window[]
|
||||
function window.get_all()
|
||||
SendRequest("GetWindows")
|
||||
|
||||
local window_ids = ReadMsg().RequestResponse.response.Windows.window_ids
|
||||
function window_global.get_all()
|
||||
local window_ids = ReadMsg(SendRequest("GetWindows")).RequestResponse.response.Windows.window_ids
|
||||
---@type Window[]
|
||||
local windows = {}
|
||||
for _, window_id in pairs(window_ids) do
|
||||
table.insert(windows, new_window({ id = window_id }))
|
||||
table.insert(windows, new_window(window_id))
|
||||
end
|
||||
return windows
|
||||
end
|
||||
|
||||
return window
|
||||
---comment
|
||||
---@param w Window
|
||||
---@param name string
|
||||
---@param output Output?
|
||||
function window_global.toggle_tag(w, name, output)
|
||||
if type(name) == "table" then
|
||||
SendMsg({
|
||||
ToggleTagOnWindow = {
|
||||
window_id = w:id(),
|
||||
tag_id = name--[[@as Tag]]:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local output = output or require("output").get_focused()
|
||||
|
||||
if output == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = require("tag").get_by_name(name)
|
||||
for _, t in pairs(tags) do
|
||||
if t:output() and t:output():name() == output:name() then
|
||||
SendMsg({
|
||||
ToggleTagOnWindow = {
|
||||
window_id = w:id(),
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---comment
|
||||
---@param w Window
|
||||
---@param name string
|
||||
---@param output Output?
|
||||
---@overload fun(w: Window, t: Tag)
|
||||
function window_global.move_to_tag(w, name, output)
|
||||
if type(name) == "table" then
|
||||
SendMsg({
|
||||
MoveWindowToTag = {
|
||||
window_id = w:id(),
|
||||
tag_id = name--[[@as Tag]]:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
local output = output or require("output").get_focused()
|
||||
|
||||
if output == nil then
|
||||
return
|
||||
end
|
||||
|
||||
local tags = require("tag").get_by_name(name)
|
||||
for _, t in pairs(tags) do
|
||||
if t:output() and t:output():name() == output:name() then
|
||||
SendMsg({
|
||||
MoveWindowToTag = {
|
||||
window_id = w:id(),
|
||||
tag_id = t:id(),
|
||||
},
|
||||
})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return window_global
|
||||
|
|
|
@ -112,7 +112,7 @@ pub fn send_to_client(
|
|||
stream: &mut UnixStream,
|
||||
msg: &OutgoingMsg,
|
||||
) -> Result<(), rmp_serde::encode::Error> {
|
||||
// tracing::debug!("Sending {msg:?}");
|
||||
tracing::debug!("Sending {msg:?}");
|
||||
let msg = rmp_serde::to_vec_named(msg)?;
|
||||
let msg_len = msg.len() as u32;
|
||||
let bytes = msg_len.to_ne_bytes();
|
||||
|
|
|
@ -85,11 +85,14 @@ pub enum Msg {
|
|||
/// Quit the compositor.
|
||||
Quit,
|
||||
|
||||
Request(Request),
|
||||
Request {
|
||||
request_id: RequestId,
|
||||
request: Request,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RequestId(pub u32);
|
||||
pub struct RequestId(u32);
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
@ -158,6 +161,7 @@ pub enum OutgoingMsg {
|
|||
args: Option<Args>,
|
||||
},
|
||||
RequestResponse {
|
||||
request_id: RequestId,
|
||||
response: RequestResponse,
|
||||
},
|
||||
}
|
||||
|
|
18
src/state.rs
18
src/state.rs
|
@ -16,7 +16,7 @@ use std::{
|
|||
|
||||
use crate::{
|
||||
api::{
|
||||
msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestResponse},
|
||||
msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestId, RequestResponse},
|
||||
PinnacleSocketSource,
|
||||
},
|
||||
focus::FocusState,
|
||||
|
@ -259,13 +259,16 @@ impl<B: Backend> State<B> {
|
|||
self.loop_signal.stop();
|
||||
}
|
||||
|
||||
Msg::Request(request) => {
|
||||
self.handle_request(request);
|
||||
Msg::Request {
|
||||
request_id,
|
||||
request,
|
||||
} => {
|
||||
self.handle_request(request_id, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_request(&mut self, request: Request) {
|
||||
fn handle_request(&mut self, request_id: RequestId, request: Request) {
|
||||
let stream = self
|
||||
.api_state
|
||||
.stream
|
||||
|
@ -284,6 +287,7 @@ impl<B: Backend> State<B> {
|
|||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::Windows { window_ids },
|
||||
},
|
||||
)
|
||||
|
@ -320,6 +324,7 @@ impl<B: Backend> State<B> {
|
|||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::WindowProps {
|
||||
size,
|
||||
loc,
|
||||
|
@ -341,6 +346,7 @@ impl<B: Backend> State<B> {
|
|||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::Outputs { output_names },
|
||||
},
|
||||
)
|
||||
|
@ -385,6 +391,7 @@ impl<B: Backend> State<B> {
|
|||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::OutputProps {
|
||||
make,
|
||||
model,
|
||||
|
@ -406,9 +413,11 @@ impl<B: Backend> State<B> {
|
|||
.flat_map(|op| op.with_state(|state| state.tags.clone()))
|
||||
.map(|tag| tag.id())
|
||||
.collect::<Vec<_>>();
|
||||
tracing::debug!("GetTags: {:?}", tag_ids);
|
||||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::Tags { tag_ids },
|
||||
},
|
||||
)
|
||||
|
@ -425,6 +434,7 @@ impl<B: Backend> State<B> {
|
|||
crate::api::send_to_client(
|
||||
&mut stream,
|
||||
&OutgoingMsg::RequestResponse {
|
||||
request_id,
|
||||
response: RequestResponse::TagProps {
|
||||
active,
|
||||
name,
|
||||
|
|
Loading…
Reference in a new issue