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
- Variables
- func Discover() (string, error)
- func Run(ctx context.Context, cfg Config) error
- type AttrList
- type Component
- type Config
- type Emitter
- type Engine
- func (e *Engine) CandidateClicked(index, button, state uint32) (err *dbus.Error)
- func (e *Engine) CommitText(text IBusText)
- func (e *Engine) CursorDown() (err *dbus.Error)
- func (e *Engine) CursorUp() (err *dbus.Error)
- func (e *Engine) DeleteSurroundingText(offset int32, nchars uint32)
- func (e *Engine) Destroy() (err *dbus.Error)
- func (e *Engine) Disable() (err *dbus.Error)
- func (e *Engine) Enable() (err *dbus.Error)
- func (e *Engine) FocusIn() (err *dbus.Error)
- func (e *Engine) FocusOut() (err *dbus.Error)
- func (e *Engine) ForwardKeyEvent(keyval, keycode, state uint32)
- func (e *Engine) Get(name string) (value dbus.Variant, err *dbus.Error)
- func (e *Engine) GetAll(interfaceName string) (props map[string]dbus.Variant, err *dbus.Error)
- func (e *Engine) PageDown() (err *dbus.Error)
- func (e *Engine) PageUp() (err *dbus.Error)
- func (e *Engine) ProcessKeyEvent(keyval, keycode, state uint32) (handled bool, err *dbus.Error)
- func (e *Engine) PropertyActivate(name string, state uint32) (err *dbus.Error)
- func (e *Engine) PropertyHide(name string) (err *dbus.Error)
- func (e *Engine) PropertyShow(name string) (err *dbus.Error)
- func (e *Engine) RequireSurroundingText()
- func (e *Engine) Reset() (err *dbus.Error)
- func (e *Engine) Set(name string, value dbus.Variant) (err *dbus.Error)
- func (e *Engine) SetCapabilities(caps uint32) (err *dbus.Error)
- func (e *Engine) SetCursorLocation(x, y, width, height int32) (err *dbus.Error)
- func (e *Engine) SetSurroundingText(text dbus.Variant, cursorPos, anchorPos uint32) (err *dbus.Error)
- func (e *Engine) UpdateModeSymbol(symbol string)
- type EngineDesc
- type EngineEvent
- type EventHandler
- type IBusText
- type LifecycleKind
- type PropList
- type Property
Constants ¶
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).
const ( CapPreeditText = 1 << 0 CapSurroundingText = 1 << 5 CapSyncProcessKey = 1 << 7 )
Client capability bits reported through SetCapabilities (ibustypes.h:119-126).
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).
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.
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 ¶
var ErrNoAddress = errors.New("no usable ibus address file")
ErrNoAddress reports that no usable IBus address file was found.
Functions ¶
func Discover ¶
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 ¶
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 ¶
CandidateClicked implements org.freedesktop.IBus.Engine.CandidateClicked (u,u,u).
func (*Engine) CommitText ¶
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 ¶
CursorDown implements org.freedesktop.IBus.Engine.CursorDown ().
func (*Engine) DeleteSurroundingText ¶
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 ¶
Destroy implements org.freedesktop.IBus.Service.Destroy (): the object is unexported from all three interfaces.
func (*Engine) ForwardKeyEvent ¶
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 ¶
Get implements org.freedesktop.DBus.Properties.Get (s) → v: engines expose no properties in Phase 1.
func (*Engine) ProcessKeyEvent ¶
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 ¶
PropertyActivate implements org.freedesktop.IBus.Engine.PropertyActivate (s,u).
func (*Engine) PropertyHide ¶
PropertyHide implements org.freedesktop.IBus.Engine.PropertyHide (s).
func (*Engine) PropertyShow ¶
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) SetCapabilities ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.