Files

77 lines
1.8 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
// Package i3ipc observes i3 without issuing window-management commands.
package i3ipc
import (
"encoding/binary"
"encoding/json"
"errors"
"io"
)
const (
workspaces = 1
subscribe = 2
tree = 4
eventBit uint32 = 1 << 31
workspaceEvent = eventBit
modeEvent = eventBit | 2
windowEvent = eventBit | 3
// A corrupt peer cannot cause an unbounded allocation or event backlog.
maxPayload = 16 << 20
maxPending = 256
)
type message struct {
kind uint32
body []byte
}
func readMessage(r io.Reader) (message, error) {
var header [14]byte
if _, err := io.ReadFull(r, header[:]); err != nil {
return message{}, err
}
if string(header[:6]) != "i3-ipc" {
return message{}, errors.New("invalid i3 magic")
}
size := binary.NativeEndian.Uint32(header[6:10])
if size > maxPayload {
return message{}, errors.New("i3 payload too large")
}
body := make([]byte, int(size))
if _, err := io.ReadFull(r, body); err != nil {
return message{}, err
}
if len(body) != 0 && !json.Valid(body) {
return message{}, errors.New("invalid i3 JSON")
}
return message{binary.NativeEndian.Uint32(header[10:]), body}, nil
}
func writeMessage(w io.Writer, kind uint32, body []byte) error {
if len(body) > maxPayload {
return errors.New("i3 payload too large")
}
var header [14]byte
copy(header[:], "i3-ipc")
binary.NativeEndian.PutUint32(header[6:10], uint32(len(body)))
binary.NativeEndian.PutUint32(header[10:], kind)
for _, part := range [][]byte{header[:], body} {
for len(part) > 0 {
n, err := w.Write(part)
if err != nil {
return err
}
if n == 0 {
return io.ErrShortWrite
}
part = part[n:]
}
}
return nil
}