Version 0.3
5 Nov 2014
Abstract
This article presents a simple cross-toolkit protocol for input methods, based on the standard D-Bus activation facility. It aims at removing any specialized input method frameworks, in favor of the standard mechanism. Note: This article is a protocol sketch at the moment and not meant to be any official proposal.
Table of Contents
In order to provide multilingual text input, several input method frameworks have been proposed in the past, such as SCIM, uim, Fcitx, and IBus. On the other hand, the essential functionalities of those frameworks largely overlap with standard application management in both KDE and GNOME. This proposal tries to replace the concept of input method framework with a standard mechanism, namely D-Bus activatable applications.
A typical input method framework does the following:
Installation: Manage addition/removal/listing of input method packages.
Activation: Maintain "active" input methods.
Messaging: Deliver messages between clients and input methods.
1 is a duplicate of the standard package management. 2 can be implemented as a D-Bus activatable application. 3 is peculiar to input method services and defined later in this proposal.
Input methods must comply with the requirements of D-Bus
activatable applications. In particular, they must
install .desktop
files with a D-Bus
well-known name.
The .desktop
files should have a
Implements
key containing
org.freedesktop.InputMethod
so they can be
distinguished from ordinary applications.
On activation (in the Activate
method of the
org.freedesktop.Application
interface), input
methods must own the D-Bus well-known name
org.freedesktop.InputMethod
, with the
DBUS_NAME_FLAG_ALLOW_REPLACEMENT
flag set.
Clients and desktop shells can be notified when the active
input method has changed, through the
NameOwnerChanged
D-Bus signal.
Input methods must implement the following interface (given in D-Bus introspection XML format):
<interface name='org.freedesktop.InputMethod'> <method name='GetAddress'> <arg type='s' name='address' direction='out'/> </method> <method name='CreateEngine'> <arg type='o' name='object_path' direction='out'/> <arg type='a{sv}' name='platform_data' direction='in'/> </method> </interface>
The org.freedesktop.InputMethod
interface is
responsible for the creation of input method engines, which
perform the succeeding communication with the clients.
The GetAddress
method returns a D-Bus address
where the created engine is exported. It may be an empty string
if the engines are exported on the same bus where the
org.freedesktop.InputMethod
interface is
exported.
The CreateEngine
method is called when a
client needs a new input method engine. The method returns the
object-path which the new engine is exported at. The
object-path must be in the form: starting with the well-known
D-Bus name of the input method, change all dots to slashes and
prefix a slash, append a unique identifier. For example, if the
well-known D-Bus name is
org.example.FooInputMethod
, an engine's
object-path could be
/org/example/FooInputMethod/Engine1
.
Note that once an engine is created, it may remain usable even
after the input method disowns the
org.freedesktop.InputMethod
name. That
allows different clients to have different input methods, by
ignoring the NameOwnerChanged
signal when
those clients don't have a focus.
Input method engines must implement the following interface:
<interface name='org.freedesktop.InputMethod.Engine'> <method name='KeyEvent'> <arg type='u' name='keycode' direction='in'/> <arg type='b' name='pressed' direction='in'/> <arg type='b' name='handled' direction='out'/> </method> <method name='Focus'> <arg type='b' name='focused'/> </method> <method name='Reset'/> <method name='SetSurroundingText'> <arg type='s' name='text' direction='in'/> <arg type='u' name='cursor_pos' direction='in'/> <arg type='u' name='anchor_pos' direction='in'/> </method> <method name='SetContentType'> <arg type='u' name='purpose' direction='in'/> <arg type='u' name='hints' direction='in'/> </method> <method name='Destroy'/> <signal name='Commit'> <arg type='s' name='text'/> </signal> <signal name='PreeditChanged'> <arg type='s' name='text'/> <arg type='a(uuu)' name='styling'/> <arg type='i' name='cursor_pos'/> </signal> <signal name='DeleteSurroundingText'> <arg type='i' name='offset'/> <arg type='u' name='nchars'/> </signal> <signal name='SetCandidateListVisible'> <arg type='b' name='visible'/> </signal> </interface>
The KeyEvent
method is called by the client
when it requests the engine to process a key event. The method
takes two input arguments: a hardware keycode and a flag
indicating whether the key is pressed or released. If the
engine consumes the key event, it returns TRUE, otherwise FALSE.
Note that there is no argument conveying a translated keysym value nor a modifier mask. It's an engine's responsibility to keep track of the keyboard state and interpret hardware keycodes. That can be typically done with a library like libxkbcommon.
The Focus
and Reset
methods are called by the client when it has changed the state.
The engine is responsible for those changes and may behave
appropriately (e.g. flushing pending input).
The SetSurroundingText
method is called by
the client when the text around cursor (surrounding text) has
changed. This is particularly useful for inputting in a complex
script which requires replacing a previously committed
characters with a combined character.
This method takes three input arguments: a string, a cursor
position, and an anchor position.
The SetContentType
method is called by the
client when the content type of the text has changed.
Content-type is a meta-information about how the text is treated
by the client. This method takes two input arguments: a purpose
(an enumeration value) and a bitmask of hints. Possible
purposes are defined as follows:
Possible hints are defined as follows. Multiple hints can be set in a bitmask, as a bitwise OR.
The Destroy
method is called by the client
when it explicitly destroys the engine. After the call, the
engine is no longer usable. The engine can also be destroyed
when the peer client is disconnected from the D-Bus session.
The Commit
signal is emitted by the engine
when it wants to finish the current editing. The signal carries
a string produced by the editing.
The PreeditChanged
signal is emitted by the
engine when it wants to update the preedit text displayed on the
client ("pre-edit" means "prior to commit"). The signal takes a
string, an array of styling attributes, and the cursor position.
A styling attribute is a tuple of three elements: a starting
position (in characters), an ending position (in characters,
exclusive), and a styling type. Possible styling types are
defined as follows:
The DeleteSurroundingText
signal is emitted
by the engine when it wants to delete a portion of text already
committed to the client. The signal takes two arguments: an
offset from the cursor, and the number of characters being
deleted.
The SetCandidateListVisible
signal is emitted
by the engine when it has changed the visibility of a candidate
list window.
Input method engines may optionally implement the following interface for a candidate list:
<interface name='org.freedesktop.InputMethod.CandidateList'> <method name='Start'> <arg type='u' name='npages' direction='out'/> <arg type='as' name='labels' direction='out'/> </method> <method name='End'/> <signal name='PageChanged'> <arg type='u' name='page'/> <arg type='as' name='content'/> </signal> <signal name='SelectionChanged'> <arg type='i' name='index'/> </signal> </interface>
Note that only one candidate list can be exported from an
engine. The object-path of a candidate list must be in the
form: append /CandidateList
to the engine's
object-path. For example, if the engine's object-path is
/org/example/FooInputMethod/Engine1
, the
candidate list's object-path will be
/org/example/FooInputMethod/Engine1/CandidateList
.
Since a candidate list is often large, the interface is designed to be capable of accommodating infinite number of candidates. An engine is supposed to feed new candidates as a page, when requested by the client.
The Start
method is called when the client is
interested in the content of the candidate list. This method
returns two parameters: the number of pages in the candidate
list, and a list of labels. The number of candidates in a page
is determined from the length of the labels list. This method
has a side-effect that triggers the initial
PageChanged
and
SelectionChanged
signals are emitted.
The End
method is called when the client is
no longer interested in the candidate list.
The PageChanged
signal is emitted by the
engine when a page transition happens. It takes two arguments:
the page index and a list of candidates.
The SelectionChanged
signal is emitted by the
engine when the selection on the page has changed. It takes an
argument indicating the index of the selected candidate in the page.
The system keymap is out of scope of this proposal. Input methods are usually not affected by the change of system keymaps.
Per-engine menus are out of scope of this proposal. There are efficient ways to implement them, such as GMenuModel D-Bus API.
The placement of a candidate list window is out of scope of this proposal. It can be implemented by a desktop shell or a toolkit.
Settings are out of scope of this proposal. Input methods can directly use GSettings or whatever.