renovate the code, update to go 1.26 and satisfy linter (#3)

This commit is contained in:
T. von Dein
2026-08-22 17:59:59 +02:00
parent 9cd6e4a6b9
commit 6d9763a0a9
77 changed files with 681 additions and 227 deletions

104
.golangci.yml Normal file
View File

@@ -0,0 +1,104 @@
version: "2"
linters:
enable:
- errcheck
- govet
- ineffassign
- staticcheck
- unused
- arangolint
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- clickhouselint
- containedctx
- copyloopvar
- cyclop
- decorder
- dogsled
- dupword
- durationcheck
- embeddedstructfieldcheck
- errchkjson
- errname
- exhaustive
- exptostd
- fatcontext
- funcorder
- funlen
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoinits
- gochecksumtype
- gocritic
- gocyclo
- godoclint
- goheader
- gomoddirectives
- gomodguard_v2
- goprintffuncname
- gosmopolitan
- grouper
- iface
- importas
- inamedparam
- interfacebloat
- intrange
- iotamixing
- lll
- loggercheck
- makezero
- misspell
- modernize
- nakedret
- nestif
- nilerr
- nilnesserr
- nlreturn
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- promlinter
- protogetter
- reassign
- recvcheck
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- tagalign
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unconvert
- unparam
- unqueryvet
- usestdlibvars
- usetesting
- varnamelen
- wastedassign
- whitespace
- wsl_v5
- zerologlint
settings:
varnamelen:
ignore-names:
- err
- wg
- mu
- ts
- to
- es
- op
- fd
- id
- fn

View File

@@ -2,7 +2,10 @@ matrix:
platform:
- linux/amd64
goversion:
- 1.24
- 1.26
lintversion:
- v2.12.2
labels:
platform: ${platform}
@@ -21,7 +24,7 @@ steps:
event: [push]
image: golang:${goversion}
commands:
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin ${lintversion}
- golangci-lint --version
- golangci-lint run ./...
depends_on: [build]

5
Makefile Normal file
View File

@@ -0,0 +1,5 @@
all:
go build .
lint:
golangci-lint run --show-stats=false

88
_examples/Makefile Normal file
View File

@@ -0,0 +1,88 @@
examples: example-current_workspace example-events example-find_focused_window example-get_bars example-get_config example-get_inputs example-get_marks example-get_outputs example-get_seats example-get_tree example-get_version example-get_workspaces example-run_command example-run_global_command
clean: clean-example-current_workspace clean-example-events clean-example-find_focused_window clean-example-get_bars clean-example-get_config clean-example-get_inputs clean-example-get_marks clean-example-get_outputs clean-example-get_seats clean-example-get_tree clean-example-get_version clean-example-get_workspaces clean-example-run_command clean-example-run_global_command
example-current_workspace:
make -C current_workspace
example-events:
make -C events
example-find_focused_window:
make -C find_focused_window
example-get_bars:
make -C get_bars
example-get_config:
make -C get_config
example-get_inputs:
make -C get_inputs
example-get_marks:
make -C get_marks
example-get_outputs:
make -C get_outputs
example-get_seats:
make -C get_seats
example-get_tree:
make -C get_tree
example-get_version:
make -C get_version
example-get_workspaces:
make -C get_workspaces
example-run_command:
make -C run_command
example-run_global_command:
make -C run_global_command
clean-example-current_workspace:
make -C current_workspace clean
clean-example-events:
make -C events clean
clean-example-find_focused_window:
make -C find_focused_window clean
clean-example-get_bars:
make -C get_bars clean
clean-example-get_config:
make -C get_config clean
clean-example-get_inputs:
make -C get_inputs clean
clean-example-get_marks:
make -C get_marks clean
clean-example-get_outputs:
make -C get_outputs clean
clean-example-get_seats:
make -C get_seats clean
clean-example-get_tree:
make -C get_tree clean
clean-example-get_version:
make -C get_version clean
clean-example-get_workspaces:
make -C get_workspaces clean
clean-example-run_command:
make -C run_command clean
clean-example-run_global_command:
make -C run_global_command clean

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f current_workspace

View File

@@ -0,0 +1,9 @@
module current_workspace
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"log"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f events

9
_examples/events/go.mod Normal file
View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

2
_examples/events/go.sum Normal file
View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -9,8 +9,8 @@ import (
"fmt"
"log"
"codeberg.org/scip/swayipc/v2"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
)
// Event callback function, needs to implement each subscribed events,
@@ -18,11 +18,11 @@ import (
func ProcessTick(event *swayipc.RawResponse) error {
var err error
switch event.PayloadType {
case swayipc.EV_Tick:
case swayipc.EvTick:
ev := &swayipc.EventTick{}
err = json.Unmarshal(event.Payload, &ev)
repr.Println(ev)
case swayipc.EV_Window:
case swayipc.EvWindow:
ev := &swayipc.EventWindow{}
err = json.Unmarshal(event.Payload, &ev)
repr.Println(ev)

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f find_focused_window

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"log"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {
@@ -25,6 +25,6 @@ func main() {
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)
focused.Name, focused.ID, focused.Geometry.Width, focused.Geometry.Height)
}
}

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_bars

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -9,7 +9,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_config

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"fmt"
"log"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_inputs

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_marks

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_outputs

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_seats

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_tree

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -4,7 +4,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_version

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f get_workspaces

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -8,7 +8,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f run_command

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -9,8 +9,8 @@ package main
import (
"log"
"codeberg.org/scip/swayipc/v2"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
)
func main() {
@@ -37,7 +37,7 @@ func main() {
// finally execute the given commands on it, you can use any run
// command, see sway(5)
responses, err := ipc.RunContainerCommand(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)

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f run_global_command

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -9,7 +9,7 @@ import (
"log"
"github.com/alecthomas/repr"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

View File

@@ -0,0 +1,5 @@
all:
go build .
clean:
rm -f send_tick

View File

@@ -0,0 +1,9 @@
module send_tick
go 1.26
require github.com/alecthomas/repr v0.5.4 // indirect
replace codeberg.org/scip/swayipc/v2 => ../..
require codeberg.org/scip/swayipc/v2 v2.1.0

View File

@@ -0,0 +1,2 @@
github.com/alecthomas/repr v0.5.4 h1:OVP7JEcuzU9CCDsT6STCr3rg17oQfWILtPWd2EG0uN4=
github.com/alecthomas/repr v0.5.4/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

View File

@@ -12,7 +12,7 @@ import (
"log"
"os"
"codeberg.org/scip/swayipc/v1"
"codeberg.org/scip/swayipc/v2"
)
func main() {

30
bar.go
View File

@@ -5,7 +5,7 @@ import (
"fmt"
)
// Container gaps
// Gaps stores container gaps
type Gaps struct {
Top int `json:"top"`
Right int `json:"right"`
@@ -13,7 +13,7 @@ type Gaps struct {
Left int `json:"left"`
}
// Color definition, used primarily by bars
// Colors stores color definitions, used primarily by bars
type Colors struct {
Background string `json:"background"`
Statusline string `json:"statusline"`
@@ -27,23 +27,23 @@ type Colors struct {
InactiveWorkspaceBorder string `json:"inactive_workspace_border"`
InactiveWorkspaceBg string `json:"inactive_workspace_bg"`
InactiveWorkspaceText string `json:"inactive_workspace_text"`
Active_workspaceBorder string `json:"active_workspace_border"`
Active_workspaceBg string `json:"active_workspace_bg"`
Active_workspaceText string `json:"active_workspace_text"`
Urgent_workspaceBorder string `json:"urgent_workspace_border"`
Urgent_workspaceBg string `json:"urgent_workspace_bg"`
Urgent_workspaceText string `json:"urgent_workspace_text"`
ActiveWorkspaceBorder string `json:"active_workspace_border"`
ActiveWorkspaceBg string `json:"active_workspace_bg"`
ActiveWorkspaceText string `json:"active_workspace_text"`
UrgentWorkspaceBorder string `json:"urgent_workspace_border"`
UrgentWorkspaceBg string `json:"urgent_workspace_bg"`
UrgentWorkspaceText string `json:"urgent_workspace_text"`
BindingModeBorder string `json:"binding_mode_border"`
BindingModeBg string `json:"binding_mode_bg"`
BindingModeText string `json:"binding_mode_text"`
}
// A bar such as a swaybar(5)
// Bar stores information about a bar such as a swaybar(5)
type Bar struct {
Id string `json:"id"`
ID string `json:"id"`
Mode string `json:"mode"`
Position string `json:"position"`
Status_command string `json:"status_command"`
StatusCommand string `json:"status_command"`
Font string `json:"font"`
Gaps *Gaps `json:"gaps"`
Height int `json:"bar_height"`
@@ -57,9 +57,9 @@ type Bar struct {
Colors *Colors `json:"colors"`
}
// Get a list of currently visible and active bar names
// GetBars returns a list of currently visible and active bar names
func (ipc *SwayIPC) GetBars() ([]string, error) {
payload, err := ipc.get(GET_BAR_CONFIG)
payload, err := ipc.get(MsgGetBarConfig)
if err != nil {
return nil, err
}
@@ -72,9 +72,9 @@ func (ipc *SwayIPC) GetBars() ([]string, error) {
return bars, nil
}
// Get the bar object of the bar specified by the string 'id'
// GetBar returns the bar object of the bar specified by the string 'id'
func (ipc *SwayIPC) GetBar(id string) (*Bar, error) {
err := ipc.sendHeader(GET_BAR_CONFIG, uint32(len(id)))
err := ipc.sendHeader(MsgGetBarConfig, uint32(len(id)))
if err != nil {
return nil, err
}

View File

@@ -7,24 +7,26 @@ import (
"strings"
)
// Execute the specified global command[s] (one or more commands can be
// given) and returns a response list.
// RunGlobalCommand can be used to 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 *SwayIPC) 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.
// RunContainerCommand can be used to 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 *SwayIPC) 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.
// RunCommand executes 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).
//
@@ -41,7 +43,7 @@ func (ipc *SwayIPC) RunCommand(id int, target string, command ...string) ([]Resp
commands = fmt.Sprintf("[%s_id=%d] %s", target, id, commands)
}
err := ipc.sendHeader(RUN_COMMAND, uint32(len(commands)))
err := ipc.sendHeader(MsgRunCommand, uint32(len(commands)))
if err != nil {
return nil, fmt.Errorf("failed to send run_command to IPC %w", err)
}

135
event.go
View File

@@ -3,136 +3,105 @@ package swayipc
import (
"encoding/json"
"fmt"
"reflect"
)
// 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
EvWorkspace int = 0x80000000
EvOutput int = 0x80000001
EvMode int = 0x80000002
EvWindow int = 0x80000003
EvBarconfigUpdate int = 0x80000004
EvBinding int = 0x80000005
EvShutdown int = 0x80000006
EvTick int = 0x80000007
EvBarStateUpdate int = 0x80000014
EvInput int = 0x80000015
)
// Subscriber struct, use this to tell swayipc which events you want to
// subscribe.
// Event is the subscriber struct, use this to tell swayipc 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 bool `json:"workspace"`
Output bool `json:"output"`
Mode bool `json:"mode"`
Window bool `json:"window"`
BarconfigUpdate bool `json:"barconfig_update"`
Binding bool `json:"binding"`
Shutdown bool `json:"shutdown"`
Tick bool `json:"tick"`
BarStateUpdate bool `json:"bar_state_update"`
Input bool `json:"input"`
}
// Workspace event response
// EventWorkspace stores a workspace event response
type EventWorkspace struct {
Change string `json:"change"`
Current *Node `json:"workspace"`
Old *Node `json:"old"`
}
// Output event response
// EventOutput stores an output event response
type EventOutput struct {
Change string `json:"change"`
}
// Mode event response
// EventMode stores a ode event response
type EventMode struct {
Change string `json:"change"`
PangoMarkup *Node `json:"pango_markup"`
}
// Window event response
// EventWindow stores a window event response
type EventWindow struct {
Change string `json:"change"`
Container *Node `json:"container"`
}
// BarConfig event response
// EventBarConfig stores a BarConfig event response
type EventBarConfig struct {
Change string `json:"change"`
Binding *Binding `json:"binding"`
}
// Shutdown event response
// EventShutdown stores a Shutdown event response
type EventShutdown struct {
Change string `json:"change"`
}
// Tick event response
// EventTick stores a Tick event response
type EventTick struct {
First bool `json:"first"`
Payload string `json:"payload"`
}
// BarState event response
// EventBarState stores a BarState event response
type EventBarState struct {
Id string `json:"id"`
ID string `json:"id"`
VisibleByModifier bool `json:"visible_by_modifier"`
}
// Input event response
// EventInput stores an Input event response
type EventInput struct {
Change string `json:"change"`
Input *Input `json:"input"`
}
// Subscribe to one or more events. Fill the swayipc.Event object
// accordingly.
// Subscribe allows to subscribe to one or more events. Fill the
// Event object accordingly.
//
// Returns a response list containing a response for every subscribed
// event.
func (ipc *SwayIPC) 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")
}
events := eventList(sub)
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)))
err = ipc.sendHeader(MsgSubscribe, uint32(len(jsonpayload)))
if err != nil {
return nil, err
}
@@ -158,7 +127,9 @@ func (ipc *SwayIPC) Subscribe(sub *Event) ([]*Response, error) {
return nil, nil
}
// Event loop: Once you have subscribed to an event, you need to enter
// EventLoop starts an 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.
//
@@ -181,3 +152,29 @@ func (ipc *SwayIPC) EventLoop(callback func(event *RawResponse) error) error {
}
}
}
// eventList returns a list of subscribe events based on given Event objectstrucstruc
func eventList(sub *Event) []string {
out := []string{}
subv := reflect.ValueOf(sub).Elem()
subType := subv.Type()
for i := range subv.NumField() {
field := subv.Field(i)
structField := subType.Field(i)
value := field.Interface()
key := structField.Name
if tag := structField.Tag.Get(`json`); tag != "" {
key = tag
}
if value.(bool) {
out = append(out, key)
}
}
return out
}

2
go.mod
View File

@@ -1,5 +1,5 @@
module codeberg.org/scip/swayipc/v2
go 1.23
go 1.26
require github.com/alecthomas/repr v0.5.1 // indirect

View File

@@ -5,7 +5,7 @@ import (
"fmt"
)
// An input (keyboard, mouse, whatever)
// Input represents an input (keyboard, mouse, whatever)
type Input struct {
Identifier string `json:"identifier"`
Name string `json:"name"`
@@ -19,7 +19,7 @@ type Input struct {
Libinput *LibInput `json:"libinput"`
}
// Holds the data associated with libinput
// LibInput stores the data associated with libinput
type LibInput struct {
SendEvents string `json:"send_events"`
Tap string `json:"tap"`
@@ -41,7 +41,7 @@ type LibInput struct {
CalibrationMatrix []float32 `json:"calibration_matrix"`
}
// A key binding
// Binding is a key binding
type Binding struct {
Command string `json:"command"`
EventStateMask []string `json:"event_state_mask"`
@@ -50,9 +50,9 @@ type Binding struct {
InputType string `json:"input_type"`
}
// Get a list of all currently supported inputs
// GetInputs returns a list of all currently supported inputs
func (ipc *SwayIPC) GetInputs() ([]*Input, error) {
payload, err := ipc.get(GET_INPUTS)
payload, err := ipc.get(MsgGetInputs)
if err != nil {
return nil, err
}

1
io.go
View File

@@ -4,7 +4,6 @@ import "os"
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if err != nil {
return false
}

16
net.go
View File

@@ -8,7 +8,7 @@ import (
"os"
)
// Contains a raw json response, not marshalled yet.
// RawResponse contains a raw json response, not marshalled yet.
type RawResponse struct {
PayloadType int
Payload []byte
@@ -39,14 +39,13 @@ func (ipc *SwayIPC) Close() error {
}
func (ipc *SwayIPC) sendHeader(messageType uint32, len uint32) error {
sendPayload := make([]byte, IPC_HEADER_SIZE)
sendPayload := make([]byte, IpcHeaderSize)
copy(sendPayload, []byte(IPC_MAGIC))
binary.LittleEndian.PutUint32(sendPayload[IPC_MAGIC_LEN:], len)
binary.LittleEndian.PutUint32(sendPayload[IPC_MAGIC_LEN+4:], messageType)
copy(sendPayload, []byte(IpcMagix))
binary.LittleEndian.PutUint32(sendPayload[IpcMagicLen:], len)
binary.LittleEndian.PutUint32(sendPayload[IpcMagicLen+4:], messageType)
_, err := ipc.socket.Write(sendPayload)
if err != nil {
return fmt.Errorf("failed to send header to IPC socket %w", err)
}
@@ -56,7 +55,6 @@ func (ipc *SwayIPC) sendHeader(messageType uint32, len uint32) error {
func (ipc *SwayIPC) sendPayload(payload []byte) error {
_, err := ipc.socket.Write(payload)
if err != nil {
return fmt.Errorf("failed to send payload to IPC socket %w", err)
}
@@ -66,14 +64,14 @@ func (ipc *SwayIPC) sendPayload(payload []byte) error {
func (ipc *SwayIPC) readResponse() (*RawResponse, error) {
// read header
buf := make([]byte, IPC_HEADER_SIZE)
buf := make([]byte, IpcHeaderSize)
_, err := ipc.socket.Read(buf)
if err != nil {
return nil, fmt.Errorf("failed to read header from ipc socket: %s", err)
}
if string(buf[:6]) != IPC_MAGIC {
if string(buf[:6]) != IpcMagix {
return nil, errors.New("got invalid response from IPC socket")
}

94
node.go
View File

@@ -5,10 +5,18 @@ import (
"fmt"
)
// A node can be an output, a workspace, a container or a container
// containing a window.
const (
NodeTypeRoot = iota + 1 // A root node
NodeTypeOutput // An output node
NodeTypeWorkspace // A workspace
NodeTypeCon // A container node (containing more nodes)
NodeTypeFloating // A floating node
)
// Node can be an output, a workspace, a container or a container
// containing other containers.
type Node struct {
Id int `json:"id"`
ID int `json:"id"`
Type string `json:"type"` // output, workspace or container
Name string `json:"name"` // workspace number or app name
Output string `json:"output"`
@@ -26,26 +34,24 @@ type Node struct {
Focus []int `json:"focus"`
Window int `json:"window"` // wayland native
X11Window string `json:"app_id"` // x11 compat
Current_workspace string `json:"current_workspace"`
CurrentWorkspace string `json:"current_workspace"`
Rect Rect `json:"rect"`
WindowRect Rect `json:"window_rect"`
DecoRect Rect `json:"deco_rect"`
Geometry Rect `json:"geometry"`
}
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.
// GetTree returns the whole information tree, which contains
// everything from output to containers as a tree of nodes. Each node
// has a field 'Nodes' which points to a list subnodes. Some nodes
// also have a field 'FloatingNodes' which points to a list of
// floating containers.
//
// The top level node is the "root" node.
//
// Use the returned node oject to further investigate the wm setup.
func (ipc *SwayIPC) GetTree() (*Node, error) {
err := ipc.sendHeader(GET_TREE, 0)
err := ipc.sendHeader(MsgGettTree, 0)
if err != nil {
return nil, err
}
@@ -63,48 +69,48 @@ func (ipc *SwayIPC) GetTree() (*Node, error) {
return node, nil
}
// Usually called on the root node, returns the container which has
// currently the focus.
// FindFocused returns the container which has currently the
// focus. Usually called on the root node.
func (node *Node) FindFocused() *Node {
searchFocused(node.Nodes)
if __focused == nil {
searchFocused(node.FloatingNodes)
focused := searchFocused(node.Nodes)
if focused == nil {
return searchFocused(node.FloatingNodes)
}
return __focused
return nil
}
// internal recursive focus node searcher
func searchFocused(nodes []*Node) {
// FindCurrentWorkspace returns the current active
// workspace name. Usually called on the root node.
func (node *Node) FindCurrentWorkspace() string {
return searchCurrentWorkspace(node.Nodes)
}
// searchCurrentWorkspace search for current workspace
func searchCurrentWorkspace(nodes []*Node) string {
for _, node := range nodes {
if node.CurrentWorkspace != "" {
return node.CurrentWorkspace
} else {
return searchCurrentWorkspace(node.Nodes)
}
}
return ""
}
// searchFocused recursively search focused node
func searchFocused(nodes []*Node) *Node {
for _, node := range nodes {
if node.Focused {
__focused = node
return
return node
} else {
searchFocused(node.Nodes)
if __focused == nil {
searchFocused(node.FloatingNodes)
focused := searchFocused(node.Nodes)
if focused == nil {
return searchFocused(node.FloatingNodes)
}
}
}
}
// 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 != "" {
__currentworkspace = node.Current_workspace
return
} else {
searchCurrentWorkspace(node.Nodes)
}
}
return nil
}

View File

@@ -5,30 +5,30 @@ import (
"fmt"
)
// Store an output mode.
// Mode stores data of 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)
// Output represents an output object (i.e. a physical monitor)
type Output struct {
Name string `json:"name"`
Make string `json:"make"`
Serial string `json:"serial"`
Active bool `json:"active"`
Primary bool `json:"primary"`
SubpixelHinting string `json:"subpixel_hinting"`
Transform string `json:"transform"`
Current_workspace string `json:"current_workspace"`
Modes []*Mode `json:"modes"`
CurrentMode *Mode `json:"current_mode"`
Name string `json:"name"`
Make string `json:"make"`
Serial string `json:"serial"`
Active bool `json:"active"`
Primary bool `json:"primary"`
SubpixelHinting string `json:"subpixel_hinting"`
Transform string `json:"transform"`
CurrentWorkspace string `json:"current_workspace"`
Modes []*Mode `json:"modes"`
CurrentMode *Mode `json:"current_mode"`
}
// Get a list of currently available and usable outputs.
// GetOutputs returns a list of currently available and usable outputs.
func (ipc *SwayIPC) GetOutputs() ([]*Output, error) {
err := ipc.sendHeader(GET_OUTPUTS, 0)
err := ipc.sendHeader(MsgGetOutputs, 0)
if err != nil {
return nil, err
}

View File

@@ -5,7 +5,7 @@ import (
"fmt"
)
// Information about a seat containing input devices
// Seat stores information about a seat containing input devices
type Seat struct {
Name string `json:"name"`
Capabilities int `json:"capabilities"`
@@ -13,9 +13,9 @@ type Seat struct {
Devices []*Input `json:"devices"`
}
// Get input seats
// GetSeats returns input seats
func (ipc *SwayIPC) GetSeats() ([]*Seat, error) {
payload, err := ipc.get(GET_SEATS)
payload, err := ipc.get(MsgGetSeats)
if err != nil {
return nil, err
}

View File

@@ -5,8 +5,9 @@ import (
"fmt"
)
// GetWorkspaces returns a list of Nodes of type NodeTypeWorkspace
func (ipc *SwayIPC) GetWorkspaces() ([]*Node, error) {
payload, err := ipc.get(GET_WORKSPACES)
payload, err := ipc.get(MsgGetWorkspaces)
if err != nil {
return nil, err
}
@@ -19,8 +20,9 @@ func (ipc *SwayIPC) GetWorkspaces() ([]*Node, error) {
return nodes, nil
}
// GetMarks returns a list of marks
func (ipc *SwayIPC) GetMarks() ([]string, error) {
payload, err := ipc.get(GET_MARKS)
payload, err := ipc.get(MsgGetMarks)
if err != nil {
return nil, err
}
@@ -33,8 +35,9 @@ func (ipc *SwayIPC) GetMarks() ([]string, error) {
return marks, nil
}
// GetBindingModes returns a list of binding modes
func (ipc *SwayIPC) GetBindingModes() ([]string, error) {
payload, err := ipc.get(GET_BINDING_MODES)
payload, err := ipc.get(MsgGetBindingModes)
if err != nil {
return nil, err
}
@@ -47,8 +50,9 @@ func (ipc *SwayIPC) GetBindingModes() ([]string, error) {
return modes, nil
}
// GetBindingState returns a list of binding states
func (ipc *SwayIPC) GetBindingState() (*State, error) {
payload, err := ipc.get(GET_BINDING_STATE)
payload, err := ipc.get(MsgGetBindingState)
if err != nil {
return nil, err
}
@@ -61,8 +65,9 @@ func (ipc *SwayIPC) GetBindingState() (*State, error) {
return state, nil
}
// GetConfig return user wm config
func (ipc *SwayIPC) GetConfig() (string, error) {
payload, err := ipc.get(GET_CONFIG)
payload, err := ipc.get(MsgGetConfig)
if err != nil {
return "", err
}
@@ -75,8 +80,9 @@ func (ipc *SwayIPC) GetConfig() (string, error) {
return config.Config, nil
}
// SendTick sends a tick
func (ipc *SwayIPC) SendTick(payload string) error {
err := ipc.sendHeader(SEND_TICK, uint32(len(payload)))
err := ipc.sendHeader(MsgSendTick, uint32(len(payload)))
if err != nil {
return err
}

View File

@@ -1,3 +1,6 @@
// Package swayipc can be used to control the sway and swayfx window
// managers ()and possibly i3wm)via a unix domain socket.
package swayipc
import (
@@ -5,41 +8,43 @@ import (
)
const (
VERSION = "v1.0.0"
VERSION = "v2.1.0"
IPC_HEADER_SIZE = 14
IPC_MAGIC = "i3-ipc"
IPC_MAGIC_LEN = 6
IpcHeaderSize = 14
IpcMagix = "i3-ipc"
IpcMagicLen = 6
)
// message types
const (
MsgRunCommand = iota
MsgGetWorkspaces
MsgSubscribe
MsgGetOutputs
MsgGettTree
MsgGetMarks
MsgGetBarConfig
MsgGetVersion
MsgGetBindingModes
MsgGetConfig
MsgSendTick
MsgSync
MsgGetBindingState
)
const (
// message types
RUN_COMMAND = iota
GET_WORKSPACES
SUBSCRIBE
GET_OUTPUTS
GET_TREE
GET_MARKS
GET_BAR_CONFIG
GET_VERSION
GET_BINDING_MODES
GET_CONFIG
SEND_TICK
SYNC
GET_BINDING_STATE
GET_INPUTS = 100
GET_SEATS = 101
MsgGetInputs = 100
MsgGetSeats = 101
)
// This is the primary struct to work with the swayipc module.
// SwayIPC is the primary struct to work with the swayipc module.
type SwayIPC struct {
socket net.Conn
SocketFile string // filename of the i3 IPC socket
Events *Event // store subscribed events, see swayipc.Subscribe()
}
// A rectangle struct, used at various places for geometry etc.
// Rect stores geometrical information, used at various places for geometry etc.
type Rect struct {
X int `json:"x"` // X coordinate
Y int `json:"y"` // Y coordinate
@@ -47,25 +52,26 @@ type Rect struct {
Height int `json:"height"`
}
// Stores responses retrieved via ipc
// Response stores meta data 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
// Config stores the user config for the WM
type Config struct {
Config string `json:"config"`
}
// Stores the binding state
// State stores the binding state
type State struct {
Name string `json:"name"`
}
// Create a new swayipc.SwayIPC object. Filename argument is optional and
// may denote a filename or the name of an environment variable.
// NewSwayIPC returns a new swayipc.SwayIPC 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
@@ -82,7 +88,7 @@ func NewSwayIPC(file ...string) *SwayIPC {
return ipc
}
// internal convenience wrapper
// get is a wrapper around sendHeader+readResponse
func (ipc *SwayIPC) get(command uint32) (*RawResponse, error) {
err := ipc.sendHeader(command, 0)
if err != nil {

View File

@@ -5,7 +5,7 @@ import (
"fmt"
)
// A version struct holding the sway wm version
// Version stores the sway wm version
type Version struct {
HumanReadable string `json:"human_readable"`
Major int `json:"major"`
@@ -13,9 +13,9 @@ type Version struct {
Patch int `json:"patch"`
}
// Get the sway software version
// GetVersion returns the sway software version
func (ipc *SwayIPC) GetVersion() (*Version, error) {
payload, err := ipc.get(GET_VERSION)
payload, err := ipc.get(MsgGetVersion)
if err != nil {
return nil, err
}