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: platform:
- linux/amd64 - linux/amd64
goversion: goversion:
- 1.24 - 1.26
lintversion:
- v2.12.2
labels: labels:
platform: ${platform} platform: ${platform}
@@ -21,7 +23,7 @@ steps:
event: [push] event: [push]
image: golang:${goversion} image: golang:${goversion}
commands: 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 --version
- golangci-lint run ./... - golangci-lint run ./...
depends_on: [build] depends_on: [build]

View File

@@ -46,10 +46,7 @@ test: clean
testlint: test lint testlint: test lint
lint: lint:
golangci-lint run golangci-lint run --show-stats=false
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
testfuzzy: clean testfuzzy: clean
go test -fuzz ./... $(ARGS) go test -fuzz ./... $(ARGS)

55
main.go
View File

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