Compare commits

...

7 Commits

Author SHA1 Message Date
d617a7dde7 GetSeats implemented 2024-02-14 16:51:23 +01:00
2dbdc93ac1 removed useless contexts 2024-02-14 16:48:50 +01:00
103b4c37a8 refactored communication into sendMessage function 2024-02-14 16:36:12 +01:00
305cc6ce13 fixed context usage in Subscribe 2024-02-14 15:26:17 +01:00
f2690ddf0b GetOutputs implemented 2024-02-14 15:26:04 +01:00
8bb43a4263 rewrite 2024-02-14 15:12:39 +01:00
8735d4dc1c added gitignore 2024-02-14 15:07:18 +01:00
27 changed files with 846 additions and 346 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*_test.go

92
client.go Normal file
View File

@ -0,0 +1,92 @@
package sway
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"sync"
)
type Client struct {
sync.Mutex
socket string
conn net.Conn
}
func GetDefaultClient() (*Client, error) {
socket, ok := os.LookupEnv("SWAYSOCK")
if !ok {
return nil, errors.New("could not find sway socket. is $SWAYSOCK set properly?")
}
return GetClientBySocket(socket)
}
func GetClientBySocket(socket string) (*Client, error) {
conn, err := net.Dial("unix", socket)
if err != nil {
return nil, err
}
return &Client{
socket: socket,
conn: conn,
}, nil
}
func (c *Client) Close() error {
c.Lock()
defer c.Unlock()
return c.conn.Close()
}
func sendMessage[T any](client *Client, messageType uint32, payload string) (T, error) {
client.Lock()
defer client.Unlock()
if _, err := fmt.Fprint(client.conn, "i3-ipc"); err != nil {
return *new(T), err
}
if err := binary.Write(client.conn, binary.LittleEndian, uint32(len(payload))); err != nil {
return *new(T), err
}
if err := binary.Write(client.conn, binary.LittleEndian, messageType); err != nil {
return *new(T), err
}
if _, err := fmt.Fprint(client.conn, payload); err != nil {
return *new(T), err
}
if _, err := client.conn.Read(make([]byte, 6)); err != nil {
return *new(T), err
}
var length uint32
if err := binary.Read(client.conn, binary.LittleEndian, &length); err != nil {
return *new(T), err
}
var responseType uint32
if err := binary.Read(client.conn, binary.LittleEndian, &responseType); err != nil {
return *new(T), err
}
// io.Copy(os.Stdout, client.conn)
// return *new(T), nil
result := new(T)
if err := json.NewDecoder(io.LimitReader(client.conn, int64(length))).Decode(result); err != nil {
return *new(T), err
}
return *result, nil
}

173
event.go
View File

