diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac1c28a --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +[![Go Report Card](https://goreportcard.com/badge/github.com/tlinden/i3ipc)](https://goreportcard.com/report/github.com/tlinden/i3ipc) +[![Actions](https://github.com/tlinden/i3ipc/actions/workflows/ci.yaml/badge.svg)](https://github.com/tlinden/i3ipc/actions) +![GitHub License](https://img.shields.io/github/license/tlinden/i3ipc) +[![GoDoc](https://godoc.org/github.com/tlinden/i3ipc?status.svg)](https://godoc.org/github.com/tlinden/i3ipc) + +# i3ipc - go bindings to control sway (and possibly i3) + +This is a go module which you can use to control [sway](https://swaywm.org/), +[swayfx](https://github.com/WillPower3309/swayfx) and possibly [i3wm](http://i3wm.org/). + +## About +i3ipc's interprocess communication (or ipc) is the interface i3wm and +sway use to receive commands from client applications such as +i3-msg. It also features a publish/subscribe mechanism for notifying +interested parties of window manager events. + +i3ipc is a go module for controlling the window manager. This project +is intended to be useful for general scripting, and for applications +that interact with the window manager like status line generators, +notification daemons, and window pagers. + +For details on how to use the library, see the +[reference documentation](https://godoc.org/github.com/tlinden/i3ipc). + +## Example usage + +In this example we retrieve the current focused window: + +```go +package main + +import ( + "fmt" + "log" + + "github.com/tlinden/i3ipc" +) + +func main() { + ipc := i3ipc.NewI3ipc() + + err := ipc.Connect() + if err != nil { + log.Fatal(err) + } + defer ipc.Close() + + tree, err := ipc.GetTree() + if err != nil { + log.Fatal(err) + } + + focused := tree.FindFocused() + + if focused != nil { + fmt.Printf("focused node: %s\n id: %d\n Geometry: %dx%d\n", + focused.Name, focused.Id, focused.Geometry.Width, + focused.Geometry.Height) + } +} +``` + +Also take a look into the **_examples** folder for more examples. + +You may take a look at the [tool swaycycle](https://github.com/tlinden/swaycycle) +which is using this module. + +## Installation + +Execute this to add the module to your project: +```sh +go get github.com/tlinden/i3ipc +``` + +## Acknowledgements + +A couple of ideas have been taken from the [i3ipc python +module](https://github.com/altdesktop/i3ipc-python/), although this +one is not just a port of it and has been written from scratch. + +## Getting help + +Although I'm happy to hear from i3ipc users in private email, that's the +best way for me to forget to do something. + +In order to report a bug, unexpected behavior, feature requests or to +submit a patch, please open an issue on github: +https://github.com/TLINDEN/i3ipc/issues. + +## Copyright and license + +This software is licensed under the GNU GENERAL PUBLIC LICENSE version 3. + +## Authors + +T.v.Dein + +## Project homepage + +https://github.com/TLINDEN/i3ipc + +## Copyright and License + +Licensed under the GNU GENERAL PUBLIC LICENSE version 3. + +## Author + +T.v.Dein + diff --git a/TODO.md b/TODO.md index c0fef1c..bcee354 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,3 @@ # to be implemented -- subscribe -- events - send_tick -- `get_binding_state` -- get_inputs -- get_seats diff --git a/_examples/current_workspace/main.go b/_examples/current_workspace/main.go index e023164..a05a7fd 100644 --- a/_examples/current_workspace/main.go +++ b/_examples/current_workspace/main.go @@ -8,7 +8,7 @@ import ( ) func main() { - ipc := i3ipc.NewI3ipc("SWAYSOCK") + ipc := i3ipc.NewI3ipc() err := ipc.Connect() if err != nil { diff --git a/_examples/events/main.go b/_examples/events/main.go new file mode 100644 index 0000000..92efcb5 --- /dev/null +++ b/_examples/events/main.go @@ -0,0 +1,58 @@ +package main + +/* + Demonstrate subscribing to events +*/ + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/alecthomas/repr" + "github.com/tlinden/i3ipc" +) + +// Event callback function, needs to implement each subscribed events, +// fed to it as RawResponse +func ProcessTick(event *i3ipc.RawResponse) error { + var err error + switch event.PayloadType { + case i3ipc.EV_Tick: + ev := &i3ipc.EventTick{} + err = json.Unmarshal(event.Payload, &ev) + repr.Println(ev) + case i3ipc.EV_Window: + ev := &i3ipc.EventWindow{} + err = json.Unmarshal(event.Payload, &ev) + repr.Println(ev) + default: + return fmt.Errorf("received unsubscribed event %d", event.PayloadType) + } + + if err != nil { + return err + } + + return nil +} + +func main() { + ipc := i3ipc.NewI3ipc() + + err := ipc.Connect() + if err != nil { + log.Fatal(err) + } + defer ipc.Close() + + _, err = ipc.Subscribe(&i3ipc.Event{ + Tick: true, + Window: true, + }) + if err != nil { + log.Fatal(err) + } + + ipc.EventLoop(ProcessTick) +} diff --git a/_examples/find_focused_window/main.go b/_examples/find_focused_window/main.go index 06f57a7..c9c1403 100644 --- a/_examples/find_focused_window/main.go +++ b/_examples/find_focused_window/main.go @@ -8,12 +8,13 @@ import ( ) func main() { - ipc := i3ipc.NewI3ipc("SWAYSOCK") + ipc := i3ipc.NewI3ipc() err := ipc.Connect() if err != nil { log.Fatal(err) } + defer ipc.Close() tree, err := ipc.GetTree() if err != nil { diff --git a/_examples/find_focused_window/tree.json b/_examples/find_focused_window/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/find_focused_window/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_bars/main.go b/_examples/get_bars/main.go index eaefd73..0795c81 100644 --- a/_examples/get_bars/main.go +++ b/_examples/get_bars/main.go @@ -19,6 +19,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() bars, err := ipc.GetBars() if err != nil { diff --git a/_examples/get_bars/tree.json b/_examples/get_bars/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_bars/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_config/main.go b/_examples/get_config/main.go index ee5272a..a80c4fb 100644 --- a/_examples/get_config/main.go +++ b/_examples/get_config/main.go @@ -18,6 +18,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() config, err := ipc.GetConfig() if err != nil { diff --git a/_examples/get_config/tree.json b/_examples/get_config/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_config/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_inputs/main.go b/_examples/get_inputs/main.go new file mode 100644 index 0000000..2b6f83f --- /dev/null +++ b/_examples/get_inputs/main.go @@ -0,0 +1,29 @@ +package main + +/* + Retrieve a list of current available inputs +*/ + +import ( + "log" + + "github.com/alecthomas/repr" + "github.com/tlinden/i3ipc" +) + +func main() { + ipc := i3ipc.NewI3ipc() + + err := ipc.Connect() + if err != nil { + log.Fatal(err) + } + defer ipc.Close() + + inputs, err := ipc.GetInputs() + if err != nil { + log.Fatal(err) + } + + repr.Println(inputs) +} diff --git a/_examples/get_marks/main.go b/_examples/get_marks/main.go index ccfb9ef..7b76c24 100644 --- a/_examples/get_marks/main.go +++ b/_examples/get_marks/main.go @@ -18,6 +18,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() marks, err := ipc.GetMarks() if err != nil { diff --git a/_examples/get_marks/tree.json b/_examples/get_marks/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_marks/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_outputs/log b/_examples/get_outputs/log deleted file mode 100644 index 5e4b4da..0000000 --- a/_examples/get_outputs/log +++ /dev/null @@ -1,124 +0,0 @@ -execve("/opt/bin/swaymsg", ["swaymsg", "-t", "get_bar_config", "bar-0"], 0x7ffca8a3d4b0 /* 96 vars */) = 0 -brk(NULL) = 0x57744ffbd000 -mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x70933d94d000 -access("/etc/ld.so.preload", R_OK) = 0 -openat(AT_FDCWD, "/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3 -fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0 -close(3) = 0 -openat(AT_FDCWD, "glibc-hwcaps/x86-64-v3/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "glibc-hwcaps/x86-64-v2/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -newfstatat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v3/", 0x7fffe6d741a0, 0) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v2/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -newfstatat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/glibc-hwcaps/x86-64-v2/", 0x7fffe6d741a0, 0) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -newfstatat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0 -openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 -fstat(3, {st_mode=S_IFREG|0644, st_size=137987, ...}) = 0 -mmap(NULL, 137987, PROT_READ, MAP_PRIVATE, 3, 0) = 0x70933d92b000 -close(3) = 0 -openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libjson-c.so.5", O_RDONLY|O_CLOEXEC) = 3 -read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832 -fstat(3, {st_mode=S_IFREG|0644, st_size=80344, ...}) = 0 -mmap(NULL, 82416, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x70933d916000 -mmap(0x70933d91a000, 45056, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x70933d91a000 -mmap(0x70933d925000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0x70933d925000 -mmap(0x70933d929000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x12000) = 0x70933d929000 -close(3) = 0 -openat(AT_FDCWD, "glibc-hwcaps/x86-64-v3/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "glibc-hwcaps/x86-64-v2/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "/opt/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) -openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 -read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@\247\2\0\0\0\0\0"..., 832) = 832 -pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840 -fstat(3, {st_mode=S_IFREG|0755, st_size=2178688, ...}) = 0 -pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840 -mmap(NULL, 2223736, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x70933d600000 -mmap(0x70933d628000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x70933d628000 -mmap(0x70933d7bd000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x70933d7bd000 -mmap(0x70933d80c000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20b000) = 0x70933d80c000 -mmap(0x70933d812000, 52856, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x70933d812000 -close(3) = 0 -mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x70933d913000 -arch_prctl(ARCH_SET_FS, 0x70933d913740) = 0 -set_tid_address(0x70933d913a10) = 97421 -set_robust_list(0x70933d913a20, 24) = 0 -rseq(0x70933d913680, 0x20, 0, 0x53053053) = 0 -mprotect(0x70933d80c000, 16384, PROT_READ) = 0 -mprotect(0x70933d929000, 4096, PROT_READ) = 0 -mprotect(0x577441241000, 4096, PROT_READ) = 0 -mprotect(0x70933d98f000, 8192, PROT_READ) = 0 -prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 -munmap(0x70933d92b000, 137987) = 0 -ioctl(1, TCGETS, 0x7fffe6d74e80) = -1 ENOTTY (Inappropriate ioctl for device) -getrandom("\xf3\xb2\x5f\xb5\x3a\x0e\x13\x06", 8, GRND_NONBLOCK) = 8 -brk(NULL) = 0x57744ffbd000 -brk(0x57744ffde000) = 0x57744ffde000 -socket(AF_UNIX, SOCK_STREAM, 0) = 3 -connect(3, {sa_family=AF_UNIX, sun_path="/run/user/1000/sway-ipc.1000.3461.sock"}, 110) = 0 -setsockopt(3, SOL_SOCKET, SO_RCVTIMEO_OLD, "\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0 -write(3, "i3-ipc\5\0\0\0\6\0\0\0", 14) = 14 -write(3, "bar-0", 5) = 5 -recvfrom(3, "i3-ipc\332\4\0\0\6\0\0\0", 14, 0, NULL, NULL) = 14 -recvfrom(3, "{ \"id\": \"bar-0\", \"mode\": \"dock\","..., 1242, 0, NULL, NULL) = 1242 -rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0 -mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_DROPPABLE|MAP_ANONYMOUS, -1, 0) = 0x70933d94c000 -mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x70933d94b000 -rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 -getrandom("\x92\xda\xab\x78\x81\x42\x79\x34\x6f\x90\x94\x8c\xb4\x47\xfb\x85\xc1\x00\x43\x1b\x00\x78\x10\x09\x87\x65\x09\x2e\x79\xdb\x4d\xf7", 32, 0) = 32 -fstat(1, {st_mode=S_IFREG|0644, st_size=5648, ...}) = 0 -close(3) = 0 -write(1, "{\n \"id\": \"bar-0\",\n \"mode\": \"do"..., 1387{ - "id": "bar-0", - "mode": "dock", - "hidden_state": "hide", - "position": "top", - "status_command": null, - "font": "monospace 10", - "gaps": { - "top": 0, - "right": 0, - "bottom": 0, - "left": 0 - }, - "bar_height": 0, - "status_padding": 1, - "status_edge_padding": 3, - "wrap_scroll": false, - "workspace_buttons": true, - "strip_workspace_numbers": false, - "strip_workspace_name": false, - "workspace_min_width": 0, - "binding_mode_indicator": true, - "verbose": false, - "pango_markup": true, - "colors": { - "background": "#000000ff", - "statusline": "#ffffffff", - "separator": "#666666ff", - "focused_background": "#000000ff", - "focused_statusline": "#ffffffff", - "focused_separator": "#666666ff", - "focused_workspace_border": "#4c7899ff", - "focused_workspace_bg": "#285577ff", - "focused_workspace_text": "#ffffffff", - "inactive_workspace_border": "#333333ff", - "inactive_workspace_bg": "#222222ff", - "inactive_workspace_text": "#888888ff", - "active_workspace_border": "#333333ff", - "active_workspace_bg": "#5f676aff", - "active_workspace_text": "#ffffffff", - "urgent_workspace_border": "#2f343aff", - "urgent_workspace_bg": "#900000ff", - "urgent_workspace_text": "#ffffffff", - "binding_mode_border": "#2f343aff", - "binding_mode_bg": "#900000ff", - "binding_mode_text": "#ffffffff" - }, - "tray_padding": 2 -} -) = 1387 -exit_group(0) = ? -+++ exited with 0 +++ diff --git a/_examples/get_outputs/main.go b/_examples/get_outputs/main.go index 5086e6d..8a1564b 100644 --- a/_examples/get_outputs/main.go +++ b/_examples/get_outputs/main.go @@ -18,6 +18,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() outputs, err := ipc.GetOutputs() if err != nil { diff --git a/_examples/get_outputs/tree.json b/_examples/get_outputs/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_outputs/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_seats/main.go b/_examples/get_seats/main.go new file mode 100644 index 0000000..b80becf --- /dev/null +++ b/_examples/get_seats/main.go @@ -0,0 +1,29 @@ +package main + +/* + Retrieve a list of current available inputs +*/ + +import ( + "log" + + "github.com/alecthomas/repr" + "github.com/tlinden/i3ipc" +) + +func main() { + ipc := i3ipc.NewI3ipc() + + err := ipc.Connect() + if err != nil { + log.Fatal(err) + } + defer ipc.Close() + + seats, err := ipc.GetSeats() + if err != nil { + log.Fatal(err) + } + + repr.Println(seats) +} diff --git a/_examples/get_tree/main.go b/_examples/get_tree/main.go index 7323a71..cba9aea 100644 --- a/_examples/get_tree/main.go +++ b/_examples/get_tree/main.go @@ -8,7 +8,7 @@ import ( ) func main() { - ipc := i3ipc.NewI3ipc("SWAYSOCK") + ipc := i3ipc.NewI3ipc() err := ipc.Connect() if err != nil { diff --git a/_examples/get_tree/tree.json b/_examples/get_tree/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_tree/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_version/main.go b/_examples/get_version/main.go index 90a2f64..39bb326 100644 --- a/_examples/get_version/main.go +++ b/_examples/get_version/main.go @@ -18,6 +18,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() version, err := ipc.GetVersion() if err != nil { diff --git a/_examples/get_version/tree.json b/_examples/get_version/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_version/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/get_workspaces/main.go b/_examples/get_workspaces/main.go index 7439994..167f890 100644 --- a/_examples/get_workspaces/main.go +++ b/_examples/get_workspaces/main.go @@ -18,6 +18,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() workspaces, err := ipc.GetWorkspaces() if err != nil { diff --git a/_examples/get_workspaces/tree.json b/_examples/get_workspaces/tree.json deleted file mode 100644 index 24eaa5a..0000000 --- a/_examples/get_workspaces/tree.json +++ /dev/null @@ -1,346 +0,0 @@ -&i3ipc.Node{ - Id: 1, - Type: "root", - Name: "root", - Nodes: []*i3ipc.Node{ - { - Id: 2147483647, - Type: "output", - Name: "__i3", - Nodes: []*i3ipc.Node{ - { - Id: 2147483646, - Type: "workspace", - Name: "__i3_scratch", - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "horizontal", - Focus: []int{ - 2147483646, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - { - Id: 4, - Type: "output", - Name: "HDMI-A-1", - Nodes: []*i3ipc.Node{ - { - Id: 7, - Type: "workspace", - Name: "2", - Nodes: []*i3ipc.Node{ - { - Id: 8, - Type: "con", - Name: "Terminal - 1:2:sh - \"tripod\" ", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.21511628, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 10, - Type: "con", - Name: "postgreslet - F-I-TS-Chat - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.78488374, - Window: 4194311, - Rect: i3ipc.Rect{ - X: 740, - Y: 26, - Width: 2700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2696, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 10, - 8, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 11, - Type: "workspace", - Name: "3", - Nodes: []*i3ipc.Node{ - { - Id: 14, - Type: "con", - Name: "AdZ Desktop (SSL/TLS Secured, 256 bit)", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.3604651, - Window: 14680067, - Rect: i3ipc.Rect{ - Y: 26, - Width: 1240, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1236, - }, - Geometry: i3ipc.Rect{ - Width: 2752, - }, - }, - { - Id: 12, - Type: "con", - Name: "E-Mail – Thomas.vonDein@f-i-ts.de - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.6395349, - Window: 4194324, - Rect: i3ipc.Rect{ - X: 1240, - Y: 26, - Width: 2200, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 2196, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 12, - 14, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 15, - Type: "workspace", - Name: "4", - Nodes: []*i3ipc.Node{ - { - Id: 16, - Type: "con", - Name: "AR System Customizable Home Page (Suchen) - Chromium", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 1, - Window: 4194338, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - WindowRect: i3ipc.Rect{ - Width: 3440, - }, - Geometry: i3ipc.Rect{ - Width: 1142, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 16, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 17, - Type: "workspace", - Name: "5", - Nodes: []*i3ipc.Node{ - { - Id: 19, - Type: "con", - Name: "Terminal - 0:5:go - \"tripod\" ", - Focused: true, - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.50581396, - X11Window: "xfce4-terminal", - Rect: i3ipc.Rect{ - Y: 26, - Width: 1740, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1736, - }, - Geometry: i3ipc.Rect{ - Width: 882, - }, - }, - { - Id: 18, - Type: "con", - Name: "emacs /home/scip/dev/i3ipc/_examples/tree/main.go", - Border: "pixel", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.49418604, - Window: 18874540, - Rect: i3ipc.Rect{ - X: 1740, - Y: 26, - Width: 1700, - }, - WindowRect: i3ipc.Rect{ - X: 2, - Y: 2, - Width: 1696, - }, - Geometry: i3ipc.Rect{ - Width: 752, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 19, - 18, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - { - Id: 20, - Type: "workspace", - Name: "6", - FloatingNodes: []*i3ipc.Node{ - { - Id: 21, - Type: "floating_con", - Name: "Home - Webex - Google Chrome", - Border: "csd", - Layout: "none", - Orientation: "none", - CurrentBorderWidth: 2, - Percent: 0.7971424, - X11Window: "google-chrome", - Rect: i3ipc.Rect{ - X: 481, - Y: 35, - Width: 2822, - }, - WindowRect: i3ipc.Rect{ - Width: 2822, - }, - Geometry: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 21, - }, - Rect: i3ipc.Rect{ - Y: 26, - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "output", - Orientation: "none", - Percent: 1, - Focus: []int{ - 17, - 7, - 20, - 11, - 15, - }, - Current_workspace: "5", - Rect: i3ipc.Rect{ - Width: 3440, - }, - }, - }, - Border: "none", - Layout: "splith", - Orientation: "horizontal", - Focus: []int{ - 4, - }, - Rect: i3ipc.Rect{ - Width: 3440, - }, -} -root: root -node: __i3 - focus: false -recursing -node: __i3_scratch - focus: false -recursing -nil diff --git a/_examples/run_command/main.go b/_examples/run_command/main.go index ddfe023..a50ae25 100644 --- a/_examples/run_command/main.go +++ b/_examples/run_command/main.go @@ -20,6 +20,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() // retrieve the sway tree tree, err := ipc.GetTree() @@ -36,7 +37,7 @@ func main() { // finally execute the given commands on it, you can use any run // command, see sway(5) - responses, err := ipc.RunCommand(focused.Id, "floating toggle, border toggle") + responses, err := ipc.RunContainerCommand(focused.Id, "floating toggle, border toggle") if err != nil { repr.Println(responses) log.Fatal(err) diff --git a/_examples/run_global_command/main.go b/_examples/run_global_command/main.go index 41fc304..962cfff 100644 --- a/_examples/run_global_command/main.go +++ b/_examples/run_global_command/main.go @@ -19,6 +19,7 @@ func main() { if err != nil { log.Fatal(err) } + defer ipc.Close() responses, err := ipc.RunGlobalCommand("border toggle") if err != nil { diff --git a/_examples/send_tick/main.go b/_examples/send_tick/main.go new file mode 100644 index 0000000..e806ab1 --- /dev/null +++ b/_examples/send_tick/main.go @@ -0,0 +1,39 @@ +package main + +/* + send a tick with an arbitrary payload, supply some argument to the + command, which will then be attached to the tick. Use the events + example to retrieve the tick (command out the Window event type to + better see it) +*/ + +import ( + "fmt" + "log" + "os" + + "github.com/tlinden/i3ipc" +) + +func main() { + payload := "send_tick.go" + + if len(os.Args) > 1 { + payload = os.Args[1] + } + + ipc := i3ipc.NewI3ipc() + + err := ipc.Connect() + if err != nil { + log.Fatal(err) + } + defer ipc.Close() + + err = ipc.SendTick(payload) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Sent tick with payload '%s'.\n", payload) +} diff --git a/bar.go b/bar.go index 352a128..8ef45fd 100644 --- a/bar.go +++ b/bar.go @@ -1,3 +1,19 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ package i3ipc import ( @@ -5,6 +21,7 @@ import ( "fmt" ) +// Container gaps type Gaps struct { Top int `json:"top"` Right int `json:"right"` @@ -12,6 +29,7 @@ type Gaps struct { Left int `json:"left"` } +// Color definition, used primarily by bars type Colors struct { Background string `json:"background"` Statusline string `json:"statusline"` @@ -36,6 +54,7 @@ type Colors struct { BindingModeText string `json:"binding_mode_text"` } +// A bar such as a swaybar(5) type Bar struct { Id string `json:"id"` Mode string `json:"mode"` @@ -54,6 +73,7 @@ type Bar struct { Colors *Colors `json:"colors"` } +// Get a list of currently visible and active bar names func (ipc *I3ipc) GetBars() ([]string, error) { payload, err := ipc.get(GET_BAR_CONFIG) if err != nil { @@ -61,13 +81,14 @@ func (ipc *I3ipc) GetBars() ([]string, error) { } bars := []string{} - if err := json.Unmarshal(payload, &bars); err != nil { + if err := json.Unmarshal(payload.Payload, &bars); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } return bars, nil } +// Get the bar object of the bar specified by the string 'id' func (ipc *I3ipc) GetBar(id string) (*Bar, error) { err := ipc.sendHeader(GET_BAR_CONFIG, uint32(len(id))) if err != nil { @@ -85,7 +106,7 @@ func (ipc *I3ipc) GetBar(id string) (*Bar, error) { } bar := &Bar{} - if err := json.Unmarshal(payload, &bar); err != nil { + if err := json.Unmarshal(payload.Payload, &bar); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } diff --git a/command.go b/command.go new file mode 100644 index 0000000..128e7de --- /dev/null +++ b/command.go @@ -0,0 +1,93 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package i3ipc + +import ( + "encoding/json" + "errors" + "fmt" + "strings" +) + +// Execute the specified global command[s] (one or more commands can be +// given) and returns a response list. +// +// Possible commands are all non-specific commands, see sway(5) +func (ipc *I3ipc) RunGlobalCommand(command ...string) ([]Response, error) { + return ipc.RunCommand(0, "", command...) +} + +// Execute the specified container command[s] (one or more commands can be +// given) and returns a response list. +// +// Possible commands are all container-specific commands, see sway(5) +func (ipc *I3ipc) RunContainerCommand(id int, command ...string) ([]Response, error) { + return ipc.RunCommand(id, "con", command...) +} + +// Execute the specified (target) command[s] (one or more commands can be +// given) and returns a response list. +// +// Possible commands are all container-specific commands, see sway(5). +// +// Target can be one of con, workspace, output, input, etc. see sway-ipc(7). +func (ipc *I3ipc) RunCommand(id int, target string, command ...string) ([]Response, error) { + if len(command) == 0 { + return nil, errors.New("empty command arg") + } + + commands := strings.Join(command, ",") + + if id > 0 { + // a type specific command, otherwise global + commands = fmt.Sprintf("[%s_id=%d] %s", target, id, commands) + } + + err := ipc.sendHeader(RUN_COMMAND, uint32(len(commands))) + if err != nil { + return nil, fmt.Errorf("failed to send run_command to IPC %w", err) + } + + err = ipc.sendPayload([]byte(commands)) + if err != nil { + return nil, fmt.Errorf("failed to send switch focus command: %w", err) + } + + payload, err := ipc.readResponse() + if err != nil { + return nil, err + } + + responses := []Response{} + + if err := json.Unmarshal(payload.Payload, &responses); err != nil { + return nil, fmt.Errorf("failed to unmarshal json response: %w", err) + } + + if len(responses) == 0 { + return nil, errors.New("got zero IPC response") + } + + for _, response := range responses { + if !response.Success { + return responses, errors.New("one or more commands failed") + } + } + + return responses, nil +} diff --git a/event.go b/event.go new file mode 100644 index 0000000..ff2d2a3 --- /dev/null +++ b/event.go @@ -0,0 +1,200 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package i3ipc + +import ( + "encoding/json" + "fmt" +) + +// Event types. +const ( + EV_Workspace int = 0x80000000 + EV_Output int = 0x80000001 + EV_Mode int = 0x80000002 + EV_Window int = 0x80000003 + EV_BarconfigUpdate int = 0x80000004 + EV_Binding int = 0x80000005 + EV_Shutdown int = 0x80000006 + EV_Tick int = 0x80000007 + EV_BarStateUpdate int = 0x80000014 + EV_Input int = 0x80000015 +) + +// Subscriber struct, use this to tell i3ipc which events you want to +// subscribe. +type Event struct { + Workspace bool + Output bool + Mode bool + Window bool + BarconfigUpdate bool + Binding bool + Shutdown bool + Tick bool + BarStateUpdate bool + Input bool +} + +// Workspace event response +type EventWorkspace struct { + Change string `json:"change"` + Current *Node `json:"workspace"` + Old *Node `json:"old"` +} + +// Output event response +type EventOutput struct { + Change string `json:"change"` +} + +// Mode event response +type EventMode struct { + Change string `json:"change"` + PangoMarkup *Node `json:"pango_markup"` +} + +// Window event response +type EventWindow struct { + Change string `json:"change"` + Container *Node `json:"container"` +} + +// BarConfig event response +type EventBarConfig struct { + Change string `json:"change"` + Binding *Binding `json:"binding"` +} + +// Shutdown event response +type EventShutdown struct { + Change string `json:"change"` +} + +// Tick event response +type EventTick struct { + First bool `json:"first"` + Payload string `json:"payload"` +} + +// BarState event response +type EventBarState struct { + Id string `json:"id"` + VisibleByModifier bool `json:"visible_by_modifier"` +} + +// Input event response +type EventInput struct { + Change string `json:"change"` + Input *Input `json:"input"` +} + +// Subscribe to one or more events. Fill the i3ipc.Event object +// accordingly. +// +// Returns a response list containing a response for every subscribed +// event. +func (ipc *I3ipc) Subscribe(sub *Event) ([]*Response, error) { + events := []string{} + + // looks ugly but makes it much more comfortable for the user + if sub.Workspace { + events = append(events, "workspace") + } + if sub.Output { + events = append(events, "output") + } + if sub.Mode { + events = append(events, "mode") + } + if sub.Window { + events = append(events, "window") + } + if sub.BarconfigUpdate { + events = append(events, "barconfig_update") + } + if sub.Binding { + events = append(events, "binding") + } + if sub.Shutdown { + events = append(events, "shutdown") + } + if sub.Tick { + events = append(events, "tick") + } + if sub.BarStateUpdate { + events = append(events, "bar_state_update") + } + if sub.Input { + events = append(events, "input") + } + + jsonpayload, err := json.Marshal(events) + if err != nil { + return nil, fmt.Errorf("failed to json marshal event list: %w", err) + } + + err = ipc.sendHeader(SUBSCRIBE, uint32(len(jsonpayload))) + if err != nil { + return nil, err + } + + err = ipc.sendPayload(jsonpayload) + if err != nil { + return nil, err + } + + payload, err := ipc.readResponse() + if err != nil { + responses := []*Response{} + if err := json.Unmarshal(payload.Payload, &responses); err != nil { + return nil, fmt.Errorf("failed to unmarshal json response: %w", err) + } + + return responses, err + } + + // register the subscribed events + ipc.Events = sub + + return nil, nil +} + +// Event loop: Once you have subscribed to an event, you need to enter +// an event loop over the same running socket connection. Sway will +// send event payloads for every subscribed event whenever it happens. +// +// You supply the loop a generic callback function, which will be +// called every time an event occurs. The function will receive the +// i3ipc.RawResponse object for the event. You need to Unmarshall the +// Payload field yourself. +// +// If your callback function returns an error, the event loop returns +// with this error and finishes thus. +func (ipc *I3ipc) EventLoop(callback func(event *RawResponse) error) error { + for { + payload, err := ipc.readResponse() + if err != nil { + return err + } + + if err := callback(payload); err != nil { + return err + } + } +} diff --git a/i3ipc.go b/i3ipc.go index 8d80877..d40571b 100644 --- a/i3ipc.go +++ b/i3ipc.go @@ -1,10 +1,24 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + package i3ipc import ( - "encoding/json" - "fmt" "net" - "strings" ) const ( @@ -30,34 +44,49 @@ const ( SEND_TICK SYNC GET_BINDING_STATE - GET_INPUTS - GET_SEATS + + GET_INPUTS = 100 + GET_SEATS = 101 ) -// module struct +// This is the primary struct to work with the i3ipc module. type I3ipc struct { socket net.Conn - SocketFile string + SocketFile string // filename of the i3 IPC socket + Events *Event // store subscribed events, see i3ipc.Subscribe() } -// i3-ipc structs +// A rectangle struct, used at various places for geometry etc. type Rect struct { - X int `json:"x"` - Y int `json:"y"` + X int `json:"x"` // X coordinate + Y int `json:"y"` // Y coordinate Width int `json:"width"` Height int `json:"height"` } +// Stores responses retrieved via ipc type Response struct { Success bool `json:"success"` ParseError bool `json:"parse_error"` Error string `json:"error"` } +// Stores the user config for the WM type Config struct { Config string `json:"config"` } +// Stores the binding state +type State struct { + Name string `json:"name"` +} + +// Create a new i3ipc.I3ipc object. Filename argument is optional and +// may denote a filename or the name of an environment variable. +// +// By default and if nothing is specified we look for the environment +// variable SWAYSOCK and use the file it points to as unix domain +// socket to communicate with sway (and possible i3). func NewI3ipc(file ...string) *I3ipc { ipc := &I3ipc{} @@ -70,7 +99,8 @@ func NewI3ipc(file ...string) *I3ipc { return ipc } -func (ipc *I3ipc) get(command uint32) ([]byte, error) { +// internal convenience wrapper +func (ipc *I3ipc) get(command uint32) (*RawResponse, error) { err := ipc.sendHeader(command, 0) if err != nil { return nil, err @@ -83,108 +113,3 @@ func (ipc *I3ipc) get(command uint32) ([]byte, error) { return payload, nil } - -func (ipc *I3ipc) RunGlobalCommand(command ...string) ([]Response, error) { - return ipc.RunCommand(0, command...) -} - -func (ipc *I3ipc) RunCommand(id int, command ...string) ([]Response, error) { - if len(command) == 0 { - return nil, fmt.Errorf("empty command arg") - } - - commands := strings.Join(command, ",") - - if id > 0 { - commands = fmt.Sprintf("[con_id=%d] %s", id, commands) - } - - err := ipc.sendHeader(RUN_COMMAND, uint32(len(commands))) - if err != nil { - return nil, fmt.Errorf("failed to send run_command to IPC %w", err) - } - - err = ipc.sendPayload([]byte(commands)) - if err != nil { - return nil, fmt.Errorf("failed to send switch focus command: %w", err) - } - - payload, err := ipc.readResponse() - if err != nil { - return nil, err - } - - responses := []Response{} - - if err := json.Unmarshal(payload, &responses); err != nil { - return nil, fmt.Errorf("failed to unmarshal json response: %w", err) - } - - if len(responses) == 0 { - return nil, fmt.Errorf("got zero IPC response") - } - - for _, response := range responses { - if !response.Success { - return responses, fmt.Errorf("one or more commands failed") - } - } - - return responses, nil -} - -func (ipc *I3ipc) GetWorkspaces() ([]*Node, error) { - payload, err := ipc.get(GET_WORKSPACES) - if err != nil { - return nil, err - } - - nodes := []*Node{} - if err := json.Unmarshal(payload, &nodes); err != nil { - return nil, fmt.Errorf("failed to unmarshal json: %w", err) - } - - return nodes, nil -} - -func (ipc *I3ipc) GetMarks() ([]string, error) { - payload, err := ipc.get(GET_MARKS) - if err != nil { - return nil, err - } - - marks := []string{} - if err := json.Unmarshal(payload, &marks); err != nil { - return nil, fmt.Errorf("failed to unmarshal json: %w", err) - } - - return marks, nil -} - -func (ipc *I3ipc) GetBindingModes() ([]string, error) { - payload, err := ipc.get(GET_BINDING_MODES) - if err != nil { - return nil, err - } - - modes := []string{} - if err := json.Unmarshal(payload, &modes); err != nil { - return nil, fmt.Errorf("failed to unmarshal json: %w", err) - } - - return modes, nil -} - -func (ipc *I3ipc) GetConfig() (string, error) { - payload, err := ipc.get(GET_CONFIG) - if err != nil { - return "", err - } - - config := &Config{} - if err := json.Unmarshal(payload, &config); err != nil { - return "", fmt.Errorf("failed to unmarshal json: %w", err) - } - - return config.Config, nil -} diff --git a/input.go b/input.go new file mode 100644 index 0000000..537b758 --- /dev/null +++ b/input.go @@ -0,0 +1,82 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package i3ipc + +import ( + "encoding/json" + "fmt" +) + +// An input (keyboard, mouse, whatever) +type Input struct { + Identifier string `json:"identifier"` + Name string `json:"name"` + Vendor int `json:"vendor"` + Product int `json:"product"` + Type string `json:"type"` + XkbActiveLayoutName string `json:"xkb_active_layout_name"` + XkbLayoutNames []string `json:"xkb_layout_names"` + XkbActiveLayoutIndex int `json:"xkb_active_layout_index"` + ScrollFactor float32 `json:"scroll_factor"` + Libinput *LibInput `json:"libinput"` +} + +// Holds the data associated with libinput +type LibInput struct { + SendEvents string `json:"send_events"` + Tap string `json:"tap"` + TapButtonMap string `json:"tap_button_map"` + TapDrag string `json:"tap_drag"` + TapDragLock string `json:"tap_drag_lock"` + AccelSpeed float32 `json:"accel_speed"` + AccelProfile string `json:"accel_profile"` + NaturalScroll string `json:"natural_scroll"` + LeftHanded string `json:"left_handed"` + ClickMethod string `json:"click_method"` + ClickButtonMap string `json:"click_button_map"` + MiddleEmulation string `json:"middle_emulation"` + ScrollMethod string `json:"scroll_method"` + ScrollButton int `json:"scroll_button"` + ScrollButtonLock string `json:"scroll_button_lock"` + Dwt string `json:"dwt"` + Dwtp string `json:"dwtp"` + CalibrationMatrix []float32 `json:"calibration_matrix"` +} + +// A key binding +type Binding struct { + Command string `json:"command"` + EventStateMask []string `json:"event_state_mask"` + InputCode int `json:"input_code"` + Symbol string `json:"symbol"` + InputType string `json:"input_type"` +} + +// Get a list of all currently supported inputs +func (ipc *I3ipc) GetInputs() ([]*Input, error) { + payload, err := ipc.get(GET_INPUTS) + if err != nil { + return nil, err + } + + inputs := []*Input{} + if err := json.Unmarshal(payload.Payload, &inputs); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return inputs, nil +} diff --git a/net.go b/net.go index 89f6773..8f45eff 100644 --- a/net.go +++ b/net.go @@ -1,12 +1,31 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + package i3ipc import ( "encoding/binary" + "errors" "fmt" "net" "os" ) +// Connect to unix domain ipc socket. func (ipc *I3ipc) Connect() error { if !fileExists(ipc.SocketFile) { ipc.SocketFile = os.Getenv(ipc.SocketFile) @@ -25,6 +44,7 @@ func (ipc *I3ipc) Connect() error { return nil } +// Close the socket. func (ipc *I3ipc) Close() { ipc.socket.Close() } @@ -55,7 +75,12 @@ func (ipc *I3ipc) sendPayload(payload []byte) error { return nil } -func (ipc *I3ipc) readResponse() ([]byte, error) { +type RawResponse struct { + PayloadType int + Payload []byte +} + +func (ipc *I3ipc) readResponse() (*RawResponse, error) { // read header buf := make([]byte, IPC_HEADER_SIZE) @@ -65,15 +90,16 @@ func (ipc *I3ipc) readResponse() ([]byte, error) { } if string(buf[:6]) != IPC_MAGIC { - return nil, fmt.Errorf("got invalid response from IPC socket") + return nil, errors.New("got invalid response from IPC socket") } payloadLen := binary.LittleEndian.Uint32(buf[6:10]) - if payloadLen == 0 { - return nil, fmt.Errorf("got empty payload response from IPC socket") + return nil, errors.New("got empty payload response from IPC socket") } + payloadType := binary.LittleEndian.Uint32(buf[10:]) + // read payload payload := make([]byte, payloadLen) @@ -82,5 +108,5 @@ func (ipc *I3ipc) readResponse() ([]byte, error) { return nil, fmt.Errorf("failed to read payload from IPC socket: %s", err) } - return payload, nil + return &RawResponse{PayloadType: int(payloadType), Payload: payload}, nil } diff --git a/node.go b/node.go index 5bfbaf8..d6c901d 100644 --- a/node.go +++ b/node.go @@ -1,3 +1,19 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ package i3ipc import ( @@ -5,6 +21,8 @@ import ( "fmt" ) +// A node can be an output, a workspace, a container or a container +// containing a window. type Node struct { Id int `json:"id"` Type string `json:"type"` // output, workspace or container @@ -32,6 +50,14 @@ type Node struct { var __focused *Node var __currentworkspace string +// Get the whole information tree, 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 *I3ipc) GetTree() (*Node, error) { err := ipc.sendHeader(GET_TREE, 0) if err != nil { @@ -44,13 +70,15 @@ func (ipc *I3ipc) GetTree() (*Node, error) { } node := &Node{} - if err := json.Unmarshal(payload, &node); err != nil { + if err := json.Unmarshal(payload.Payload, &node); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } return node, nil } +// Usually called on the root node, returns the container which has +// currently the focus. func (node *Node) FindFocused() *Node { searchFocused(node.Nodes) if __focused == nil { @@ -60,6 +88,7 @@ func (node *Node) FindFocused() *Node { return __focused } +// internal recursive focus node searcher func searchFocused(nodes []*Node) { for _, node := range nodes { if node.Focused { @@ -75,11 +104,14 @@ func searchFocused(nodes []*Node) { } } +// Usually called on the root node, returns the current active +// workspace name. func (node *Node) FindCurrentWorkspace() string { searchCurrentWorkspace(node.Nodes) return __currentworkspace } +// internal recursive workspace node searcher func searchCurrentWorkspace(nodes []*Node) { for _, node := range nodes { if node.Current_workspace != "" { diff --git a/output.go b/output.go index 1c28a60..47866ae 100644 --- a/output.go +++ b/output.go @@ -1,3 +1,19 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ package i3ipc import ( @@ -5,12 +21,14 @@ import ( "fmt" ) +// Store an output mode. type Mode struct { Width int `json:"width"` Height int `json:"height"` Refresh int `json:"refresh"` } +// An output object (i.e. a physical monitor) type Output struct { Name string `json:"name"` Make string `json:"make"` @@ -24,6 +42,7 @@ type Output struct { CurrentMode *Mode `json:"current_mode"` } +// Get a list of currently available and usable outputs. func (ipc *I3ipc) GetOutputs() ([]*Output, error) { err := ipc.sendHeader(GET_OUTPUTS, 0) if err != nil { @@ -36,7 +55,7 @@ func (ipc *I3ipc) GetOutputs() ([]*Output, error) { } workspaces := []*Output{} - if err := json.Unmarshal(payload, &workspaces); err != nil { + if err := json.Unmarshal(payload.Payload, &workspaces); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } diff --git a/seat.go b/seat.go new file mode 100644 index 0000000..309ce44 --- /dev/null +++ b/seat.go @@ -0,0 +1,45 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package i3ipc + +import ( + "encoding/json" + "fmt" +) + +// Information about a seat containing input devices +type Seat struct { + Name string `json:"name"` + Capabilities int `json:"capabilities"` + Focus int `json:"focus"` + Devices []*Input `json:"devices"` +} + +// Get input seats +func (ipc *I3ipc) GetSeats() ([]*Seat, error) { + payload, err := ipc.get(GET_SEATS) + if err != nil { + return nil, err + } + + seats := []*Seat{} + if err := json.Unmarshal(payload.Payload, &seats); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return seats, nil +} diff --git a/simpletons.go b/simpletons.go new file mode 100644 index 0000000..a001ed0 --- /dev/null +++ b/simpletons.go @@ -0,0 +1,90 @@ +package i3ipc + +import ( + "encoding/json" + "fmt" +) + +func (ipc *I3ipc) GetWorkspaces() ([]*Node, error) { + payload, err := ipc.get(GET_WORKSPACES) + if err != nil { + return nil, err + } + + nodes := []*Node{} + if err := json.Unmarshal(payload.Payload, &nodes); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return nodes, nil +} + +func (ipc *I3ipc) GetMarks() ([]string, error) { + payload, err := ipc.get(GET_MARKS) + if err != nil { + return nil, err + } + + marks := []string{} + if err := json.Unmarshal(payload.Payload, &marks); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return marks, nil +} + +func (ipc *I3ipc) GetBindingModes() ([]string, error) { + payload, err := ipc.get(GET_BINDING_MODES) + if err != nil { + return nil, err + } + + modes := []string{} + if err := json.Unmarshal(payload.Payload, &modes); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return modes, nil +} + +func (ipc *I3ipc) GetBindingState() (*State, error) { + payload, err := ipc.get(GET_BINDING_STATE) + if err != nil { + return nil, err + } + + state := &State{} + if err := json.Unmarshal(payload.Payload, &state); err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return state, nil +} + +func (ipc *I3ipc) GetConfig() (string, error) { + payload, err := ipc.get(GET_CONFIG) + if err != nil { + return "", err + } + + config := &Config{} + if err := json.Unmarshal(payload.Payload, &config); err != nil { + return "", fmt.Errorf("failed to unmarshal json: %w", err) + } + + return config.Config, nil +} + +func (ipc *I3ipc) SendTick(payload string) error { + err := ipc.sendHeader(SEND_TICK, uint32(len(payload))) + if err != nil { + return err + } + + err = ipc.sendPayload([]byte(payload)) + if err != nil { + return err + } + + return nil +} diff --git a/version.go b/version.go index 625c69d..e221aa1 100644 --- a/version.go +++ b/version.go @@ -1,3 +1,19 @@ +/* +Copyright © 2025 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ package i3ipc import ( @@ -5,6 +21,7 @@ import ( "fmt" ) +// A version struct holding the sway wm version type Version struct { HumanReadable string `json:"human_readable"` Major int `json:"major"` @@ -12,6 +29,7 @@ type Version struct { Patch int `json:"patch"` } +// Get the sway software version func (ipc *I3ipc) GetVersion() (*Version, error) { payload, err := ipc.get(GET_VERSION) if err != nil { @@ -19,7 +37,7 @@ func (ipc *I3ipc) GetVersion() (*Version, error) { } version := &Version{} - if err := json.Unmarshal(payload, &version); err != nil { + if err := json.Unmarshal(payload.Payload, &version); err != nil { return nil, fmt.Errorf("failed to unmarshal json: %w", err) }