Files
cthulhu/internal/wine/review_protocol.go

83 lines
2.6 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package wine
import (
"encoding/hex"
"errors"
"strings"
"git.stormux.org/storm/cthulhu/internal/atspi"
"github.com/godbus/dbus/v5"
)
const ReviewProtocolVersion uint32 = 1
const ReviewObjectPath dbus.ObjectPath = "/org/stormux/Cthulhu1/WineReview"
const ReviewInterface = "org.stormux.Cthulhu1.WineReview"
var (
ErrReviewUnavailable = errors.New("Wine review unavailable")
ErrReviewStale = errors.New("Wine review source changed")
ErrReviewBusy = errors.New("Wine review capture busy")
ErrReviewLimited = errors.New("Wine review capture exceeds limits")
ErrReviewInvalid = errors.New("invalid Wine review snapshot")
)
// Notices are copied by the registry. Sender and Path are supplied by actual
// registration, never by native payloads. The event loop owns their eligibility.
type ReviewFocusNotice struct {
Sender string
Path dbus.ObjectPath
Instance, Window, Focus string
Revision uint64
Title, Role string
Protected bool
Bounds atspi.Rectangle
}
type ReviewRange struct {
Start, End, UTF16Start, UTF16End uint64
Text string
Bounds atspi.Rectangle
}
type ReviewNode struct {
ID, Parent, Role, Name string
Protected, Visible, TextKnown, CaretKnown bool
Caret, CaretUTF16 uint64
Bounds atspi.Rectangle
Ranges []ReviewRange
}
type ReviewSnapshot struct {
Version uint32
Instance, Window, Focus string
Revision uint64
Status string
Bounds atspi.Rectangle
Nodes []ReviewNode
}
func ReviewID(sender, instance, window, node string) atspi.ObjectID {
encode := func(value string) string { return hex.EncodeToString([]byte(value)) }
return atspi.NewObjectID(sender, "/wine/"+encode(instance)+"/"+encode(window)+"/"+encode(node))
}
func IsReviewID(id atspi.ObjectID) bool {
if !id.Valid() || !strings.HasPrefix(id.BusName, ":") {
return false
}
parts := strings.Split(id.Path, "/")
if len(parts) != 5 || parts[0] != "" || parts[1] != "wine" {
return false
}
for _, part := range parts[2:] {
if part == "" || len(part) > 512 {
return false
}
if _, err := hex.DecodeString(part); err != nil {
return false
}
}
return true
}
func (notice ReviewFocusNotice) ID() atspi.ObjectID {
return ReviewID(notice.Sender, notice.Instance, notice.Window, notice.Focus)
}