Extending Hildon Input Methods

Overview

This document has been reviewed for maemo 4.0.

Maemo platform is intended to be used on embedded devices. It is a quite straightforward request that one might want to have different input methods from the ones available by default, or just simply want a different layout for the virtual keyboard. For this reason, maemo 4.0 introduces a way to enable writing custom plug-ins for Hildon Input Method.

This document describes writing a simple plug-in for Hildon Input Method. This example plug-in is going to implement a very basic virtual keyboard.

The illustration below shows what area of the Hildon Input Method can be redefined with the custom plug-in.

HIM user interface

Illustration 1 Layout of the main UI and the common buttons

Technically the plug-in is an almost standard GTK widget (with additional steps to support dynamic loading). The widget of the plug-in will be placed in the Plug-in area.

The illustration below shows the data flow of a user's input:

HIM components

Illustration 2 Components involved in input method

User inputs directly to the plug-in, then the plug-in sends the inputted data to HIM main user interface. The HIM main UI will interact with the IM context, and then commit the inputted text to the client widget.

In case of a custom plug-in, the plug-in itself - i.e. its writer - is responsible for handling all the inputs (even the buttons that are part of HIM main UI, e.g. tab or enter) and propagate them to the right modules (e.g. IM context).

The function and outlook of the buttons in HIM main UI can be customized, but one cannot remove them completely from the UI - only dim them (see handwriting plug-in), and they cannot be rearranged (for further information, see section Common buttons).

Plug-in Features

Hildon Input Method plug-in must be a GTK widget. In addition to the GTK widget interface, the plug-in must implement certain functions.

Interface

As already mentioned, the plug-in must handle all the inputs - both real user input and management signals from the system - that are coming to the HIM main UI. The first step is to take a look at which functions need to be implemented and provided by the plug-in to the Hildon Input Method Plug-in Interface. Later on, it will be shown how these functions are actually registered for the HIM Plug-in Interface.

