engine

package
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package engine implements the IBus input-method engine wire adapter: it speaks the private IBus D-Bus protocol (registration, factory, per-input context engine object) and funnels decoded key and lifecycle events into the EventHandler seam.

Index

Constants

View Source
const (
	MaskShift   = 1 << 0
	MaskControl = 1 << 2
	MaskMod1    = 1 << 3 // Alt.
	MaskMod4    = 1 << 6 // Super.
	MaskMod5    = 1 << 7 // Level3/AltGr.
	MaskRelease = 1 << 30
)

Modifier masks decoded from the IBus key-event state word (/usr/include/ibus-1.0/ibustypes.h:70-77,97 — values verified verbatim).

View Source
const (
	CapPreeditText     = 1 << 0
	CapSurroundingText = 1 << 5
	CapSyncProcessKey  = 1 << 7
)

Client capability bits reported through SetCapabilities (ibustypes.h:119-126).

View Source
const (
	KeyBackSpace = 0xff08
	KeyTab       = 0xff09
	KeyReturn    = 0xff0d
	KeyEscape    = 0xff1b
	// KeyKPEnter is the keypad variant of Enter (XK_KP_Enter) — a CORR-09
	// reset trigger in its own right: the numpad Enter must clear the buffer
	// exactly like the main-row one (plan 02-05).
	KeyKPEnter  = 0xff8b
	KeyShiftL   = 0xffe1
	KeyShiftR   = 0xffe2
	KeyControlL = 0xffe3
	// KeyControlR is the right Ctrl key — the default combo key of the
	// word-layout gesture (SWCH-02/D-36; ibuskeysyms.h:191
	// "#define IBUS_KEY_Control_R 0xffe4", verified verbatim — never from
	// memory, the 02-05 class trap). The resolved binding itself lives in
	// the pure hotkey package as KeyvalCtrlR.
	KeyControlR = 0xffe4
	// KeySuperL/KeySuperR are the Super/Windows modifier keysyms
	// (ibuskeysyms.h "#define IBUS_KEY_Super_L 0xffeb" / Super_R 0xffec,
	// verified verbatim) — the press/release pair the MACR layer tracks for
	// its consumed-upstream detect (ADR-005 b.1/b.2).
	KeySuperL = 0xffeb
	KeySuperR = 0xffec
	KeySpace  = 0x020
	// KeyV is the Latin-1 'v' — the paste half of the clipboard rung's
	// Ctrl+V forward burst (plan 03-03, D-28).
	KeyV = 0x076
)

Keyvals used by the daemon (/usr/include/ibus-1.0/ibuskeysyms.h — values verified verbatim).

View Source
const (
	NameEN = "goswitch-en"
	NameRU = "goswitch-ru"
)

NameEN and NameRU are the wire names of the goswitch engine pair — the single source the flip path derives its SetGlobalEngine target from (D-52). The values are config-level literals (D-20/D-21): a flip journal record names the engine, never any user text.

View Source
const (
	PropTypeNormal     = 0 // ibusproperty.h:79 "PROP_TYPE_NORMAL = 0".
	PropStateUnchecked = 0 // ibusproperty.h:109 "PROP_STATE_UNCHECKED = 0".

)

The property constants (ibusproperty.h — values verified verbatim) and the mode-indicator identity: the key string is the EngineDesc icon_prop_key the GNOME panel resolves to the dynamic icon property, the initial symbol is the daemon's start mode (ADR-001: EN at start).

Variables

View Source
var ErrNoAddress = errors.New("no usable ibus address file")

ErrNoAddress reports that no usable IBus address file was found.

Functions

func Discover

func Discover() (string, error)

Discover returns the address of the live IBus daemon's private socket.

Precedence: the IBUS_ADDRESS environment variable, else the newest bus file in ~/.config/ibus/bus/ whose suffix matches the session type (-unix-<WAYLAND_DISPLAY> on Wayland, -unix-<DISPLAY> on X11). The daemon writes a fresh socket per generation (stale files accumulate), so callers must re-discover on every connection attempt and never cache the result.

func Run

func Run(ctx context.Context, cfg Config) error

Run connects to the private IBus bus, registers the component and serves engine traffic until ctx is cancelled. On bus loss the whole cycle repeats from address discovery — the socket path changes on every ibus-daemon generation, so the address is never cached (INTEG-04). A cancelled context is a clean shutdown: Run returns nil.

