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 i3’s 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 }