The essential functions that must be implemented by a basic plug-in:

  • void (*enable) (HildonIMPlugin *plugin, gboolean init);

    enable is called whenever the plug-in becomes available to the user. init holds TRUE whenever this is the initialization time.

    /* Called when the plug-in is available to the user */
    static void
    enable (HildonIMPlugin *plugin, gboolean init)
    {
      HimExampleVKBPrivate *priv;
      HimExampleVKB *vkb;
    
      vkb = HIMEXAMPLE_VKB (plugin);
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (vkb);
      if (init == TRUE) 
      {
        hildon_im_ui_button_set_toggle (priv->ui, 
          HILDON_IM_BUTTON_MODE_A, TRUE);
        hildon_im_ui_button_set_toggle (priv->ui, 
          HILDON_IM_BUTTON_MODE_B, TRUE);
        hildon_im_ui_button_set_label (priv->ui, 
          HILDON_IM_BUTTON_MODE_A, "ABC");
        hildon_im_ui_button_set_label (priv->ui, 
          HILDON_IM_BUTTON_MODE_B, "Shift");
      }
    
      hildon_im_ui_send_communication_message(priv->ui,
                                              HILDON_IM_CONTEXT_DIRECT_MODE);
    }
    
  • void (*disable) (HildonIMPlugin *plugin)

    disable is called whenever the plug-in becomes unavailable to the user (e.g. when the main UI is closed).

    
    /* Called when the plug-in is 'disappear' */
    static void
    disable(HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*settings_changed) (HildonIMPlugin *plugin, const gchar *key, const GConfValue *value);

    settings_changed is called whenever the HIM main UI receives a notification from GConf about Hildon Input Method settings being changed. The affected settings are all settings residing in /apps/osso/inputmethod path. key and value hold the GConf key and its value respectively.

    /* Called when the standard input method settings 
     * has been changed */
    static void
    settings_changed (HildonIMPlugin *plugin,
        const gchar *key, const GConfValue *value)
    {
      /* not implemented */
    }
    
  • void (*input_mode_changed) (HildonIMPlugin *plugin);

    input_mode_changed is called whenever the input mode is changed. Input mode is changed to what has been specified by the client widget. The input mode puts constraints to the plug-in to limit whether input shall be accepted or ignored.

    /* Called when input mode changed */
    static void
    input_mode_changed (HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*clear) (HildonIMPlugin *plugin);

    clear is called whenever the HIM main UI requests the plug-in to clear or refresh its user interface.

    
    /* Called when the plug-in is requested to 'clear'/refresh its UI */
    static void
    clear(HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*client_widget_changed) (HildonIMPlugin *plugin);

    client_widget_changed is called whenever the client widget is changed from one to another. For instance, the case could be that the user taps on another text entry.

    /* Called when the client widget changed */
    static void
    client_widget_changed (HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*save_data) (HildonIMPlugin *plugin);

    save_data is called whenever the HIM main UI is requested to save its (and the plug-in's) data. Usually it is called when the main UI is requested to quit.

    /* Called when the plug-in is requested to save its data */
    static void
    save_data(HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*mode_a) (HildonIMPlugin *plugin);

    mode_a is called whenever the Mode A (Caps Lock in virtual keyboard plug-in) is pressed.

    /* Called when the MODE_A button is pressed */
    static void
    mode_a(HildonIMPlugin *plugin)
    {
      HimExampleVKBPrivate *priv;
      HimExampleVKB *vkb;
    
      vkb = HIMEXAMPLE_VKB (plugin);
    
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (vkb);
      if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_B)) {
        hildon_im_ui_button_set_active (priv->ui,
            HILDON_IM_BUTTON_MODE_B, FALSE);
        if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_A)) {
          priv->case_mode = CASE_UPPER;
        } else {
          priv->case_mode = CASE_LOWER;
        }
      } else {
        if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_A)) {
          priv->case_mode = CASE_UPPER;
        } else {
          priv->case_mode = CASE_LOWER;
        }
      }
      
      update_layout (vkb);
    }
    
  • void (*mode_b) (HildonIMPlugin *plugin);

    mode_b is called whenever the Mode B (Shift in virtual keyboard plug-in) is pressed.

    /* Called when the MODE_B button is pressed */
    static void
    mode_b(HildonIMPlugin *plugin)
    {
      HimExampleVKBPrivate *priv;
      HimExampleVKB *vkb;
    
      vkb = HIMEXAMPLE_VKB (plugin);
    
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (vkb);
    
      if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_B)) {
        if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_A)) {
          priv->case_mode = CASE_LOWER;
        } else {
          priv->case_mode = CASE_UPPER;
        }
      } else {
        if (hildon_im_ui_button_get_active (priv->ui,
            HILDON_IM_BUTTON_MODE_A)) {
          priv->case_mode = CASE_UPPER;
        } else {
          priv->case_mode = CASE_LOWER;
        }
      }
    
      update_layout (vkb);
    }
    
  • void (*backspace) (HildonIMPlugin *plugin);

    backspace is called whenever the virtual backspace key is pressed.

    /* Called when the backspace button is pressed */
    static void
    backspace (HildonIMPlugin *plugin)
    {
      HimExampleVKBPrivate *priv;
    
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (HIMEXAMPLE_VKB (plugin));
      hildon_im_ui_send_communication_message (priv->ui, 
          HILDON_IM_CONTEXT_HANDLE_BACKSPACE);
    }
    
  • void (*enter) (HildonIMPlugin *plugin);

    enter is called whenever the virtual enter key is pressed.

    /* Called when the enter button is pressed */
    static void
    enter (HildonIMPlugin *plugin)
    {
      HimExampleVKBPrivate *priv;
    
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (HIMEXAMPLE_VKB (plugin));
      hildon_im_ui_send_communication_message (priv->ui, 
          HILDON_IM_CONTEXT_HANDLE_ENTER);
    }
    
  • void (*tab) (HildonIMPlugin *plugin);

    tab is called whenever the virtual tab key is pressed.

    /* Called when the tab button is pressed */
    static void
    tab (HildonIMPlugin *plugin)
    {
      HimExampleVKBPrivate *priv;
    
      priv = HIMEXAMPLE_VKB_GET_PRIVATE (HIMEXAMPLE_VKB (plugin));
      hildon_im_ui_send_communication_message (priv->ui, 
          HILDON_IM_CONTEXT_HANDLE_TAB);
    }
    