Types

type AttrList

type AttrList struct {
	Name        string // "IBusAttrList".
	Attachments map[string]dbus.Variant
	Attributes  []dbus.Variant // empty av for plain commits
}

AttrList mirrors the serialized IBusAttrList wire struct; Attributes is an av of variant-wrapped serialized IBusAttribute values — ibusattrlist.h keeps a GArray of attribute OBJECTS, and the daemon's CommitText parser reads the child as "av": an 'au' there trips its format assertion and crashes the 1.5.29 daemon (live-verified 2026-09-14, journal SEGV).

type Component

type Component struct {
	Name          string                  // "IBusComponent".
	Attachments   map[string]dbus.Variant // {}.
	ComponentName string                  // "org.freedesktop.IBus.goswitch".
	Description   string
	Version       string
	License       string // "MIT".
	Author        string
	Homepage      string
	Exec          string // "" — programmatic registration only, never spawned.
	Textdomain    string
	ObservedPaths []dbus.Variant // empty av.
	EngineList    []dbus.Variant // MakeVariant of each EngineDesc value.
}

Component mirrors the serialized IBusComponent wire struct (12 fields).

func NewComponent

func NewComponent(engines []EngineDesc) Component

NewComponent builds the goswitch IBusComponent wire payload with the given engine descriptions.

type Config

type Config struct {
	Component Component
	Engines   []EngineDesc
	Handler   EventHandler // may be nil — observer-only engine.

	// PostRegister is the optional self-reactivation seam (SY8): invoked
	// synchronously after EVERY successful RegisterComponent — generation 0
	// and every reconnect generation. nil = no-op. It must be cheap and
	// non-fatal: serve runs it inline before waitBusLoss, so a hang here
	// stalls the generation (the activate package's 10 s per-call timeout
	// bounds it).
	PostRegister func(ctx context.Context, generation int)

	// BindSwitcher is the optional flip seam (D-52): invoked once per
	// connection generation, right beside PostRegister, with the closure
	// that calls org.freedesktop.IBus.SetGlobalEngine on THIS generation's
	// connection. A switcher captured from a dead generation must never
	// survive the reconnect — the rebinding HERE is the guarantee
	// (T-05-03-03). nil = no-op (the PostRegister contract). The closure is
	// invoked by the actor on the flip path, never by the serve loop: a slow
	// bus can only stall a flip, never the registration cycle, and its
	// failures are journal lines (switch_engine), never generation errors.
	BindSwitcher func(flip func(ctx context.Context, engineName string) error)

	// OnGlobalEngine is the sync-listener input (05-04 criterion 3): invoked
	// with the engine name of every org.freedesktop.IBus.GlobalEngineChanged
	// signal on this connection AND of every FocusIn — the daemon follows
	// the FACTUAL active engine instead of trusting gsettings. nil = no-op.
	// It is called from the signal dispatcher's goroutine and from engine
	// D-Bus handlers: the callee must be cheap and never panic (the
	// FocusIn path is recoverHandler-wrapped). It NEVER flips anything
	// itself — the sync corrects only the receiver's internal state, never
	// the bus (single-writer, T-05-04-01).
	OnGlobalEngine func(engineName string)

	// BindGlobalEngine is the generation-scoped GetGlobalEngine reader seam
	// (05-04, Pitfall 3): invoked once per connection generation — bound
	// BEFORE PostRegister, so the same generation's reactivation can consult
	// it — with the closure that reads the FACTUAL active engine off THIS
	// generation's connection. A reader captured from a dead generation must
	// never survive the reconnect. nil = no-op (the PostRegister contract).
	BindGlobalEngine func(get func(ctx context.Context) (string, error))
}

Config carries everything Run needs: the component identity, the engines to register and the optional seams.

type Emitter

type Emitter interface {
	RequireSurroundingText()
	DeleteSurroundingText(offset int32, nchars uint32)
	ForwardKeyEvent(keyval, keycode, state uint32)
	CommitText(text IBusText)
	UpdateModeSymbol(symbol string)
}

Emitter is the outgoing-signal surface of an Engine — the ladder primitives the correction pipeline drives (ADR-003). Declared here because EventHandler.AttachEngine hands the freshly minted engine to the handler as this seam, not as the concrete object: consumers (and their test doubles) program against the four emitters alone.

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the per-input-context D-Bus object exported on the three interfaces above. godbus dispatches every method call on its own goroutine, so every exported handler carries a recover shim (INTEG-05): one unrecovered panic would kill the process and, through ibus-daemon's lifecycle coupling, desktop-wide input.

