package swayipc import ( "encoding/json" "fmt" ) const ( NodeTypeRoot = iota + 1 // A root node NodeTypeOutput // An output node NodeTypeWorkspace // A workspace NodeTypeCon // A container node (containing more nodes) NodeTypeFloating // A floating node ) // Node can be an output, a workspace, a container or a container // containing other containers. type Node struct { ID int `json:"id"` Type string `json:"type"` // output, workspace or container Name string `json:"name"` // workspace number or app name Output string `json:"output"` Nodes []*Node `json:"nodes"` FloatingNodes []*Node `json:"floating_nodes"` Focused bool `json:"focused"` Visible bool `json:"visible"` Urgent bool `json:"urgent"` Sticky bool `json:"sticky"` Border string `json:"border"` Layout string `json:"layout"` Orientation string `json:"orientation"` CurrentBorderWidth int `json:"current_border_width"` Percent float32 `json:"percent"` Focus []int `json:"focus"` Window int `json:"window"` // wayland native X11Window string `json:"app_id"` // x11 compat CurrentWorkspace string `json:"current_workspace"` Rect Rect `json:"rect"` WindowRect Rect `json:"window_rect"` DecoRect Rect `json:"deco_rect"` Geometry Rect `json:"geometry"` } // GetTree returns the whole information tree, which contains // everything from output to containers as a tree of nodes. Each node // has a field 'Nodes' which points to a list subnodes. Some nodes // also have a field 'FloatingNodes' which points to a list of // floating containers. // // The top level node is the "root" node. // // Use the returned node oject to further investigate the wm setup. func (ipc *SwayIPC) GetTree() (*Node, error) { err := ipc.sendHeader(MsgGettTree, 0) if err != nil { return nil, err } payload, err := ipc.readResponse() if err != nil { return nil, err } node := &Node{} if err := json.Unmarshal(payload.Payload, &node); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } return node, nil } // FindFocused returns the container which has currently the // focus. Usually called on the root node. func (node *Node) FindFocused() *Node { focused := searchFocused(node.Nodes) if focused == nil { return searchFocused(node.FloatingNodes) } return nil } // FindCurrentWorkspace returns the current active // workspace name. Usually called on the root node. func (node *Node) FindCurrentWorkspace() string { return searchCurrentWorkspace(node.Nodes) } // searchCurrentWorkspace search for current workspace func searchCurrentWorkspace(nodes []*Node) string { for _, node := range nodes { if node.CurrentWorkspace != "" { return node.CurrentWorkspace } else { return searchCurrentWorkspace(node.Nodes) } } return "" } // searchFocused recursively search focused node func searchFocused(nodes []*Node) *Node { for _, node := range nodes { if node.Focused { return node } else { focused := searchFocused(node.Nodes) if focused == nil { return searchFocused(node.FloatingNodes) } } } return nil }