Couple of functions related to changing language:

  • void (*language) (HildonIMPlugin *plugin);

    language is called whenever a new language is selected from the HIM main UI menu.

    /* Called when the language has been changed */
    static void
    language (HildonIMPlugin *plugin)
    {
      /* not implemented */
    }
    
  • void (*language_settings_changed) (HildonIMPlugin *plugin, gint index);

    language_settings_changed is called whenever the language settings for the specified language index have been changed.

Plug-in Loading

A nature of a plug-in is that it can be dynamically loaded. In this case also, the HIM plug-in is loaded whenever the user selects it, so in order to support the dynamic loading, the plug-in has to provide the following three specific functions for Hildon Input Method plug-in system:

  • void module_init (GTypeModule *module);

    This function initializes the plug-in as a module, meaning the type of GTypeInfo needs to be registered, and the interface and instance information (GInterfaceInfo) need to be added to the module.

    void
    module_init(GTypeModule *module)
    {
      static const GTypeInfo type_info = {
        sizeof(HimExampleVKBClass),
        NULL, /* base_init */
        NULL, /* base_finalize */
        (GClassInitFunc) himExample_vkb_class_init,
        NULL, /* class_finalize */
        NULL, /* class_data */
        sizeof(HimExampleVKB),
        0, /* n_preallocs */
        (GInstanceInitFunc) himExample_vkb_init,
      };
      
      static const GInterfaceInfo plugin_info = {
        (GInterfaceInitFunc) himExample_vkb_iface_init,
        NULL, /* interface_finalize */
        NULL, /* interface_data */
      };
    
      himExample_vkb_type =
              g_type_module_register_type(module,
                                          GTK_TYPE_WIDGET, "HimExampleVKB",
                                          &type_info,
                                          0);
      
      g_type_module_add_interface(module,
                                  HIMEXAMPLE_VKB_TYPE,
                                  HILDON_IM_TYPE_PLUGIN,
                                  &plugin_info);
    }
    

    The himExample_vkb_iface_init function should register the custom interface functions in HildonIMPluginIface:

    /* Standard GTK stuff */
    static void
    himExample_vkb_iface_init (HildonIMPluginIface *iface)
    {
      iface->enable = enable;
      iface->disable = disable;
      iface->enter = enter;
      iface->tab = tab;
      iface->backspace = backspace;
      iface->clear = clear;
      iface->input_mode_changed = input_mode_changed;
      iface->client_widget_changed = client_widget_changed;
      iface->save_data = save_data;
      iface->language = language;
      iface->mode_a = mode_a;
      iface->mode_b = mode_b;
      iface->language_settings_changed = language_settings_changed;
      iface->settings_changed = settings_changed;
    
      return;
    }
    
  • void module_exit (void);

    This function defines actions when the module is unloaded from the memory.

    void
    module_exit(void)
    {
      /* empty */
    }
    
  • HildonIMPlugin* module_create (HildonIMUI *ui);

    This function creates and returns the plug-in widget to the main UI.

    HildonIMPlugin* 
    module_create (HildonIMUI *ui)
    {
      return HILDON_IM_PLUGIN (himExample_vkb_new (keyboard));
    }
    

Plug-in Info