func NewEngine

func NewEngine(handler EventHandler, name string) *Engine

NewEngine creates an engine object for the named engine (e.g. "goswitch-en"); the name rides along into lifecycle log records so an observer can tell WHICH of the registered engines took focus — the observable the D-01 experiment asserts on. The handler may be nil. The connection and object path are bound at export time; a detached engine (no connection) simply skips signal emission, which keeps the object constructible in headless unit tests.

func (*Engine) CandidateClicked

func (e *Engine) CandidateClicked(index, button, state uint32) (err *dbus.Error)

CandidateClicked implements org.freedesktop.IBus.Engine.CandidateClicked (u,u,u).

func (*Engine) CommitText

func (e *Engine) CommitText(text IBusText)

CommitText emits the org.freedesktop.IBus.Engine.CommitText signal — the text-injection primitive. The adapter ships the emitter from day one; correction logic wires it in Phase 2.

func (*Engine) CursorDown

func (e *Engine) CursorDown() (err *dbus.Error)

CursorDown implements org.freedesktop.IBus.Engine.CursorDown ().

func (*Engine) CursorUp

func (e *Engine) CursorUp() (err *dbus.Error)

CursorUp implements org.freedesktop.IBus.Engine.CursorUp ().

func (*Engine) DeleteSurroundingText

func (e *Engine) DeleteSurroundingText(offset int32, nchars uint32)

DeleteSurroundingText emits the org.freedesktop.IBus.Engine. DeleteSurroundingText signal — the level-1 ladder primitive (ADR-003): the client is asked to delete nchars runes before (negative offset) or after the cursor. Fire-and-forget on IBus 1.5.29 — the ADR-004 verification is the compensation, not an ack.

func (*Engine) Destroy

func (e *Engine) Destroy() (err *dbus.Error)

Destroy implements org.freedesktop.IBus.Service.Destroy (): the object is unexported from all three interfaces.

func (*Engine) Disable

func (e *Engine) Disable() (err *dbus.Error)

Disable implements org.freedesktop.IBus.Engine.Disable ().

func (*Engine) Enable

func (e *Engine) Enable() (err *dbus.Error)

Enable implements org.freedesktop.IBus.Engine.Enable ().

func (*Engine) FocusIn

func (e *Engine) FocusIn() (err *dbus.Error)

FocusIn implements org.freedesktop.IBus.Engine.FocusIn ().

func (*Engine) FocusOut

func (e *Engine) FocusOut() (err *dbus.Error)

FocusOut implements org.freedesktop.IBus.Engine.FocusOut ().

func (*Engine) ForwardKeyEvent

func (e *Engine) ForwardKeyEvent(keyval, keycode, state uint32)

ForwardKeyEvent emits the org.freedesktop.IBus.Engine.ForwardKeyEvent signal — the level-2 ladder primitive (ADR-003): the key event is replayed to the client as if the user pressed it (the Backspace burst before the replacement commit). Fire-and-forget like every engine signal — the ADR-004 verification is the compensation, never an expected ack.

func (*Engine) Get

func (e *Engine) Get(name string) (value dbus.Variant, err *dbus.Error)

Get implements org.freedesktop.DBus.Properties.Get (s) → v: engines expose no properties in Phase 1.

func (*Engine) GetAll

func (e *Engine) GetAll(interfaceName string) (props map[string]dbus.Variant, err *dbus.Error)

GetAll implements org.freedesktop.DBus.Properties.GetAll (s) → a{sv}.

func (*Engine) PageDown

func (e *Engine) PageDown() (err *dbus.Error)

PageDown implements org.freedesktop.IBus.Engine.PageDown ().

func (*Engine) PageUp

func (e *Engine) PageUp() (err *dbus.Error)

PageUp implements org.freedesktop.IBus.Engine.PageUp ().

func (*Engine) ProcessKeyEvent

func (e *Engine) ProcessKeyEvent(keyval, keycode, state uint32) (handled bool, err *dbus.Error)

