update swayipc, go, add linter cfg, satisfy linter

This commit is contained in:
2026-08-22 20:51:17 +02:00
parent 60081f9dce
commit 25d5c571ec
4 changed files with 145 additions and 33 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,9 @@ matrix:
platform:
- linux/amd64
goversion:
- 1.24
- 1.26
lintversion:
- v2.12.2
labels:
platform: ${platform}
@@ -21,7 +23,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]

View File

@@ -46,10 +46,7 @@ test: clean
testlint: test lint
lint:
golangci-lint run
lint-full:
golangci-lint run --enable-all --exclude-use-default --disable exhaustivestruct,exhaustruct,depguard,interfacer,deadcode,golint,structcheck,scopelint,varcheck,ifshort,maligned,nosnakecase,godot,funlen,gofumpt,cyclop,noctx,gochecknoglobals,paralleltest
golangci-lint run --show-stats=false
testfuzzy: clean
go test -fuzz ./... $(ARGS)

61
main.go
View File

@@ -45,13 +45,6 @@ const (
LevelNotice = slog.Level(2)
VERSION = "v0.3.2"
IPC_HEADER_SIZE = 14
IPC_MAGIC = "i3-ipc"
// message types
IPC_GET_TREE = 4
IPC_RUN_COMMAND = 0
)
var (
@@ -88,7 +81,7 @@ func main() {
flag.BoolVarP(&Previous, "prev", "p", false, "cycle backward")
flag.BoolVarP(&Debug, "debug", "d", false, "enable debugging")
flag.BoolVarP(&Dumptree, "dump", "D", false, "dump the sway tree (needs -d as well)")
flag.BoolVarP(&Dumpvisibles, "dump-visibles", "", false, "dump a list of visible windows on current workspace (needs -d)")
flag.BoolVarP(&Dumpvisibles, "dump-visibles", "", false, "dump the sway tree (needs -d as well)")
flag.BoolVarP(&Notswitch, "no-switch", "n", false, "do not switch windows")
flag.BoolVarP(&Version, "version", "v", false, "show program version")
flag.BoolVarP(&Showhelp, "help", "h", Showhelp, "show help")
@@ -117,35 +110,46 @@ func main() {
log.Fatalf("failed to close log file: %s", err)
}
}()
setupLogging(file)
} else {
setupLogging(os.Stdout)
}
if err := cycle(); err != nil {
log.Fatal(err) // nolint:gocritic
}
}
func cycle() error {
// connect to sway unix socket
ipc := swayipc.NewSwayIPC()
err := ipc.Connect()
if err != nil {
return err
}
defer func() {
if err := ipc.Close(); err != nil {
log.Fatal(err)
}
defer ipc.Close()
}()
sway, err := ipc.GetTree()
if err != nil {
log.Fatal(err)
return err
}
// traverse the tree and find visible windows
if err := processJSON(sway); err != nil {
log.Fatalf("%s", err)
return err
}
if len(Visibles) == 0 {
os.Exit(0)
return nil
}
id := 0
var id int
if Previous {
id = findPrevWindow()
slog.Debug("findPrevWindow", "nextid", id)
@@ -156,9 +160,11 @@ func main() {
if id > 0 && !Notswitch {
if err := switchFocus(id, ipc); err != nil {
log.Fatalf("%s", err)
return err
}
}
return nil
}
// get into the sway tree, determine current workspace and extract all
@@ -177,6 +183,7 @@ func processJSON(sway *swayipc.Node) error {
// this is an output node containing the current workspace
CurrentWorkspace = node.CurrentWorkspace
recurseNodes(node.Nodes)
break
}
}
@@ -200,6 +207,7 @@ func findNextWindow() int {
for _, node := range Visibles {
if node.Focused {
seenfocused = true
continue
}
@@ -227,6 +235,7 @@ func findPrevWindow() int {
if node.Focused {
return prevnode
}
prevnode = node.ID
}
@@ -237,7 +246,7 @@ func findPrevWindow() int {
func switchFocus(id int, ipc *swayipc.SwayIPC) error {
responses, err := ipc.RunContainerCommand(id, "focus")
if err != nil {
log.Fatalf("failed to send focus command to container %d: %s (%s)",
return fmt.Errorf("failed to send focus command to container %d: %s (%s)",
id, responses[0].Error, err)
}
@@ -249,18 +258,18 @@ func switchFocus(id int, ipc *swayipc.SwayIPC) error {
// iterate recursively over given node list extracting visible windows
func recurseNodes(nodes []*swayipc.Node) {
for _, node := range nodes {
if istype(node, workspace) {
if node.Name == CurrentWorkspace {
//floating_nodes need to be sorted because
//order changes each time they are focused.
// floating_nodes need to be sorted because
// order changes each time they are focused.
FloatVis := node.FloatingNodes
sort.Slice(FloatVis, func(i, j int) bool {
return FloatVis[i].ID < FloatVis[j].ID
})
//now we can handle nodes and floating_nodes identical
// now we can handle nodes and floating_nodes identical
node.Nodes = append(node.Nodes, FloatVis...)
recurseNodes(node.Nodes)
return
}
@@ -329,25 +338,25 @@ func setupLogging(output io.Writer) {
}
}
// little helper to distinguish sway tree node types
// istype is a little helper to distinguish sway tree node types
func istype(nd *swayipc.Node, which int) bool {
switch nd.Type {
case "root":
return which == root
return which == swayipc.NodeTypeRoot
case "output":
return which == output
return which == swayipc.NodeTypeOutput
case "workspace":
return which == workspace
return which == swayipc.NodeTypeWorkspace
case "con":
return which == con
return which == swayipc.NodeTypeCon
case "floating_con":
return which == floating
return which == swayipc.NodeTypeFloating
}
return false
}
// returns TRUE if stdout is NOT a tty or windows
// IsNoTty returns TRUE if stdout is NOT a tty or windows
func IsNoTty() bool {
if !isatty.IsTerminal(os.Stdout.Fd()) {
return true