@ -1,16 +1,173 @@
package sway
import (
"encoding/json"
import "encoding/json"
"git.milar.in/milarin/adverr"
type EventType uint32
const (
// i3 events
EventTypeWorkspace EventType = 0x80000000 + iota
_ EventType = 0x80000000 + iota // output event not available in sway
EventTypeMode EventType = 0x80000000 + iota
EventTypeWindow EventType = 0x80000000 + iota
_ EventType = 0x80000000 + iota // TODO implement EventTypeBarConfigUpdate
EventTypeBinding EventType = 0x80000000 + iota
EventTypeShutdown EventType = 0x80000000 + iota
EventTypeTick EventType = 0x80000000 + iota
// sway-only events
EventTypeBarStateUpdate EventType = 0x80000014
EventTypeInput EventType = 0x80000015
)
type Event struct {
Change string `json:"change"`
Container Node `json:"container"`
var eventStringRepresentations = map[EventType]string{
EventTypeWorkspace: "workspace",
EventTypeMode: "mode",
EventTypeWindow: "window",
//EventTypeBarConfigUpdate: "barconfig_update",
EventTypeBinding: "binding",
EventTypeShutdown: "shutdown",
EventTypeTick: "tick",
EventTypeBarStateUpdate: "bar_state_update",
EventTypeInput: "input",
}
func (e Event) String() string {
return string(adverr.Must(json.MarshalIndent(e, "", "\t")))
func (et EventType) String() string {
return eventStringRepresentations[et]
}
type Event interface {
Type() EventType
}
type WorkspaceEvent struct {
Change WorkspaceEventChange `json:"change"`
Current *Workspace `json:"current"`
Old *Workspace `json:"old"`
}
var _ Event = &WorkspaceEvent{}
func (e WorkspaceEvent) Type() EventType {
return EventTypeWorkspace
}
func (e WorkspaceEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type ModeEvent struct {
Change string `json:"change"`
PangoMarkup bool `json:"pango_markup"`
}
var _ Event = &ModeEvent{}
func (e ModeEvent) Type() EventType {
return EventTypeMode
}
func (e ModeEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type WindowEvent struct {
Change WindowEventChange `json:"change"`
Container *Node `json:"container"`
}
var _ Event = &WindowEvent{}
func (e WindowEvent) Type() EventType {
return EventTypeWindow
}
func (e WindowEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type BindingEvent struct {
Change string `json:"change"`
Command string `json:"command"`
EventStateMask []string `json:"event_state_mask"`
InputCode int `json:"input_code"`
Symbol string `json:"symbol"`
InputType InputType `json:"input_type"`
}
var _ Event = &BindingEvent{}
func (e BindingEvent) Type() EventType {
return EventTypeBinding
}
func (e BindingEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type ShutdownEvent struct {
Change string `json:"change"`
}
var _ Event = &ShutdownEvent{}
func (e ShutdownEvent) Type() EventType {
return EventTypeShutdown
}
func (e ShutdownEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type TickEvent struct {
First bool `json:"first"`
Payload string `json:"payload"`
}
var _ Event = &TickEvent{}
func (e TickEvent) Type() EventType {
return EventTypeTick
}
func (e TickEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type BarStateUpdateEvent struct {
ID string `json:"id"`
VisibleByModifier bool `json:"visible_by_modifier"`
}
var _ Event = &BarStateUpdateEvent{}
func (e BarStateUpdateEvent) Type() EventType {
return EventTypeBarStateUpdate
}
func (e BarStateUpdateEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}
type InputEvent struct {
Change InputEventChange `json:"change"`
//Input Input `json:"input"` // TODO implement
}
var _ Event = &InputEvent{}
func (e InputEvent) Type() EventType {
return EventTypeInput
}
func (e InputEvent) String() string {
data, _ := json.MarshalIndent(e, "", "\t")
return string(data)
}

View File

@ -1,9 +0,0 @@
package sway
type EventType string
const (
EventTypeWindow EventType = "window"
EventTypeWorkspace EventType = "workspace"
// TODO
)

31
find.go
View File

@ -1,31 +0,0 @@
package sway
func (n Node) FindAll(condition func(n Node) bool) []Node {
nodes := make([]Node, 0, 10)
if condition(n) {
nodes = append(nodes, n)
}
for _, child := range n.Nodes {
nodes = append(nodes, child.FindAll(condition)...)
}
for _, child := range n.FloatingNodes {
nodes = append(nodes, child.FindAll(condition)...)
}
return nodes
}
func IsOutput(n Node) bool {
return n.Type == NodeTypeOutput
}
func IsWorkspace(n Node) bool {
return n.Type == NodeTypeWorkspace
}
func IsContainer(n Node) bool {
return n.Type == NodeTypeCon
}

View File

@ -1,18 +0,0 @@
package sway
import (
"context"
"fmt"
"testing"
)
func TestFind(t *testing.T) {
root, err := GetTree(context.Background())
if err != nil {
t.Fatal(err)
}
for _, node := range root.FindAll(IsWorkspace) {
fmt.Println(node.Type, node.Name)
}
}

9
get_bar_config.go Normal file
View File

@ -0,0 +1,9 @@
package sway
func (c *Client) GetBarIDs() ([]string, error) {
return sendMessage[[]string](c, 6, "")
}
func (c *Client) GetBarConfig(barID string) (*BarConfig, error) {
return sendMessage[*BarConfig](c, 6, barID)
}

5
get_binding_modes.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetBindingModes() ([]string, error) {
return sendMessage[[]string](c, 8, "")
}

9
get_config.go Normal file
View File

@ -0,0 +1,9 @@
package sway
func (c *Client) GetConfig() (string, error) {
cfg, err := sendMessage[config](c, 9, "")
if err != nil {
return "", err
}
return cfg.Config, nil
}

View File

@ -0,0 +1,9 @@
package sway
func (c *Client) GetCurrentBindingMode() (string, error) {
mode, err := sendMessage[name](c, 12, "")
if err != nil {
return "", err
}
return mode.Name, nil
}

5
get_inputs.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetInputs() ([]InputDevice, error) {
return sendMessage[[]InputDevice](c, 100, "")
}

5
get_marks.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetMarks() ([]string, error) {
return sendMessage[[]string](c, 5, "")
}

5
get_outputs.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetOutputs() ([]Output, error) {
return sendMessage[[]Output](c, 3, "")
}

5
get_seats.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetSeats() ([]Seat, error) {
return sendMessage[[]Seat](c, 101, "")
}

View File

@ -1,23 +1,5 @@
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
func (c *Client) GetTree() (*Node, error) {
return sendMessage[*Node](c, 4, "")
}

View File

@ -1,16 +0,0 @@
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)
}

5
get_version.go Normal file
View File

@ -0,0 +1,5 @@
package sway
func (c *Client) GetVersion() (*Version, error) {
return sendMessage[*Version](c, 7, "")
}

View File

@ -1,23 +1,5 @@
package sway
import (
"context"
"encoding/json"
"os/exec"
)
func GetWorkspaces(ctx context.Context) ([]Node, error) {
cmd := exec.CommandContext(ctx, "swaymsg", "-t", "get_workspaces")
data, err := cmd.Output()
if err != nil {
return nil, err
}
var workspaces []Node
if err := json.Unmarshal(data, &workspaces); err != nil {
return nil, err
}
return workspaces, nil
func (c *Client) GetWorkspaces() ([]Workspace, error) {
return sendMessage[[]Workspace](c, 1, "")
}

View File

@ -1,18 +0,0 @@
package sway
import (
"context"
"fmt"
"testing"
)
func TestGetWorkspaces(t *testing.T) {
workspaces, err := GetWorkspaces(context.Background())
if err != nil {
t.Fatal(err)
}
for _, workspace := range workspaces {
fmt.Println(workspace.Type, workspace.Output, workspace.Name)
}
}

4
go.mod
View File

@ -2,4 +2,6 @@ module git.milar.in/milarin/sway
go 1.21.1
require git.milar.in/milarin/adverr v1.1.1
require git.milar.in/milarin/slices v0.0.8
require git.milar.in/milarin/gmath v0.0.3 // indirect

6
go.sum
View File

@ -1,2 +1,4 @@
git.milar.in/milarin/adverr v1.1.1 h1:ENtBcqT7CncLsVfaLC3KzX8QSSGiSpsC7I7wDqladu8=
git.milar.in/milarin/adverr v1.1.1/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E=
git.milar.in/milarin/gmath v0.0.3 h1:ii6rKNItS55O/wtIFhD1cTN2BMwDZjTBmiOocKURvxM=
git.milar.in/milarin/gmath v0.0.3/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE=
git.milar.in/milarin/slices v0.0.8 h1:qN9TE3tkArdTixMKSnwvNPcApwAjxpLVwA5a9k1rm2s=
git.milar.in/milarin/slices v0.0.8/go.mod h1:qMhdtMnfWswc1rHpwgNw33lB84aNEkdBn5BDiYA+G3k=

166
node.go
View File

@ -1,166 +0,0 @@
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"`
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"`
// Xorg compatibility
XorgWindowID *int64 `json:"window"`
XorgProperties *XorgProperties `json:"window_properties"`
// 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"`
// NodeTypeWorkspace only
WorkspaceNumber int `json:"num"`
Output string `json:"output"`
Representation string `json:"representation"`
}
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"
)
type XorgProperties struct {
Title string `json:"title"`
Instance string `json:"instance"`
Class string `json:"class"`
Role string `json:"window_role"`
Transient NodeID `json:"transient_for"`
}

View File

@ -1,16 +1,15 @@
package sway
import (
"context"
"os/exec"
"git.milar.in/milarin/slices"
)
func RunCommand(ctx context.Context, command string) error {
cmd := exec.CommandContext(ctx, "swaymsg", command)
if err := cmd.Start(); err != nil {
return err
func (c *Client) RunCommand(cmd string) ([]error, error) {
results, err := sendMessage[[]commandResult](c, 0, cmd)
if err != nil {
return nil, err
}
return cmd.Wait()
errors := slices.Map(slices.Filter(results, commandResult.HasError), commandResult.GetError)
return errors, nil
}

14
send_tick.go Normal file
View File

@ -0,0 +1,14 @@
package sway
func (c *Client) SendTick(payload string) error {
result, err := sendMessage[commandResult](c, 10, payload)
if err != nil {
return err
}
if result.HasError() {
return result.GetError()
}
return nil
}

View File

@ -2,37 +2,132 @@ package sway
import (
"context"
"encoding/binary"
"encoding/json"
"os/exec"
"fmt"
"io"
"net"
"git.milar.in/milarin/slices"
)
func Subscribe(ctx context.Context, events ...EventType) (<-chan Event, error) {
data, err := json.Marshal(events)
func (c *Client) Subscribe(ctx context.Context, events ...EventType) (<-chan Event, error) {
conn, err := net.Dial("unix", c.socket)
if err != nil {
return nil, err
}
cmd := exec.CommandContext(ctx, "swaymsg", "-t", "subscribe", "-m", string(data))
if _, err := fmt.Fprint(conn, "i3-ipc"); err != nil {
return nil, err
}
stdout, err := cmd.StdoutPipe()
payload, err := json.Marshal(slices.Map(events, EventType.String))
if err != nil {
return nil, err
}
if err := binary.Write(conn, binary.LittleEndian, uint32(len(payload))); err != nil {
return nil, err
}
if err := binary.Write(conn, binary.LittleEndian, uint32(2)); err != nil {
return nil, err
}
if _, err := conn.Write(payload); err != nil {
return nil, err
}
if _, err := conn.Read(make([]byte, 6)); err != nil {
return nil, err
}
var length uint32
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
return nil, err
}
var messageType uint32
if err := binary.Read(conn, binary.LittleEndian, &messageType); err != nil {
return nil, err
}
result := commandResult{}
if err := json.NewDecoder(io.LimitReader(conn, int64(length))).Decode(&result); err != nil {
return nil, err
}
if result.HasError() {
return nil, result.GetError()
}
return parseEvents(ctx, conn)
}
func parseEvents(ctx context.Context, conn net.Conn) (<-chan Event, error) {
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) {
defer conn.Close()
<-ctx.Done()
}()
go func() {
defer close(out)
for ctx.Err() == nil {
if _, err := conn.Read(make([]byte, 6)); err != nil {
return
}
var length uint32
if err := binary.Read(conn, binary.LittleEndian, &length); err != nil {
return
}
var messageType EventType
if err := binary.Read(conn, binary.LittleEndian, &messageType); err != nil {
return
}
event, err := parseEvent(conn, length, messageType)
if err != nil {
return
}
out <- event
}
}()
if err := cmd.Start(); err != nil {
return out, nil
}
func parseEvent(conn net.Conn, length uint32, messageType EventType) (Event, error) {
var event Event
switch messageType {
case EventTypeWorkspace:
event = &WorkspaceEvent{}
case EventTypeMode:
event = &ModeEvent{}
case EventTypeWindow:
event = &WindowEvent{}
case EventTypeBinding:
event = &BindingEvent{}
case EventTypeShutdown:
event = &ShutdownEvent{}
case EventTypeTick:
event = &TickEvent{}
case EventTypeBarStateUpdate:
event = &BarStateUpdateEvent{}
case EventTypeInput:
event = &InputEvent{}
default:
return nil, fmt.Errorf("invalid event type: %x", messageType)
}
if err := json.NewDecoder(io.LimitReader(conn, int64(length))).Decode(event); err != nil {
return nil, err
}
return out, nil
return event, nil
}

View File

@ -1,18 +0,0 @@
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)
}
}

393
types.go Normal file
View File

@ -0,0 +1,393 @@
package sway
import (
"encoding/json"
"errors"
)
type NodeID int64
type Rectangle struct {
X int `json:"x"`
Y int `json:"y"`
Width int `json:"width"`
Height int `json:"height"`
}
func (r Rectangle) String() string {
data, _ := json.MarshalIndent(r, "", "\t")
return string(data)
}
type Workspace struct {
Num int `json:"num"`
Name string `json:"name"`
Visible bool `json:"visible"`
Focused bool `json:"focused"`
Urgent bool `json:"urgent"`
Rect Rectangle `json:"rect"`
Output string `json:"output"`
}
func (w Workspace) String() string {
data, _ := json.MarshalIndent(w, "", "\t")
return string(data)
}
type Output struct {
Name string `json:"name"`
Make string `json:"make"`
Model string `json:"model"`
Serial string `json:"serial"`
Active bool `json:"active"`
DPMS bool `json:"dpms"`
Power bool `json:"power"`
Scale float64 `json:"scale"`
SubPixelHinting SubPixelHinting `json:"subpixel_hinting"`
Transform Transform `json:"transform"`
CurrentWorkspace string `json:"current_workspace"`
Modes []Mode `json:"modes"`
CurrentMode Mode `json:"current_mode"`
Rect Rectangle `json:"rect"`
}
func (o Output) String() string {
data, _ := json.MarshalIndent(o, "", "\t")
return string(data)
}
type BarConfig struct {
ID string `json:"id"`
Mode BarMode `json:"mode"`
Position BarPosition `json:"position"`
StatusCommand string `json:"status_command"`
Font string `json:"font"`
WorkspaceButtons bool `json:"workspace_buttons"`
WorkspaceMinWidth int `json:"workspace_min_width"`
BindingModeIndicator bool `json:"binding_mode_indicator"`
Colors BarColors `json:"colors"`
Gaps BarGaps `json:"gaps"`
BarHeight int `json:"bar_height"`
StatusPadding int `json:"status_padding"`
StatusEdgePadding int `json:"status_edge_padding"`
}
func (bc BarConfig) String() string {
data, _ := json.MarshalIndent(bc, "", "\t")
return string(data)
}
type WindowProperties struct {
Title string `json:"title"`
Instance string `json:"instance"`
Class string `json:"class"`
Role string `json:"window_role"`
Transient NodeID `json:"transient_for"`
}
func (w WindowProperties) String() string {
data, _ := json.MarshalIndent(w, "", "\t")
return string(data)
}
type Node struct {
ID NodeID `json:"id"`
Name string `json:"name"`
Type NodeType `json:"type"`
Border BorderStyle `json:"border"`
CurrentBorderWidth int `json:"current_border_width"`
Layout Layout `json:"layout"`
Orientation Orientation `json:"orientation"`
Percent float64 `json:"percent"`
Rect Rectangle `json:"rect"`
WindowRect Rectangle `json:"window_rect"`
DecoRect Rectangle `json:"deco_rect"`
Geometry Rectangle `json:"geometry"`
Urgent bool `json:"urgent"`
Sticky bool `json:"sticky"`
Marks []string `json:"marks"`
Focused bool `json:"focused"`
Focus []NodeID `json:"focus"`
Nodes []Node `json:"nodes"`
FloatingNodes []Node `json:"floating_nodes"`
Representation string `json:"representation"`
FullscreenMode FullscreenMode `json:"fullscreen_mode"`
AppID string `json:"app_id"`
PID int `json:"pid"`
Visible bool `json:"visible"`
Shell string `json:"shell"`
InhibitIdle bool `json:"inhibit_idle"`
Window int `json:"window"`
WindowProperties WindowProperties `json:"window_properties"`
// The following fields are undocumented. Use with caution!
// They are included in sway as well as in i3 for years though
Num int `json:"num"` // workspace number
Output string `json:"output"` // output port name
}
func (n Node) String() string {
data, _ := json.MarshalIndent(n, "", "\t")
return string(data)
}
type NodeType = string
const (
NodeTypeRoot NodeType = "root"
NodeTypeOutput NodeType = "output"
NodeTypeWorkspace NodeType = "workspace"
NodeTypeContainer NodeType = "con"
NodeTypeFloatingContainer NodeType = "floating_con"
)
type BorderStyle = string
const (
BorderStyleNormal BorderStyle = "normal"
BorderStyleNone BorderStyle = "none"
BorderStylePixel BorderStyle = "pixel"
BorderStyleCSD BorderStyle = "csd"
)
type Layout = string
const (
LayoutSplitH Layout = "splith"
LayoutSplitV Layout = "splitv"
LayoutStacked Layout = "stacked"
LayoutTabbed Layout = "tabbed"
LayoutOutput Layout = "output"
)
type Orientation = string
const (
OrientationVertical Orientation = "vertical"
OrientationHorizontal Orientation = "horizontal"
OrientationNone Orientation = "none"
)
type FullscreenMode = uint8
const (
FullscreenModeNone FullscreenMode = iota
FullscreenModeWorkspace FullscreenMode = iota
FullscreenModeGlobal FullscreenMode = iota
)
type WorkspaceEventChange = string
const (
WorkspaceEventChangeInit WorkspaceEventChange = "init"
WorkspaceEventChangeEmpty WorkspaceEventChange = "empty"
WorkspaceEventChangeFocus WorkspaceEventChange = "focus"
WorkspaceEventChangeMove WorkspaceEventChange = "move"
WorkspaceEventChangeRename WorkspaceEventChange = "rename"
WorkspaceEventChangeUrgent WorkspaceEventChange = "urgent"
WorkspaceEventChangeReload WorkspaceEventChange = "reload"
)
type WindowEventChange = string
const (
WindowEventChangeNew WindowEventChange = "new"
WindowEventChangeClose WindowEventChange = "close"
WindowEventChangeFocus WindowEventChange = "focus"
WindowEventChangeTitle WindowEventChange = "title"
WindowEventChangeFullscreenMode WindowEventChange = "fullscreen_mode"
WindowEventChangeMove WindowEventChange = "move"
WindowEventChangeFloating WindowEventChange = "floating"
WindowEventChangeUrgent WindowEventChange = "urgent"
WindowEventChangeMark WindowEventChange = "mark"
)
type InputEventChange = string
const (
InputEventChangeAdded InputEventChange = "added"
InputEventChangeRemoved InputEventChange = "removed"
InputEventChangeXKBKeymap InputEventChange = "xkb_kemap"
InputEventChangeXKBLayout InputEventChange = "xkb_layout"
InputEventChangeLibInputConfig InputEventChange = "libinput_config"
)
type InputType = string
const (
InputTypeKeyboard InputType = "keyboard"
InputTypeMouse InputType = "mouse"
)
type SubPixelHinting = string
const (
SubPixelHintingRGB SubPixelHinting = "rgb"
SubPixelHintingBGR SubPixelHinting = "bgr"
SubPixelHintingVRGB SubPixelHinting = "vrgb"
SubPixelHintingVBGR SubPixelHinting = "vbgr"
SubPixelHintingNone SubPixelHinting = "none"
)
type Transform = string
const (
Transform90 Transform = "90"
Transform180 Transform = "180"
Transform270 Transform = "270"
TransformFlipped90 Transform = "flipped-90"
TransformFlipped180 Transform = "flipped-180"
TransformFlipped270 Transform = "flipped-270"
)
type BarMode = string
const (
BarModeDock BarMode = "dock"
BarModeHide BarMode = "hide"
barModeInvisible BarMode = "invisible"
)
type BarPosition = string
const (
BarPositionTop BarPosition = "top"
BarPositionBottom BarPosition = "bottom"
)
type InputDeviceType = string
const (
InputDeviceTypeKeyboard InputDeviceType = "keyboard"
InputDeviceTypePointer InputDeviceType = "pointer"
InputDeviceTypeTouch InputDeviceType = "touch"
InputDeviceTypeTabletTool InputDeviceType = "tablet_tool"
InputDeviceTypeTabletPad InputDeviceType = "tablet_pad"
InputDeviceTypeSwitch InputDeviceType = "switch"
)
type commandResult struct {
Success bool `json:"success"`
ParseError bool `json:"parse_error"`
Error string `json:"error"`
}
func (r commandResult) String() string {
data, _ := json.MarshalIndent(r, "", "\t")
return string(data)
}
func (r commandResult) GetError() error {
return errors.New(r.Error)
}
func (r commandResult) HasError() bool {
return !r.Success
}
type Mode struct {
Width int `json:"width"`
Height int `json:"height"`
Refresh int `json:"refresh"`
}
type BarColors struct {
Background string `json:"background"`
Statusline string `json:"statusline"`
Separator string `json:"separator"`
FocusedBackground string `json:"focused_background"`
FocusedStatusline string `json:"focused_statusline"`
FocusedSeparator string `json:"focused_separator"`
FocusedWorkspaceText string `json:"focused_workspace_text"`
FocusedWorkspaceBackground string `json:"focused_workspace_bg"`
FocusedWorkspaceBorder string `json:"focused_workspace_border"`
ActiveWorkspaceText string `json:"active_workspace_text"`
ActiveWorkspaceBackground string `json:"active_workspace_bg"`
ActiveWorkspaceBorder string `json:"active_workspace_border"`
InactiveWorkspaceText string `json:"inactive_workspace_text"`
InactiveWorkspaceBackground string `json:"inactive_workspace_bg"`
InactiveWorkspaceBorder string `json:"inactive_workspace_border"`
UrgentWorkspaceText string `json:"urgent_workspace_text"`
UrgentWorkspaceBackground string `json:"urgent_workspace_bg"`
UrgentWorkspaceBorder string `json:"urgent_workspace_border"`
BindingModeText string `json:"binding_mode_text"`
BindingModeBackground string `json:"binding_mode_bg"`
BindingModeBorder string `json:"binding_mode_border"`
}
type BarGaps struct {
Top int `json:"top"`
Right int `json:"right"`
Bottom int `json:"bottom"`
Left int `json:"left"`
}
type Version struct {
Major int `json:"major"`
Minor int `json:"minor"`
Patch int `json:"patch"`
HumanReadable string `json:"human_readable"`
LoadedConfigFileName string `json:"loaded_config_file_name"`
}
func (v Version) String() string {
data, _ := json.MarshalIndent(v, "", "\t")
return string(data)
}
type config struct {
Config string `json:"config"`
}
type name struct {
Name string `json:"name"`
}
type InputDevice struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
Vendor int `json:"vendor"`
Product int `json:"product"`
Type InputDeviceType `json:"type"`
XKBActiveLayoutName string `json:"xkb_active_layout_name"`
XKBLayoutNames []string `json:"xkb_layout_names"`
XKBActiveLayoutIndex int `json:"xkb_active_layout_index"`
ScrollFactor float64 `json:"scroll_factor"`
LibInput LibInput `json:"libinput"`
}
func (id InputDevice) String() string {
data, _ := json.MarshalIndent(id, "", "\t")
return string(data)
}
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 float64 `json:"accel_speed"`
AccelProfile string `json:"accel_profile"`
NaturalScroll string `json:"natural_scroll"`
LeftHanded string `json:"left_handed"`
ClickMethod string `json:"click_method"`
MiddleEmulation string `json:"middle_emulation"`
ScrollMethod string `json:"scroll_method"`
ScrollButton int `json:"scroll_button"`
Dwt string `json:"dwt"`
Dwtp string `json:"dwtp"`
CalibrationMatrix [6]float64 `json:"calibration_matrix"`
}
type Seat struct {
Name string `json:"name"`
Capabilities int `json:"capabilities"`
Focus NodeID `json:"focus"`
Devices []InputDevice `json:"devices"`
}
func (s Seat) String() string {
data, _ := json.MarshalIndent(s, "", "\t")
return string(data)
}