Some basic information of the plug-in needs to be provided to the HIM plug-in system, so it can keep up with the kinds of plug-ins that are available in the system. This is performed with two specific functions:

  • const HildonIMPluginInfo* hildon_im_plugin_get_info(void);

    The function creates HildonIMPluginInfo struct for providing the required information.

    /* Input Method plug-in information.
     * This structure tells the main UI about this plug-in */
    const HildonIMPluginInfo *
    hildon_im_plugin_get_info(void)
    { 
      static const HildonIMPluginInfo info =
      {
        "HIM VKB Example",                  /* description */
        "himExample_vkb",                   /* name */
        "Keyboard (EXAMPLE)",               /* menu title */
        NULL,                               /* gettext domain */
        TRUE,                               /* visible in menu */
        FALSE,                              /* cached */
        HILDON_IM_TYPE_DEFAULT,             /* UI type */
        HILDON_IM_GROUP_LATIN,              /* group */
        HILDON_IM_DEFAULT_PLUGIN_PRIORITY,  /* priority */
        NULL,                               /* special character plugin */
        "",                                 /* help page */
        FALSE,                              /* disable common UI buttons */
        HILDON_IM_DEFAULT_HEIGHT,           /* plugin height */
        HILDON_IM_TRIGGER_STYLUS            /* trigger */
      };
    
      return &info;
    }
    
  • gchar** hildon_im_plugin_get_available_languages (gboolean *free)

    The function returns a NULL terminated array of string, containing language codes supported by the plug-in. Set free to TRUE if HIM main UI should free the returned value when no longer used.

    /*
     * This function returns the list of available languages supported
     * by the plug-in.
     * */
    gchar ** 
    hildon_im_plugin_get_available_languages (gboolean *free)
    {
      static gchar *list[] = { "en_GB", NULL };
      *free = FALSE;
    
      return list;
    }
    

Interaction with Main User Interface

As mentioned above, the plug-in is placed inside the HIM main UI. This section deals with interacting with it. This is mainly done by calling its functions, as defined in hildon-im-ui.h.

Handling Input

void hildon_im_ui_send_utf8(HildonIMUI *main_ui, const gchar *text);

The plug-in can request the main UI to commit a UTF-8 encoded text by calling this function.

void hildon_im_ui_send_communication_message(HildonIMUI *main_ui, gint message);

The plug-in can use this function to tell the main UI that Enter, Backspace, or Tab virtual buttons are pressed. Simply call this function and pass one of the constants below as the message argument:

  • HILDON_IM_CONTEXT_HANDLE_ENTER
  • HILDON_IM_CONTEXT_HANDLE_BACKSPACE
  • HILDON_IM_CONTEXT_HANDLE_TAB

UI Visibility

void hildon_im_ui_set_visible(HildonIMUI *ui, gboolean visible);

The plug-in can request the main UI to set it's visibility by calling this function.

gboolean hildon_im_ui_get_visibility(HildonIMUI *main_ui);

By calling this function, the plug-in can get the visibility status of the main UI.

Get Input Method State

The following two state reader functions could be very handy in case the plug-in receives a state change notification (e.g. language change), because this way all the state information does not need to be saved.

HildonIMCommand hildon_im_ui_get_autocase_mode(HildonIMUI *main_ui);

The function returns the auto-capitalization mode of the current client widget.

const gchar * hildon_im_ui_get_active_language(HildonIMUI *main_ui);

The function returns the current language code.

Common buttons

As mentioned in the Overview section, the outlook and even the function of the buttons in the main UI can be modified. N.B. It is not recommended to alter buttons, except the Mode A and Mode B buttons! Other buttons may have hardwired behavior within the main UI.

If the plug-in changes the functionality of a button, one might want to reflect this also in the UI by changing the label of the button. The layout of the buttons can be altered by the following two functions:

  • void hildon_im_ui_button_set_label(HildonIMUI *keyboard, gint button_enum, const gchar *label);

    With this function, the plug-in can set the label of a button.

    Possible values of button_enum (see hildon-im-ui.h):

    • HILDON_IM_BUTTON_TAB
    • HILDON_IM_BUTTON_MODE_A
    • HILDON_IM_BUTTON_MODE_B
    • HILDON_IM_BUTTON_INPUT_MENU
    • HILDON_IM_BUTTON_BACKSPACE
    • HILDON_IM_BUTTON_ENTER
    • HILDON_IM_BUTTON_SPECIAL_CHAR
    • BUTTON_CLOSE
  • void hildon_im_ui_button_set_id(HildonIMUI *self, gint button_enum, const gchar *id);

    This function sets a name to a particular button.