ProcessKeyEvent implements org.freedesktop.IBus.Engine.ProcessKeyEvent (u keyval, u keycode, u state) → b. It decodes the event, traces it at DEBUG and forwards it to the handler: the handler's verdict IS the answer — consumption is decided by the EventHandler (the RU script mode of Phase 2 commits a Cyrillic rune and consumes the key), the transport only returns it. A nil handler stays a pure observer (transit, the Phase 1 contract), and bare modifier presses are never consumed — the actor declines every non-printable.

func (*Engine) PropertyActivate

func (e *Engine) PropertyActivate(name string, state uint32) (err *dbus.Error)

PropertyActivate implements org.freedesktop.IBus.Engine.PropertyActivate (s,u).

func (*Engine) PropertyHide

func (e *Engine) PropertyHide(name string) (err *dbus.Error)

PropertyHide implements org.freedesktop.IBus.Engine.PropertyHide (s).

func (*Engine) PropertyShow

func (e *Engine) PropertyShow(name string) (err *dbus.Error)

PropertyShow implements org.freedesktop.IBus.Engine.PropertyShow (s).

func (*Engine) RequireSurroundingText

func (e *Engine) RequireSurroundingText()

RequireSurroundingText emits the org.freedesktop.IBus.Engine. RequireSurroundingText signal — the client answers with a fresh SetSurroundingText, which is the input of the pre-correction verification (ADR-004).

func (*Engine) Reset

func (e *Engine) Reset() (err *dbus.Error)

Reset implements org.freedesktop.IBus.Engine.Reset ().

func (*Engine) Set

func (e *Engine) Set(name string, value dbus.Variant) (err *dbus.Error)

Set implements org.freedesktop.DBus.Properties.Set (s,v).

func (*Engine) SetCapabilities

func (e *Engine) SetCapabilities(caps uint32) (err *dbus.Error)

SetCapabilities implements org.freedesktop.IBus.Engine.SetCapabilities (u caps): the capability bitmap is stored per input context and funneled to the handler — the input of the ADR-003 ladder-level choice.

func (*Engine) SetCursorLocation

func (e *Engine) SetCursorLocation(x, y, width, height int32) (err *dbus.Error)

SetCursorLocation implements org.freedesktop.IBus.Engine.SetCursorLocation (i,i,i,i): a no-op in Phase 1.

func (*Engine) SetSurroundingText

func (e *Engine) SetSurroundingText(text dbus.Variant, cursorPos, anchorPos uint32) (err *dbus.Error)

SetSurroundingText implements org.freedesktop.IBus.Engine.SetSurroundingText (v text, u cursor_pos, u anchor_pos): the variant-wrapped IBusText is decoded once here and the text with BOTH positions is funneled to the handler — the input of the ADR-004 pre-correction verification and of the selection detection (D-30: anchor_pos is the selection anchor, ibusengine.h:430). Logged at DEBUG with both fields, contents never recorded at INFO (D-20). A payload that does not decode as IBusText is dropped: the correction waiting on it then times out silently.

func (*Engine) UpdateModeSymbol

func (e *Engine) UpdateModeSymbol(symbol string)

UpdateModeSymbol emits the org.freedesktop.IBus.Engine.UpdateProperty signal for the mode-indicator panel property (owner decision 1, quick plan 260927-way): the GNOME input indicator's dynamic icon (EngineDesc icon_prop_key) follows the daemon's script mode. Fire-and-forget like every engine signal, quiet on a detached engine.

type EngineDesc

type EngineDesc struct {
	Name          string                  // "IBusEngineDesc".
	Attachments   map[string]dbus.Variant // {}.
	EngineName    string                  // "goswitch-en" / "goswitch-ru".
	LongName      string
	Description   string
	Language      string // "en" / "ru".
	License       string // "MIT".
	Author        string
	Icon          string
	Layout        string // "us" / "ru".
	Rank          uint32
	Hotkeys       string
	Symbol        string // "en" / "ru".
	Setup         string
	LayoutVariant string
	LayoutOption  string
	Version       string
	Textdomain    string
	IconPropKey   string // modePropKey — the dynamic panel icon property.
}

