Re-add lost API functionality, fix msg ordering

This commit is contained in:
Ottatop 2023-07-21 14:36:32 -05:00
parent 3ddd57c63f
commit fb63e7ada3
8 changed files with 444 additions and 253 deletions

View file

@ -31,7 +31,7 @@
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
---@class _Request ---@class __Request
--Windows --Windows
---@field GetWindowProps { window_id: WindowId } ---@field GetWindowProps { window_id: WindowId }
--Outputs --Outputs
@ -39,11 +39,12 @@
--Tags --Tags
---@field GetTagProps { tag_id: TagId } ---@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 ---@class IncomingMsg
---@field CallCallback { callback_id: integer, args: Args } ---@field CallCallback { callback_id: integer, args: Args? }
---@field RequestResponse { response: RequestResponse } ---@field RequestResponse { request_id: integer, response: RequestResponse }
---@class Args ---@class Args
---@field Spawn { stdout: string?, stderr: string?, exit_code: integer?, exit_msg: string? } ---@field Spawn { stdout: string?, stderr: string?, exit_code: integer?, exit_msg: string? }
@ -51,6 +52,7 @@
---@alias WindowId integer ---@alias WindowId integer
---@alias TagId integer ---@alias TagId integer
---@alias RequestId integer
---@alias OutputName string ---@alias OutputName string
---@class RequestResponse ---@class RequestResponse

View file

