go-i3/tree.go

195 lines
5.7 KiB
Go
Raw Permalink Normal View History

2023-10-22 16:19:10 +02:00
package i3
import (
"encoding/json"
)
// NodeType indicates the specific kind of Node.
type NodeType string
// i3 currently implements the following node types:
const (
Root NodeType = "root"
OutputNode NodeType = "output"
Con NodeType = "con"
FloatingCon NodeType = "floating_con"
WorkspaceNode NodeType = "workspace"
DockareaNode NodeType = "dockarea"
)
// Layout indicates the layout of a Node.
type Layout string
// i3 currently implements the following layouts:
const (
SplitH Layout = "splith"
SplitV Layout = "splitv"
Stacked Layout = "stacked"
Tabbed Layout = "tabbed"
DockareaLayout Layout = "dockarea"
OutputLayout Layout = "output"
)
// BorderStyle indicates the border style of a node.
type BorderStyle string
// i3 currently implements the following border styles:
const (
NormalBorder BorderStyle = "normal"
NoBorder BorderStyle = "none"
PixelBorder BorderStyle = "pixel"
)
// Rect is a rectangle, used for various dimensions in Node, for example.
type Rect struct {
X int64 `json:"x"`
Y int64 `json:"y"`
Width int64 `json:"width"`
Height int64 `json:"height"`
}
// WindowProperties correspond to X11 window properties
//
// See https://build.i3wm.org/docs/ipc.html#_tree_reply
type WindowProperties struct {
Title string `json:"title"`
Instance string `json:"instance"`
Class string `json:"class"`
Role string `json:"window_role"`
Transient NodeID `json:"transient_for"`
}
// NodeID is an i3-internal ID for the node, which can be used to identify
// containers within the IPC interface.
type NodeID int64
// FullscreenMode indicates whether the container is fullscreened, and relative
// to where (its output, or globally). Note that all workspaces are considered
// fullscreened on their respective output.
type FullscreenMode int64
const (
FullscreenNone FullscreenMode = 0
FullscreenOutput FullscreenMode = 1
FullscreenGlobal FullscreenMode = 2
)
// FloatingType indicates the floating type of Node.
type FloatingType string
// i3 currently implements the following node types:
const (
AutoOff FloatingType = "auto_off"
AutoOn FloatingType = "auto_on"
UserOn FloatingType = "user_on"
UserOff FloatingType = "user_off"
)
// Node is a node in a Tree.
//
// See https://i3wm.org/docs/ipc.html#_tree_reply for more details.
type Node struct {
ID NodeID `json:"id"`
Name string `json:"name"` // window: title, container: internal name
Type NodeType `json:"type"`
Border BorderStyle `json:"border"`
CurrentBorderWidth int64 `json:"current_border_width"`
Layout Layout `json:"layout"`
Percent float64 `json:"percent"`
Rect Rect `json:"rect"` // absolute (= relative to X11 display)
WindowRect Rect `json:"window_rect"` // window, relative to Rect
DecoRect Rect `json:"deco_rect"` // decoration, relative to Rect
Geometry Rect `json:"geometry"` // original window geometry, absolute
Window int64 `json:"window"` // X11 window ID of the client window
WindowProperties WindowProperties `json:"window_properties"`
Urgent bool `json:"urgent"` // urgency hint set
Marks []string `json:"marks"`
Focused bool `json:"focused"`
WindowType string `json:"window_type"`
FullscreenMode FullscreenMode `json:"fullscreen_mode"`
Focus []NodeID `json:"focus"`
Nodes []*Node `json:"nodes"`
FloatingNodes []*Node `json:"floating_nodes"`
Floating FloatingType `json:"floating"`
ScratchpadState string `json:"scratchpad_state"`
AppID string `json:"app_id"` // if talking to Sway: Wayland App ID
Sticky bool `json:"sticky"`
Output string `json:"output"`
}
// FindChild returns the first Node matching predicate, using pre-order
// depth-first search.
func (n *Node) FindChild(predicate func(*Node) bool) *Node {
if predicate(n) {
return n
}
for _, c := range n.Nodes {
if con := c.FindChild(predicate); con != nil {
return con
}
}
for _, c := range n.FloatingNodes {
if con := c.FindChild(predicate); con != nil {
return con
}
}
return nil
}
// FindFocused returns the first Node matching predicate from the sub-tree of
// directly and indirectly focused containers.
//
// As an example, consider this layout tree (simplified):
//
// root
// │
// HDMI2
//
// … workspace 1
//
// XTerm Firefox
//
// In this example, if Firefox is focused, FindFocused will return the first
// container matching predicate of root, HDMI2, workspace 1, Firefox (in this
// order).
func (n *Node) FindFocused(predicate func(*Node) bool) *Node {
if predicate(n) {
return n
}
if len(n.Focus) == 0 {
return nil
}
first := n.Focus[0]
for _, c := range n.Nodes {
if c.ID == first {
return c.FindFocused(predicate)
}
}
for _, c := range n.FloatingNodes {
if c.ID == first {
return c.FindFocused(predicate)
}
}
return nil
}
// Tree is an i3 layout tree, starting with Root.
type Tree struct {
// Root is the root node of the layout tree.
Root *Node
}
// GetTree returns i3s layout tree.
//
// GetTree is supported in i3 ≥ v4.0 (2011-07-31).
func GetTree() (Tree, error) {
reply, err := roundTrip(messageTypeGetTree, nil)
if err != nil {
return Tree{}, err
}
var root Node
err = json.Unmarshal(reply.Payload, &root)
return Tree{Root: &root}, err
}