EngineDesc mirrors the serialized IBusEngineDesc wire struct (19 fields: s, a{sv}, s×8, u, s×8 — Layout at index 9 drives mutter XKB for the two-engine experiment, Symbol at index 12 is the GNOME indicator label, and IconPropKey appended LAST (19th, ibusenginedesc.h:335 appended-field order) names the engine property whose SYMBOL becomes the panel's dynamic icon — the mode-indicator key of owner decision 1, quick plan 260927-way).

func NewEngineDesc

func NewEngineDesc(name, longName, language, layout, symbol string) EngineDesc

NewEngineDesc builds one goswitch IBusEngineDesc wire payload.

type EngineEvent

type EngineEvent struct {
	Keyval  uint32
	Keycode uint32
	Release bool
	Mods    uint32
}

EngineEvent is the decoded form of a ProcessKeyEvent call. The adapter decodes the raw IBus state word exactly once; consumers never re-parse raw state.

type EventHandler

type EventHandler interface {
	HandleKey(ev EngineEvent) (consume bool)
	HandleLifecycle(kind LifecycleKind)
	// HandleSurroundingText delivers the surrounding text the client
	// reported with both positions (D-30: a selection is active exactly when
	// anchorPos != cursorPos) — the input of the ADR-004 pre-correction
	// verification and of the selection-range detection.
	HandleSurroundingText(text string, cursorPos, anchorPos uint32)
	// HandleCapabilities delivers the per-input-context capability bitmap
	// (SetCapabilities), the input of the ADR-003 ladder-level choice.
	HandleCapabilities(caps uint32)
	// AttachEngine binds the emitter sink of the engine minted for the
	// input context: the handler's corrections drive it.
	AttachEngine(eng Emitter)
}

EventHandler is the seam the rest of the daemon plugs into (hotkey FSM, buffers, correction). A nil handler is legal: the engine then only observes and logs. HandleKey's bool is the consumption verdict: true means the handler already delivered the key's character (RU-mode commit) and the client must not insert anything.

type IBusText

type IBusText struct {
	Name        string // "IBusText".
	Attachments map[string]dbus.Variant
	Text        string
	AttrList    dbus.Variant
}

IBusText mirrors the serialized IBusText wire struct; AttrList carries a variant-wrapped AttrList value.

func NewIBusText

func NewIBusText(text string) IBusText

NewIBusText builds a plain-text IBusText payload with an empty attribute list — the shape CommitText expects for unstyled commits.

type LifecycleKind

type LifecycleKind int

LifecycleKind enumerates the engine lifecycle events forwarded to the EventHandler seam.

const (
	LifecycleFocusIn LifecycleKind = iota
	LifecycleFocusOut
	LifecycleReset
	LifecycleEnable
	LifecycleDisable
)

Lifecycle kinds delivered to HandleLifecycle.

func (LifecycleKind) String

func (k LifecycleKind) String() string

String returns the stable label used in log records.

type PropList

type PropList struct {
	Name        string                  // "IBusPropList".
	Attachments map[string]dbus.Variant // {}.
	Properties  []dbus.Variant          // variant-wrapped Property values.
}

PropList mirrors the serialized IBusPropList wire struct — the container RegisterProperties carries and Property.SubProps wraps.

type Property

type Property struct {
	Name        string                  // "IBusProperty".
	Attachments map[string]dbus.Variant // {}.
	Key         string                  // modePropKey — the panel icon key.
	Type        uint32                  // PropTypeNormal (ibusproperty.h:79).
	Label       dbus.Variant            // variant-wrapped IBusText.
	Icon        string                  // "" — the symbol carries the mode.
	Tooltip     dbus.Variant            // variant-wrapped IBusText.
	Sensitive   bool
	Visible     bool
	State       uint32 // PropStateUnchecked (ibusproperty.h:109).
	SubProps    dbus.Variant
	Symbol      dbus.Variant // variant-wrapped IBusText — the panel icon glyph.
}

Property mirrors the serialized IBusProperty wire struct — the panel property of the mode indicator (owner decision 1, quick plan 260927-way). Field order is the ctor order of ibusproperty.h:159-167 (key, type, label, icon, tooltip, sensitive, visible, state, prop_list) with Symbol APPENDED LAST — the appended-field convention of every IBus struct (verified against the goibus property.go serialization, the STACK-sanctioned read-only wire reference). Label/Tooltip/SubProps/Symbol are variant-wrapped IBusText/PropList values, the IBusText.AttrList precedent.

func NewModeProperty

func NewModeProperty(symbol string) Property

NewModeProperty builds the mode-indicator panel property payload: a plain normal property (NOT a toggle — PropertyActivate stays a stub, nothing may invite clicking) whose Label AND Symbol carry the given script symbol, with an empty sub-property list.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL