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
---@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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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();

View file

@ -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,
},
}

View file

@ -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,