@ -4,67 +4,64 @@
-- --
-- SPDX-License-Identifier: MPL-2.0 -- SPDX-License-Identifier: MPL-2.0
---@class OutputGlobal
local output_global = {}
---@class Output A display. ---@class Output A display.
---@field private _name string The name of this output (or rather, of its connector). ---@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". ---Get this output's name. This is something like "eDP-1" or "HDMI-A-0".
---@return string ---@return string
function op:name() function output:name()
return self._name return self._name
end end
---Get all tags on this output. See `tag.get_on_output`. ---Get all tags on this output. See `tag.get_on_output`.
---@return Tag[] ---@return Tag[]
function op:tags() function output:tags()
return require("tag").get_on_output(self) return require("tag").get_on_output(self)
end end
---Add tags to this output. See `tag.add`. ---Add tags to this output. See `tag.add`.
---@param ... string The names of the tags you want to add. ---@param ... string The names of the tags you want to add.
---@overload fun(self: self, tag_names: string[]) ---@overload fun(self: self, tag_names: string[])
function op:add_tags(...) function output:add_tags(...)
require("tag").add(self, ...) require("tag").add(self, ...)
end end
---Get this output's make. ---Get this output's make.
---@return string|nil ---@return string|nil
function op:make() function output:make()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
return props.make return props.make
end end
---Get this output's model. ---Get this output's model.
---@return string|nil ---@return string|nil
function op:model() function output:model()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
return props.model return props.model
end end
---Get this output's location in the global space. ---Get this output's location in the global space.
---@return { x: integer, y: integer }|nil ---@return { x: integer, y: integer }|nil
function op:loc() function output:loc()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
if props.loc == nil then if props.loc == nil then
return nil return nil
@ -75,14 +72,12 @@ end
---Get this output's resolution in pixels. ---Get this output's resolution in pixels.
---@return { w: integer, h: integer }|nil ---@return { w: integer, h: integer }|nil
function op:res() function output:res()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
if props.res == nil then if props.res == nil then
return nil return nil
@ -94,28 +89,24 @@ end
---Get this output's refresh rate in millihertz. ---Get this output's refresh rate in millihertz.
---For example, 60Hz will be returned as 60000. ---For example, 60Hz will be returned as 60000.
---@return integer|nil ---@return integer|nil
function op:refresh_rate() function output:refresh_rate()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
return props.refresh_rate return props.refresh_rate
end end
---Get this output's physical size in millimeters. ---Get this output's physical size in millimeters.
---@return { w: integer, h: integer }|nil ---@return { w: integer, h: integer }|nil
function op:physical_size() function output:physical_size()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
if props.physical_size == nil then if props.physical_size == nil then
return nil 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. ---Get whether or not this output is focused. This is currently defined as having the cursor on it.
---@return boolean|nil ---@return boolean|nil
function op:focused() function output:focused()
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = self._name, output_name = self._name,
}, },
}) }))
local response = ReadMsg()
local props = response.RequestResponse.response.OutputProps local props = response.RequestResponse.response.OutputProps
return props.focused return props.focused
end end
@ -145,7 +134,7 @@ local function new_output(output_name)
---@type Output ---@type Output
local o = { _name = output_name } local o = { _name = output_name }
-- Copy functions over -- Copy functions over
for k, v in pairs(op) do for k, v in pairs(output) do
o[k] = v o[k] = v
end end
@ -154,9 +143,6 @@ end
------------------------------------------------------ ------------------------------------------------------
---@class OutputGlobal
local output = {}
---Get an output by its name. ---Get an output by its name.
--- ---
---"Name" in this sense does not mean its model or manufacturer; ---"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. ---@param name string The name of the output.
---@return Output|nil output The output, or nil if none have the provided name. ---@return Output|nil output The output, or nil if none have the provided name.
function output.get_by_name(name) function output_global.get_by_name(name)
SendRequest("GetOutputs") local response = ReadMsg(SendRequest("GetOutputs"))
local response = ReadMsg()
local output_names = response.RequestResponse.response.Outputs.output_names local output_names = response.RequestResponse.response.Outputs.output_names
for _, output_name in pairs(output_names) do 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. ---This is something like "DELL E2416H" or whatever gibberish monitor manufacturers call their displays.
---@param model string The model of the output(s). ---@param model string The model of the output(s).
---@return Output[] outputs All outputs with this model. ---@return Output[] outputs All outputs with this model.
function output.get_by_model(model) function output_global.get_by_model(model)
SendRequest("GetOutputs") local response = ReadMsg(SendRequest("GetOutputs"))
local response = ReadMsg()
local output_names = response.RequestResponse.response.Outputs.output_names local output_names = response.RequestResponse.response.Outputs.output_names
---@type Output[] ---@type Output[]
@ -212,10 +196,8 @@ end
---@param width integer The width of the outputs, in pixels. ---@param width integer The width of the outputs, in pixels.
---@param height integer The height of the outputs, in pixels. ---@param height integer The height of the outputs, in pixels.
---@return Output[] outputs All outputs with this resolution. ---@return Output[] outputs All outputs with this resolution.
function output.get_by_res(width, height) function output_global.get_by_res(width, height)
SendRequest("GetOutputs") local response = ReadMsg(SendRequest("GetOutputs"))
local response = ReadMsg()
local output_names = response.RequestResponse.response.Outputs.output_names 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 ---local tags = output.get_focused():tags() -- will NOT warn for nil
---``` ---```
---@return Output|nil output The output, or nil if none are focused. ---@return Output|nil output The output, or nil if none are focused.
function output.get_focused() function output_global.get_focused()
SendRequest("GetOutputs") local response = ReadMsg(SendRequest("GetOutputs"))
local response = ReadMsg()
local output_names = response.RequestResponse.response.Outputs.output_names local output_names = response.RequestResponse.response.Outputs.output_names
for _, output_name in pairs(output_names) do 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. ---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. ---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. ---@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 ---@param args Args
table.insert(CallbackTable, function(args) table.insert(CallbackTable, function(args)
local args = args.ConnectForAllOutputs local args = args.ConnectForAllOutputs
@ -290,14 +271,12 @@ end
---Get the output the specified tag is on. ---Get the output the specified tag is on.
---@param tag Tag ---@param tag Tag
---@return Output|nil ---@return Output|nil
function output.get_for_tag(tag) function output_global.get_for_tag(tag)
SendRequest({ local response = ReadMsg(SendRequest({
GetTagProps = { GetTagProps = {
tag_id = tag:id(), tag_id = tag:id(),
}, },
}) }))
local response = ReadMsg()
local output_name = response.RequestResponse.response.TagProps.output_name local output_name = response.RequestResponse.response.TagProps.output_name
if output_name == nil then if output_name == nil then
@ -307,4 +286,4 @@ function output.get_for_tag(tag)
end end
end end
return output return output_global

View file

@ -9,6 +9,33 @@ local msgpack = require("msgpack")
local SOCKET_PATH = "/tmp/pinnacle_socket" 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. ---Read the specified number of bytes.
---@param socket_fd integer The socket file descriptor ---@param socket_fd integer The socket file descriptor
---@param count integer The amount of bytes to read ---@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. ---This is an internal global function used to send serialized messages to the Pinnacle server.
---@param data Msg ---@param data Msg
function SendMsg(data) function SendMsg(data)
-- RPrint(data)
local encoded = msgpack.encode(data) local encoded = msgpack.encode(data)
assert(encoded) assert(encoded)
-- print(encoded) -- print(encoded)
@ -93,54 +121,98 @@ function pinnacle.setup(config_func)
socket.send(socket_fd, encoded) socket.send(socket_fd, encoded)
end 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. ---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) function SendRequest(data)
local req_id = next_request_id()
SendMsg({ SendMsg({
Request = data, Request = {
request_id = req_id,
request = data,
},
}) })
return req_id
end end
---This is an internal global function used to read messages sent from the server. ---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. ---These are used to call user-defined functions and provide requested information.
function ReadMsg() ---@return IncomingMsg
local msg_len_bytes, err_msg, err_num = read_exact(socket_fd, 4) ---@param req_id integer? A request id if you're looking for that specific message.
assert(msg_len_bytes) 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 -- TODO: break here if error in read_exact
local msg_len = string.unpack("=I4", msg_len_bytes)
-- print(msg_len)
local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len) ---@type integer
assert(msg_bytes) local msg_len = string.unpack("=I4", msg_len_bytes)
-- print(msg_bytes) -- print(msg_len)
---@type IncomingMsg local msg_bytes, err_msg2, err_num2 = read_exact(socket_fd, msg_len)
local tb = msgpack.decode(msg_bytes) assert(msg_bytes)
-- print(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 end
config_func(pinnacle) config_func(pinnacle)
while true do while true do
local tb = ReadMsg() for cb_id, inc_msg in pairs(unread_cb_msgs) do
CallbackTable[inc_msg.CallCallback.callback_id](inc_msg.CallCallback.args)
if tb.CallCallback and tb.CallCallback.callback_id then unread_cb_msgs[cb_id] = nil -- INFO: does this shift the table and frick everything up?
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
end end
-- if tb.RequestResponse then local inc_msg = ReadMsg()
-- local req_id = tb.RequestResponse.request_id
-- Requests[req_id] = tb.RequestResponse.response assert(inc_msg.CallCallback) -- INFO: is this gucci or no
-- end
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
end end

View file

@ -4,7 +4,8 @@
-- --
-- SPDX-License-Identifier: MPL-2.0 -- SPDX-License-Identifier: MPL-2.0
local tag = {} ---@class TagGlobal
local tag_global = {}
---@alias Layout ---@alias Layout
---| "MasterStack" # One master window on the left with all other windows stacked to the right. ---| "MasterStack" # One master window on the left with all other windows stacked to the right.
@ -17,7 +18,7 @@ local tag = {}
---@class Tag ---@class Tag
---@field private _id integer The internal id of this tag. ---@field private _id integer The internal id of this tag.
local tg = {} local tag = {}
---@param tag_id integer ---@param tag_id integer
---@return Tag ---@return Tag
@ -25,67 +26,66 @@ local function new_tag(tag_id)
---@type Tag ---@type Tag
local t = { _id = tag_id } local t = { _id = tag_id }
-- Copy functions over -- Copy functions over
for k, v in pairs(tg) do for k, v in pairs(tag) do
t[k] = v t[k] = v
end end
return t return t
end 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. ---Get this tag's internal id.
---@return integer ---@return integer
function tg:id() function tag:id()
return self._id return self._id
end end
---Get this tag's active status. ---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. ---@return boolean|nil active `true` if the tag is active, `false` if not, and `nil` if the tag doesn't exist.
function tg:active() function tag:active()
SendRequest({ local response = ReadMsg(SendRequest({
GetTagProps = { GetTagProps = {
tag_id = self._id, tag_id = self._id,
}, },
}) }))
local response = ReadMsg()
local active = response.RequestResponse.response.TagProps.active local active = response.RequestResponse.response.TagProps.active
return active return active
end end
---Get this tag's name. ---Get this tag's name.
---@return string|nil name The name of this tag, or nil if it doesn't exist. ---@return string|nil name The name of this tag, or nil if it doesn't exist.
function tg:name() function tag:name()
SendRequest({ local response = ReadMsg(SendRequest({
GetTagProps = { GetTagProps = {
tag_id = self._id, tag_id = self._id,
}, },
}) }))
local response = ReadMsg()
local name = response.RequestResponse.response.TagProps.name local name = response.RequestResponse.response.TagProps.name
return name return name
end end
---Get this tag's output. ---Get this tag's output.
---@return Output|nil output The output this tag is on, or nil if the tag doesn't exist. ---@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) return require("output").get_for_tag(self)
end 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. ---Set this tag's layout.
---@param layout Layout ---@param layout Layout
function tg:set_layout(layout) function tag:set_layout(layout)
tag.set_layout(self, layout) local name = self:name()
if name ~= nil then
tag_global.set_layout(name, layout)
end
end end
----------------------------------------------------------- -----------------------------------------------------------
@ -110,7 +110,7 @@ end
---@param output Output The output you want these tags to be added to. ---@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. ---@param ... string The names of the new tags you want to add.
---@overload fun(output: Output, tag_names: string[]) ---@overload fun(output: Output, tag_names: string[])
function tag.add(output, ...) function tag_global.add(output, ...)
local varargs = { ... } local varargs = { ... }
if type(varargs[1]) == "string" then if type(varargs[1]) == "string" then
local tag_names = varargs local tag_names = varargs
@ -145,17 +145,43 @@ end
---tag.toggle("2", op) ---tag.toggle("2", op)
----- will cause windows on both tags 1 and 2 to be displayed at the same time. ----- will cause windows on both tags 1 and 2 to be displayed at the same time.
---``` ---```
---@param t Tag ---@param name string The name of the tag.
function tag.toggle(t) ---@param output Output? The output.
SendMsg({ ---@overload fun(t: Tag)
ToggleTag = { function tag_global.toggle(name, output)
tag_id = t:id(), 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 end
---Switch to a tag on the specified output, deactivating any other active tags on it. ---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. ---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. ---This is used to replicate what a traditional workspace is on some other Wayland compositors.
--- ---
@ -164,25 +190,73 @@ end
---```lua ---```lua
---tag.switch_to("3") -- Switches to and displays *only* windows on tag 3 ---tag.switch_to("3") -- Switches to and displays *only* windows on tag 3
---``` ---```
---@param t Tag ---@param name string The name of the tag.
function tag.switch_to(t) ---@param output Output? The output.
SendMsg({ ---@overload fun(t: Tag)
SwitchToTag = { function tag_global.switch_to(name, output)
tag_id = t:id(), 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 end
---Set a layout for the specified tag. ---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.
---@param t Tag ---Alternatively, provide a tag object instead of a name and output.
---@param layout Layout ---@param name string The name of the tag.
function tag.set_layout(t, layout) ---@param layout Layout The layout.
SendMsg({ ---@param output Output? The output.
SetLayout = { ---@overload fun(t: Tag, layout: Layout)
tag_id = t:id(), function tag_global.set_layout(name, layout, output)
layout = layout, 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 end
---Get all tags on the specified output. ---Get all tags on the specified output.
@ -195,14 +269,12 @@ end
---``` ---```
---@param output Output ---@param output Output
---@return Tag[] ---@return Tag[]
function tag.get_on_output(output) function tag_global.get_on_output(output)
SendRequest({ local response = ReadMsg(SendRequest({
GetOutputProps = { GetOutputProps = {
output_name = output:name(), output_name = output:name(),
}, },
}) }))
local response = ReadMsg()
local tag_ids = response.RequestResponse.response.OutputProps.tag_ids local tag_ids = response.RequestResponse.response.OutputProps.tag_ids
@ -223,18 +295,13 @@ end
---Get all tags with this name across all outputs. ---Get all tags with this name across all outputs.
---@param name string The name of the tags you want. ---@param name string The name of the tags you want.
---@return Tag[] ---@return Tag[]
function tag.get_by_name(name) function tag_global.get_by_name(name)
SendRequest("GetTags") local t_s = tag_global.get_all()
local response = ReadMsg()
local tag_ids = response.RequestResponse.response.Tags.tag_ids
---@type Tag[] ---@type Tag[]
local tags = {} local tags = {}
for _, tag_id in pairs(tag_ids) do for _, t in pairs(t_s) do
local t = new_tag(tag_id)
if t:name() == name then if t:name() == name then
table.insert(tags, t) table.insert(tags, t)
end end
@ -245,10 +312,9 @@ end
---Get all tags across all ouptuts. ---Get all tags across all ouptuts.
---@return Tag[] ---@return Tag[]
function tag.get_all() function tag_global.get_all()
SendRequest("GetTags") local response = ReadMsg(SendRequest("GetTags"))
RPrint(response)
local response = ReadMsg()
local tag_ids = response.RequestResponse.response.Tags.tag_ids local tag_ids = response.RequestResponse.response.Tags.tag_ids
@ -262,4 +328,4 @@ function tag.get_all()
return tags return tags
end end
return tag return tag_global

View file

@ -4,19 +4,24 @@
-- --
-- SPDX-License-Identifier: MPL-2.0 -- SPDX-License-Identifier: MPL-2.0
---@class Window ---@class WindowGlobal
---@field private id integer The internal id of this window local window_global = {}
local win = {}
---@param props Window ---@class Window
---@field private _id integer The internal id of this window
local window = {}
---@param window_id WindowId
---@return Window ---@return Window
local function new_window(props) local function new_window(window_id)
---@type Window
local w = { _id = window_id }
-- Copy functions over -- Copy functions over
for k, v in pairs(win) do for k, v in pairs(window) do
props[k] = v w[k] = v
end end
return props return w
end end
---Set a window's size. ---Set a window's size.
@ -28,10 +33,10 @@ end
---window.get_focused():set_size({}) -- do absolutely nothing useful ---window.get_focused():set_size({}) -- do absolutely nothing useful
---``` ---```
---@param size { w: integer?, h: integer? } ---@param size { w: integer?, h: integer? }
function win:set_size(size) function window:set_size(size)
SendMsg({ SendMsg({
SetWindowSize = { SetWindowSize = {
window_id = self.id, window_id = self._id,
width = size.w, width = size.w,
height = size.h, height = size.h,
}, },
@ -46,14 +51,11 @@ end
---window.get_focused():move_to_tag("5") ---window.get_focused():move_to_tag("5")
----- ...will make the window only appear on tag 5. ----- ...will make the window only appear on tag 5.
---``` ---```
---@param t Tag ---@param name string
function win:move_to_tag(t) ---@param output Output?
SendMsg({ ---@overload fun(self: self, t: Tag)
MoveWindowToTag = { function window:move_to_tag(name, output)
window_id = self.id, window_global.move_to_tag(self, name, output)
tag_id = t:id(),
},
})
end end
---Toggle the specified tag for this window. ---Toggle the specified tag for this window.
@ -66,14 +68,11 @@ end
---window.get_focused():toggle_tag("2") ---window.get_focused():toggle_tag("2")
----- ...will also make the window appear on tag 2. ----- ...will also make the window appear on tag 2.
---``` ---```
---@param t Tag ---@param name string
function win:toggle_tag(t) ---@param output Output?
SendMsg({ ---@overload fun(self: self, t: Tag)
ToggleTagOnWindow = { function window:toggle_tag(name, output)
window_id = self.id, window_global.toggle_tag(self, name, output)
tag_id = t:id(),
},
})
end end
---Close this window. ---Close this window.
@ -85,10 +84,10 @@ end
---```lua ---```lua
---window.get_focused():close() -- close the currently focused window ---window.get_focused():close() -- close the currently focused window
---``` ---```
function win:close() function window:close()
SendMsg({ SendMsg({
CloseWindow = { CloseWindow = {
window_id = self.id, window_id = self._id,
}, },
}) })
end end
@ -99,10 +98,10 @@ end
---```lua ---```lua
---window.get_focused():toggle_floating() -- toggles the focused window between tiled and floating ---window.get_focused():toggle_floating() -- toggles the focused window between tiled and floating
---``` ---```
function win:toggle_floating() function window:toggle_floating()
SendMsg({ SendMsg({
ToggleFloating = { ToggleFloating = {
window_id = self.id, window_id = self._id,
}, },
}) })
end end
@ -116,14 +115,12 @@ end
----- ...should have size equal to `{ w = 3840, h = 2160 }`. ----- ...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. ---@return { w: integer, h: integer }|nil size The size of the window, or nil if it doesn't exist.
function win:size() function window:size()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local size = response.RequestResponse.response.WindowProps.size local size = response.RequestResponse.response.WindowProps.size
if size == nil then if size == nil then
return nil return nil
@ -149,14 +146,12 @@ end
----- ...should have loc equal to `{ x = 1920, y = 0 }`. ----- ...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. ---@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() function window:loc()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local loc = response.RequestResponse.response.WindowProps.loc local loc = response.RequestResponse.response.WindowProps.loc
if loc == nil then if loc == nil then
return nil return nil
@ -177,14 +172,12 @@ end
----- ...should print "Alacritty". ----- ...should print "Alacritty".
---``` ---```
---@return string|nil class This window's class, or nil if it doesn't exist. ---@return string|nil class This window's class, or nil if it doesn't exist.
function win:class() function window:class()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local class = response.RequestResponse.response.WindowProps.class local class = response.RequestResponse.response.WindowProps.class
return class return class
end end
@ -198,14 +191,12 @@ end
----- ...should print the directory Alacritty is in or what it's running (what's in its title bar). ----- ...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. ---@return string|nil title This window's title, or nil if it doesn't exist.
function win:title() function window:title()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local title = response.RequestResponse.response.WindowProps.title local title = response.RequestResponse.response.WindowProps.title
return title return title
end end
@ -219,14 +210,12 @@ end
----- ...should print `true`. ----- ...should print `true`.
---``` ---```
---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist. ---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist.
function win:floating() function window:floating()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local floating = response.RequestResponse.response.WindowProps.floating local floating = response.RequestResponse.response.WindowProps.floating
return floating return floating
end end
@ -238,28 +227,28 @@ end
---print(window.get_focused():focused()) -- should print `true`. ---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. ---@return boolean|nil floating `true` if it's floating, `false` if it's tiled, or nil if it doesn't exist.
function win:focused() function window:focused()
SendRequest({ local response = ReadMsg(SendRequest({
GetWindowProps = { GetWindowProps = {
window_id = self.id, window_id = self._id,
}, },
}) }))
local response = ReadMsg()
local focused = response.RequestResponse.response.WindowProps.focused local focused = response.RequestResponse.response.WindowProps.focused
return focused return focused
end 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). ---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". ---@param class string The class. For example, Alacritty's class is "Alacritty".
---@return Window[] ---@return Window[]
function window.get_by_class(class) function window_global.get_by_class(class)
local windows = window.get_all() local windows = window_global.get_all()
---@type Window[] ---@type Window[]
local windows_ret = {} local windows_ret = {}
@ -275,8 +264,8 @@ end
---Get all windows with the specified title. ---Get all windows with the specified title.
---@param title string The title. ---@param title string The title.
---@return Window[] ---@return Window[]
function window.get_by_title(title) function window_global.get_by_title(title)
local windows = window.get_all() local windows = window_global.get_all()
---@type Window[] ---@type Window[]
local windows_ret = {} local windows_ret = {}
@ -291,8 +280,8 @@ end
---Get the currently focused window. ---Get the currently focused window.
---@return Window|nil ---@return Window|nil
function window.get_focused() function window_global.get_focused()
local windows = window.get_all() local windows = window_global.get_all()
for _, w in pairs(windows) do for _, w in pairs(windows) do
if w:focused() then if w:focused() then
@ -305,16 +294,85 @@ end
---Get all windows. ---Get all windows.
---@return Window[] ---@return Window[]
function window.get_all() function window_global.get_all()
SendRequest("GetWindows") local window_ids = ReadMsg(SendRequest("GetWindows")).RequestResponse.response.Windows.window_ids
local window_ids = ReadMsg().RequestResponse.response.Windows.window_ids
---@type Window[] ---@type Window[]
local windows = {} local windows = {}
for _, window_id in pairs(window_ids) do for _, window_id in pairs(window_ids) do
table.insert(windows, new_window({ id = window_id })) table.insert(windows, new_window(window_id))
end end
return windows return windows
end 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

View file

@ -112,7 +112,7 @@ pub fn send_to_client(
stream: &mut UnixStream, stream: &mut UnixStream,
msg: &OutgoingMsg, msg: &OutgoingMsg,
) -> Result<(), rmp_serde::encode::Error> { ) -> Result<(), rmp_serde::encode::Error> {
// tracing::debug!("Sending {msg:?}"); tracing::debug!("Sending {msg:?}");
let msg = rmp_serde::to_vec_named(msg)?; let msg = rmp_serde::to_vec_named(msg)?;
let msg_len = msg.len() as u32; let msg_len = msg.len() as u32;
let bytes = msg_len.to_ne_bytes(); let bytes = msg_len.to_ne_bytes();

View file

@ -85,11 +85,14 @@ pub enum Msg {
/// Quit the compositor. /// Quit the compositor.
Quit, Quit,
Request(Request), Request {
request_id: RequestId,
request: Request,
},
} }
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RequestId(pub u32); pub struct RequestId(u32);
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
@ -158,6 +161,7 @@ pub enum OutgoingMsg {
args: Option<Args>, args: Option<Args>,
}, },
RequestResponse { RequestResponse {
request_id: RequestId,
response: RequestResponse, response: RequestResponse,
}, },
} }

View file

@ -16,7 +16,7 @@ use std::{
use crate::{ use crate::{
api::{ api::{
msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestResponse}, msg::{Args, CallbackId, Msg, OutgoingMsg, Request, RequestId, RequestResponse},
PinnacleSocketSource, PinnacleSocketSource,
}, },
focus::FocusState, focus::FocusState,
@ -259,13 +259,16 @@ impl<B: Backend> State<B> {
self.loop_signal.stop(); self.loop_signal.stop();
} }
Msg::Request(request) => { Msg::Request {
self.handle_request(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 let stream = self
.api_state .api_state
.stream .stream
@ -284,6 +287,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Windows { window_ids }, response: RequestResponse::Windows { window_ids },
}, },
) )
@ -320,6 +324,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::WindowProps { response: RequestResponse::WindowProps {
size, size,
loc, loc,
@ -341,6 +346,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Outputs { output_names }, response: RequestResponse::Outputs { output_names },
}, },
) )
@ -385,6 +391,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::OutputProps { response: RequestResponse::OutputProps {
make, make,
model, model,
@ -406,9 +413,11 @@ impl<B: Backend> State<B> {
.flat_map(|op| op.with_state(|state| state.tags.clone())) .flat_map(|op| op.with_state(|state| state.tags.clone()))
.map(|tag| tag.id()) .map(|tag| tag.id())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
tracing::debug!("GetTags: {:?}", tag_ids);
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::Tags { tag_ids }, response: RequestResponse::Tags { tag_ids },
}, },
) )
@ -425,6 +434,7 @@ impl<B: Backend> State<B> {
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
request_id,
response: RequestResponse::TagProps { response: RequestResponse::TagProps {
active, active,
name, name,