From 8345de3597c54cd039aeea3562ad06413fcb34c8 Mon Sep 17 00:00:00 2001 From: milarin Date: Sat, 7 Oct 2023 16:21:49 +0200 Subject: [PATCH] initial commit --- event.go | 16 +++++ event_type.go | 9 +++ get_tree.go | 23 +++++++ get_tree_test.go | 16 +++++ go.mod | 5 ++ go.sum | 2 + node.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++ subscribe.go | 38 ++++++++++++ subscribe_test.go | 18 ++++++ 9 files changed, 277 insertions(+) create mode 100644 event.go create mode 100644 event_type.go create mode 100644 get_tree.go create mode 100644 get_tree_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 node.go create mode 100644 subscribe.go create mode 100644 subscribe_test.go diff --git a/event.go b/event.go new file mode 100644 index 0000000..47cdc22 --- /dev/null +++ b/event.go @@ -0,0 +1,16 @@ +package sway + +import ( + "encoding/json" + + "git.milar.in/milarin/adverr" +) + +type Event struct { + Change string `json:"change"` + Container Node `json:"container"` +} + +func (e Event) String() string { + return string(adverr.Must(json.MarshalIndent(e, "", "\t"))) +} diff --git a/event_type.go b/event_type.go new file mode 100644 index 0000000..77fb3c3 --- /dev/null +++ b/event_type.go @@ -0,0 +1,9 @@ +package sway + +type EventType string + +const ( + EventTypeWindow EventType = "window" + EventTypeWorkspace EventType = "workspace" + // TODO +) diff --git a/get_tree.go b/get_tree.go new file mode 100644 index 0000000..c6da1e7 --- /dev/null +++ b/get_tree.go @@ -0,0 +1,23 @@ +package sway + +import ( + "context" + "encoding/json" + "os/exec" +) + +func GetTree(ctx context.Context) (*Node, error) { + cmd := exec.CommandContext(ctx, "swaymsg", "-t", "get_tree") + + data, err := cmd.Output() + if err != nil { + return nil, err + } + + var root Node + if err := json.Unmarshal(data, &root); err != nil { + return nil, err + } + + return &root, nil +} diff --git a/get_tree_test.go b/get_tree_test.go new file mode 100644 index 0000000..d1a09a0 --- /dev/null +++ b/get_tree_test.go @@ -0,0 +1,16 @@ +package sway + +import ( + "context" + "fmt" + "testing" +) + +func TestGetTree(t *testing.T) { + root, err := GetTree(context.Background()) + if err != nil { + t.Fatal(err) + } + + fmt.Println(root.ID, root.Name) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3ce9f4b --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.milar.in/milarin/sway + +go 1.21.1 + +require git.milar.in/milarin/adverr v1.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1664c40 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +git.milar.in/milarin/adverr v1.1.1 h1:ENtBcqT7CncLsVfaLC3KzX8QSSGiSpsC7I7wDqladu8= +git.milar.in/milarin/adverr v1.1.1/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E= diff --git a/node.go b/node.go new file mode 100644 index 0000000..6c6ad44 --- /dev/null +++ b/node.go @@ -0,0 +1,150 @@ +package sway + +type Node struct { + ID NodeID `json:"id"` + Type NodeType `json:"type"` + Orientation Orientation `json:"orientation"` + Percentage float64 `json:"percent"` + Urgent bool `json:"urgent"` + Marks []string `json:"marks"` + Focused bool `json:"focused"` + Layout string `json:"layout"` + Border BorderStyle `json:"border"` + CurrentBorderWidth int `json:"current_border_width"` + Rect Rect `json:"rect"` + DecoRect Rect `json:"deco_rect"` + WindowRect Rect `json:"window_rect"` + Geometry Rect `json:"geometry"` + Name string `json:"name"` + XorgWindowID *int64 `json:"window"` + Nodes []Node `json:"nodes"` + FloatingNodes []Node `json:"floating_nodes"` + Focus []NodeID `json:"focus"` + FullscreenMode FullscreenMode `json:"fullscreen_mode"` + Sticky bool `json:"sticky"` + PID int64 `json:"pid"` + AppID string `json:"app_id"` + Visible bool `json:"visible"` + MaxRenderTimeMilli int64 `json:"max_render_time"` + Shell string `json:"shell"` + InhibitIdle bool `json:"inhibit_idle"` + IdleInhibitors IdleInhibitors `json:"idle_inhibitors"` + + // NodeTypeOutput only + Primary bool `json:"primary"` + Make string `json:"make"` + Model string `json:"model"` + Serial string `json:"serial"` + Modes []OutputMode `json:"modes"` + NonDesktop bool `json:"non_desktop"` + Active bool `json:"active"` + DPMS bool `json:"dpms"` + Power bool `json:"power"` + Scale float64 `json:"scale"` + ScaleFilter ScaleFilter `json:"scale_filter"` + Transform Transform `json:"transform"` + AdaptiveSync string `json:"adaptive_sync_status"` + LayerShellSurfaces []LayerShellSurface `json:"layer_shell_surfaces"` + CurrentWorkspace string `json:"current_workspace"` + CurrentMode OutputMode `json:"current_mode"` +} + +type NodeID int64 + +type NodeType string + +const ( + NodeTypeRoot NodeType = "root" + NodeTypeOutput NodeType = "output" + NodeTypeCon NodeType = "con" + NodeTypeFloatingCon NodeType = "floating_con" + NodeTypeWorkspace NodeType = "workspace" + NodeTypeDockarea NodeType = "dockarea" +) + +type Rect struct { + Y int `json:"y"` + X int `json:"x"` + Width int `json:"width"` + Height int `json:"height"` +} + +type BorderStyle string + +const ( + BorderStyleNormal BorderStyle = "normal" + BorderStyleNo BorderStyle = "none" + BorderStylePixel BorderStyle = "pixel" +) + +type Layout string + +const ( + LayoutSplitH Layout = "splith" + LayoutSplitV Layout = "splitv" + LayoutStacked Layout = "stacked" + LayoutTabbed Layout = "tabbed" + LayoutDockarea Layout = "dockarea" + LayoutOutput Layout = "output" +) + +type FullscreenMode int64 + +const ( + FullscreenNone FullscreenMode = 0 + FullscreenOutput FullscreenMode = 1 + FullscreenGlobal FullscreenMode = 2 +) + +type IdleInhibitors struct { + User string `json:"user"` + Application string `json:"application"` +} + +type OutputMode struct { + Width int `json:"width"` + Height int `json:"height"` + RefreshRate int `json:"refresh"` + AspectRatio string `json:"picture_aspect_ratio"` +} + +type ScaleFilter string + +const ( + ScaleFilterNearest ScaleFilter = "nearest" + ScaleFilterLinear ScaleFilter = "linear" + ScaleFilterSmart ScaleFilter = "smart" +) + +type Transform string + +const ( + TransformNormal Transform = "normal" + Transform90 Transform = "90" + Transform180 Transform = "180" + Transform270 Transform = "270" + TransformFlipped Transform = "flipped" + TransformFlipped90 Transform = "flipped-90" + TransformFlipped180 Transform = "flipped-180" + TransformFlipped270 Transform = "flipped-270" +) + +type LayerShellSurface struct { + Namespace string `json:"namespace"` + Layer string `json:"layer"` + Extent Rect `json:"extenct"` + Effects []Effect `json:"effects"` +} + +type Effect string + +const ( + EffectBlur Effect = "blur" +) + +type Orientation string + +const ( + OrientationHorizontal Orientation = "horizontal" + OrientationVertical Orientation = "vertical" +) diff --git a/subscribe.go b/subscribe.go new file mode 100644 index 0000000..f10fb5e --- /dev/null +++ b/subscribe.go @@ -0,0 +1,38 @@ +package sway + +import ( + "context" + "encoding/json" + "os/exec" +) + +func Subscribe(ctx context.Context, events ...EventType) (<-chan Event, error) { + data, err := json.Marshal(events) + if err != nil { + return nil, err + } + + cmd := exec.CommandContext(ctx, "swaymsg", "-t", "subscribe", "-m", string(data)) + + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, err + } + + out := make(chan Event, 10) + + go func() { + defer cmd.Wait() + dec := json.NewDecoder(stdout) + var event Event + for err := dec.Decode(&event); err == nil; err = dec.Decode(&event) { + out <- event + } + }() + + if err := cmd.Start(); err != nil { + return nil, err + } + + return out, nil +} diff --git a/subscribe_test.go b/subscribe_test.go new file mode 100644 index 0000000..475dbe0 --- /dev/null +++ b/subscribe_test.go @@ -0,0 +1,18 @@ +package sway + +import ( + "context" + "fmt" + "testing" +) + +func TestSubscribe(t *testing.T) { + events, err := Subscribe(context.Background(), EventTypeWindow, EventTypeWorkspace) + if err != nil { + t.Fatal(err) + } + + for event := range events { + fmt.Println(event) + } +}