Since every input to the HIM main UI is caught by the plug-in, it is necessary to keep the button state (active or in-active) in sync. The state of a particular button can be changed, queried and toggled with the following functions:

  • void hildon_im_ui_button_set_active(HildonIMUI *keyboard, gint button_enum, gboolean active);

    This function sets the active state of a particular button.

  • gboolean hildon_im_ui_button_get_active(HildonIMUI *keyboard, gint button_enum);

    This function returns the active state of a particular button.

  • void hildon_im_ui_button_set_toggle(HildonIMUI *keyboard, gint button_enum, gboolean toggle);

    The plug-in can set the toggle state of a particular button with this function.

Miscellaneous button manipulation functions:

  • void hildon_im_ui_button_set_menu(HildonIMUI *keyboard, gint button_enum, GtkWidget *menu);

    With this function, the plug-in can attach a menu - which is a GtkWidget - to a particular button.

  • void hildon_im_ui_button_set_sensitive(HildonIMUI *keyboard, gint button_enum, gboolean sensitive);

    All the buttons defined on the HIM main UI may not be needed, or the functionality of a button may be wished to be switched off in some states. In this case, the sensitivity of a particular button can be set by calling this function.

  • void hildon_im_ui_button_set_repeat(HildonIMUI *keyboard, gint button_enum, gboolean repeat);

    This function controls whether a particular button will repeat when pressed for a long time.

Component Dependencies

At least the following headers shall be included in the plug-in:

#include <hildon-im-plugin.h>
#include <hildon-im-ui.h>

hildon-input-method-framework-dev and libhildon-im-ui-dev packages.

Language Codes

These are the language codes recognized; they are numbered, the first, af_ZA, being 0.

af_ZA am_ET ar_AE ar_BH ar_DZ ar_EG ar_IN ar_IQ ar_JO ar_KW
ar_LB ar_LY ar_MA ar_OM ar_QA ar_SA ar_SD ar_SY ar_TN ar_YE 
az_AZ be_BY bg_BG bn_IN br_FR bs_BA ca_ES cs_CZ cy_GB da_DK 
de_AT de_BE de_CH de_DE de_LU el_GR en_AU en_BW en_CA en_DK 
en_GB en_HK en_IE en_IN en_NZ en_PH en_SG en_US en_ZA en_ZW 
eo_EO es_AR es_BO es_CL es_CO es_CR es_DO es_EC es_ES es_GT 
es_HN es_MX es_NI es_PA es_PE es_PR es_PY es_SV es_US es_UY 
es_VE et_EE eu_ES fa_IR fi_FI fo_FO fr_BE fr_CA fr_CH fr_FR 
fr_LU ga_IE gd_GB gl_ES gv_GB he_IL hi_IN hr_HR hu_HU hy_AM 
id_ID is_IS it_CH it_IT iw_IL ja_JP ka_GE kl_GL ko_KR kw_GB 
lt_LT lv_LV mi_NZ mk_MK mr_IN ms_MY mt_MT nl_BE nl_NL nn_NO 
no_NO oc_FR pl_PL pt_BR pt_PT ro_RO ru_RU ru_UA se_NO sk_SK 
sl_SI sq_AL sr_YU sv_FI sv_SE ta_IN te_IN tg_TJ th_TH ti_ER 
ti_ET tl_PH tr_TR tt_RU uk_UA ur_PK uz_UZ vi_VN wa_BE yi_US 
zh_CN zh_HK zh_SG zh_TW

     

Code Examples

The example Hildon Input Method plug-in can be retrieved by running

apt-get source hildon-input-method-plugins-example
inside Chinook SDK.



Improve this page