D-Bus Activatable Input Method

Daiki Ueno


    
  

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

Introduction
Installation
Activation
Messaging
Note

Introduction

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:

  1. Installation: Manage addition/removal/listing of input method packages.

  2. Activation: Maintain "active" input methods.

  3. 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.

Installation

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.

Activation

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.

Messaging

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:

Table 1. Purposes

FlagValue
PURPOSE_FREE_FORM0
PURPOSE_ALPHA1
PURPOSE_DIGITS2
PURPOSE_NUMBER3
PURPOSE_PHONE4
PURPOSE_URL5
PURPOSE_EMAIL6
PURPOSE_NAME7
PURPOSE_PASSWORD8
PURPOSE_PIN9

Possible hints are defined as follows. Multiple hints can be set in a bitmask, as a bitwise OR.

Table 2. Hints

FlagValue
HINT_NONE0
HINT_SPELLCHECK1
HINT_NO_SPELLCHECK2
HINT_WORD_COMPLETION4
HINT_LOWERCASE8
HINT_UPPERCASE_CHARS16
HINT_UPPERCASE_WORDS32
HINT_UPPERCASE_SENTENCES64
HINT_INHIBIT_OSK128

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:

Table 3. Styling Types

FlagValue
STYLING_NORMAL0
STYLING_UNDERLINE1
STYLING_SELECTED2
STYLING_SECONDARY_SELECTED3

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.

Note

  • 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.