Documentation Archive Developer
Search

Frequently Asked Text Services Manager (TSM) Questions

This Technote covers the following Frequently Asked Text Services Manager (TSM) Questions by Input Method Developers for Tiger

Q: How can I get my input method palettes to show up above a Dashboard widget?

A: Windows in Dashboard and Spotlight have high window levels (typically set to level 100) in order to be visible above all application UI. When Dashboard or Spotlight have key focus, they will set the kTSMDocumentWindowLevelPropertyTag property of the active TSMDocument to their window level. If the input method wishes to position its windows above this window level in order to be visible to users and assist with typing, it should increment the window level returned by the TSMGetDocumentProperty API for the property kTSMDocumentWindowLevelPropertyTag. This property returns the window level above which the input method should set its palette window levels. The input method should query this property every time its windows/palettes are about to be shown.

To adjust its window levels, the IM should create a window group (see MacWindows.h) to include its window (for example, the candidate window for inline input), and set the window group's level via Window Mgr API.

Back to Top 

Q: Are there other issues when using input methods in Dashboard widgets that I need to be aware of?

A: There are two issues that you should be aware of in Tiger when using Input Methods in the Dashboard environment.

First, when the user initiates an inline input session in a Dashboard widget text field and leaves it unconfirmed before switching out of Dashboard, the inline input session will be commited in the widget. The next time the Dashboard widget receives key focus, the user will need to reinitiate the inline session, or reconvert the confirmed text into an inline session in the same way as happens when an inline session is accidentally confirmed. There are no input method workarounds for this issue.

Second, when using an input method in a Dashboard widget, if the user clicks in a popup menu in one of your input method palettes, and then releases the menu, key focus does not return to the Dashboard widget. Instead it returns to the previous frontmost application, i.e. TextEdit, Finder, etc... there are no known workarounds in the input method that address this issue.

Back to Top 

Q: How should I modify my input method's messaging to my server to contribute to system stability?

A: Input Methods that communicate with their (UI) server need to be careful that arbitrary runLoop sources do not fire while the input method client is waiting on a reply, and to not use synchronous RPC requests (via unnecessary message timeouts) for messages that should be asynchronous.

Some input methods using CFMessagePort for their messaging have used the kCFRunLoopDefaultMode. This runLoop mode will allow any runLoop source to fire while an input method (component) client awaits a reply from a server. This has the undesirable effect of triggering re-entrant calls into TSM while the input method awaits its reply. For example, an event can be received by the app that needs to be processed by TSM. If this happens while the input method is waiting for a reply, the result is a re-entrant call into TSM and potentially into the input method. This runLoop mode also allows other parts of the System to call into TSM. The solution is to use a more restrictive runLoop mode. While it may seem tempting to use something like kCFRunLoopCommonModes, input methods are strongly advised to create their own private runLoop modes (strings), add their runLoop sources to the runLoop in that private mode, and run their runLoop in that mode when waiting for a reply. This is done implicitly when you specify your private mode as the replyMode in CFMessagePortSendRequest.

Also, when sending messages to a server via CFMessagePort for which no reply is desired, the input method specifies a NULL replyMode to CFMessagePortSendRequest. In addition to this, the sendTimeout/rcvTimeout should also be zero for a true asynchronous RPC. A non-zero timeout on a send-only RPC will cause the kernel to block on message delivery (up to the timeout value), so you should be prepared to resend the message at a later time if delivery fails with a zero timeout.

Back to Top 

Q: How do I write a palette-class input method?

A: You need to do the following:

Use the 'cplt' for your component sub-type (see kCharacterPaletteInputMethodClass in TextServices.h.) Note that your text service need not necessarily be a character palette. It can be any text service that operates independently of the current keyboard input source, and that you want instantiated in the context of every application. It may even be a text service with no UI at all (see topic "How do I make my palette-class input method invisible in System UI?") This technique has been useful for some input methods that need information only available in the context of the frontmost (or key focus) process.

When the user interacts with your User Interface (such as a floating palette presented by a UI server process), send messages to the instance of your input method that is currently active (i.e. in the process that currently has key focus). If these actions produce text, you need to pass kEventTextInputUnicodeText events (see CarbonEvents.h) to TSM's SendTextInputEvent API. TSM will first try to "chain" your kEventTextInputUnicodeText event into the current keyboard input method, via its TextServiceEventRef component call. If there is no input method, or it doesn't support the kEventTextInputUnicodeText event, TSM will convert your event to a kEventTextInputUnicodeForKeyEvent and dispatch it. Some keyboard input methods that support this event may insert your text into an existing inline input session, while others will first commit the user's inline input session in the application so that the text from your palette input method can safely be inserted into a document.

Back to Top 

Q: How do I make my palette-class input method invisible in System UI?

A: Making your input method invisible to System UI can be useful when your (palette) input method provides a service specific to an application or a set of applications that know how to discover, enable, and select your service. This technique is also useful for input methods that need information only available in the context of the frontmost (or key focus) process. To make your input method invisible, specify a boolean value of 'true' for the "ComponentInvisibleInSystemUI" key in your component's Info.plist dictionary. See kComponentBundleInvisibleInSystemUIKey in TextServices.h.

Back to Top 

Q: Do I still need to define my icons in my component's resource fork?

A: No, in fact we now discourage input method developers from keeping any data (other than that required by Component Manager) in your component's resource fork, and may deprecate support for these resources in the future. You can now specify the "tsInputMethodIconFileKey" key in your component's Info.plist dictionary. The key's value specifies a partial URL relative the your component's Resources/ directory that points to an icon file, either a TIFF (.tif) or .icns icon file. An .icns is preferrable because TSM automatically registers your icon with Icon Services registry.

Back to Top 

Q: How do I make my keyboard input method input-mode savvy?

A: Your input method should become input-mode savvy if it provides multiple ways of processing keyboard text input, such as translation of key events via different keyboard layouts, different kinds of transliteration, and/or multiple kinds of more complex processing like Kotoeri's Hiragana input or the Traditional Chinese input method's Hanin input mode. Instead of presenting these different input modes in your own UI, your input method should let the System do this for you. This integrates your input method's principal features into a consistent system-provided UI, alongside other input modes and keyboard layouts enabled by the user. The user can then select your input modes from the Text Input menu, and can enable them independently of one another via the International Preferences pane in System Preferences.

To become mode-savvy, your input method must:

1. Add a new dictionary in its Info.plist file for the ComponentInputModeDict key

Your Info.plist must specify the "ComponentInputModeDict" key. The value for the "ComponentInputModeDict" key is a dictionary that must define 2 keys: "tsInputModeListKey" whose value is a dictionary that defines all your input modes, and "tsVisibleInputModeOrderedArrayKey" whose value is an array of input mode strings. This array defines how your input modes are ordered relative to one another in System provided UI. See kComponentBundleInputModeDictKey, kTSInputModeListKey, and kTSVisibleInputModeOrderedArrayKey in TextServices.h.

The following sample would describe a mode-savvy input method, called "Special IM". It uses some TSM provided (standard) input modes for Japanese and Roman.

All icon files reside in the component's Resources/ directory. These icon files can be TIFF (.tif) or .icns icons. .icns icons are preferrable because TSM automatically register them with Icon Services registry. The UI names for the input modes reside in InfoPlist.strings files for the various localizations. A sample for these is shown below.

Some notes on the various input mode properties.

Required properties:

Input Modes are assigned a Mac ScriptCode, such as smJapanese (see tsInputModeScriptKey).

If the input method defines several input modes in the same script, at most one of these should be marked as the "primary" input mode for that script. See tsInputModePrimaryInScriptKey.

Each input mode should define an icon to be used in the TextInput menu to show when the input mode is selected.

An "alternate" icon should also be defined for use when the user clicks the icon in the menu bar. See tsInputModeMenuIconFileKey and tsInputModeAlternateMenuIconFileKey.

Input Modes can be enabled by default when the input method itself is enabled in System Preferences/International Prefs pane. This is specified via tsInputModeDefaultStateKey. Those input modes for which this key is 'false' are not automatically "checked" in International Preferences.

Optional properties:

An Input mode can define a shortcut sequence via tsInputModeKeyEquivalentKey and tsInputModeKeyEquivalentModifiersKey, such as Ctl-Shift J for "Special Hiragana" below.

Private Input Modes can be defined. It is not necessary to restrict input mode keys to the list in TextServices.h. However, when an input mode provides substantially the same functionality as one would expect from a pre-defined input mode, the TSM input mode should be used instead, such as com.apple.inputmethod.Roman for Roman input or com.apple.inputmethod.Japanese for Hiragana input.

Info.plist dictionary for a Japanese input method called "Special IM":

Listing 1: Info.plist dictionary.

<key>ComponentInputModeDict</key>
<dict>
  <key>tsInputModeListKey</key>
  <dict>
    <key>com.apple.inputmethod.Japanese</key>
    <dict>
      <key>tsInputModeDefaultStateKey</key>
      <true/>
      <key>tsInputModeIsVisibleKey</key>
      <true/>
      <key>tsInputModeKeyEquivalentKey</key>
      <string>J</string>
      <key>tsInputModeKeyEquivalentModifiersKey</key>
      <integer>4608</integer>  // Ctl-Shift modifiers
      <key>tsInputModeMenuIconFileKey</key>
      <string>Hiragana.tif</string>
      <key>tsInputModeAlternateMenuIconFileKey</key>
      <string>HiraganaSelected.tif</string>
      <key>tsInputModeScriptKey</key>
      <string>smJapanese</string>
      <key>tsInputModePrimaryInScriptKey</key>
      <true/>
    </dict>
    <key>com.apple.inputmethod.Japanese.Katakana</key>
    <dict>
      <key>tsInputModeDefaultStateKey</key>
      <true/>
      <key>tsInputModeIsVisibleKey</key>
      <true/>
      <key>tsInputModeMenuIconFileKey</key>
      <string>Katakana.tif</string>
      <key>tsInputModeAlternateMenuIconFileKey</key>
      <string>KatakanaSelected.tif</string>
      <key>tsInputModeScriptKey</key>
      <string>smJapanese</string>
      <key>tsInputModePrimaryInScriptKey</key>
      <false/>
    </dict>
    <key>com.myDomain.SpecialIM.SpecialInputMode</key>
    <dict>
      <key>tsInputModeDefaultStateKey</key>
      <true/>
      <key>tsInputModeIsVisibleKey</key>
      <true/>
      <key>tsInputModePrimaryInScriptKey</key>
      <false/>
      <key>tsInputModeScriptKey</key>
      <string>smJapanese</string>
    </dict>
    <key>com.apple.inputmethod.Roman</key>
    <dict>
      <key>tsInputModeDefaultStateKey</key>
      <false/>
      <key>tsInputModeIsVisibleKey</key>
      <true/>
      <key>tsInputModeMenuIconFileKey</key>
      <string>Roman.tif</string>
      <key>tsInputModeAlternateMenuIconFileKey</key>
      <string>RomanSelected.tif</string>
      <key>tsInputModeScriptKey</key>
      <string>smRoman</string>
      <key>tsInputModePrimaryInScriptKey</key>
      <true/>
    </dict>
  </dict>
  <key>tsVisibleInputModeOrderedArrayKey</key>
  <array>
    <string>com.apple.inputmethod.Japanese</string>
    <string>com.apple.inputmethod.Japanese.Katakana</string>
    <string>com.myDomain.SpecialIM.SpecialInputMode</string>
    <string>com.apple.inputmethod.Roman</string>
  </array>
</dict>
<key>tsInputMethodIconFileKey</key>
<string>SpecialIM.tif</string>

The following InfoPlist.strings files provide the localization for the above input modes for Japanese and English:

Listing 2: Japanese.lproj/InfoPlist.strings.

CFBundleName = "スペーシャル IM";
com.apple.inputmethod.Roman = "スペーシャル 英字";
com.apple.inputmethod.Japanese = "スペーシャル ひらがな";
com.myDomain.SpecialIM.SpecialInputMode = "スペーシャル モード";
com.apple.inputmethod.Japanese.Katakana = "スペーシャル カタカナ";

Listing 3: English.lproj/InfoPlist.strings.

CFBundleName = "Special IM";
com.apple.inputmethod.Roman = "Special Romaji";
com.apple.inputmethod.Japanese = "Special Hiragana";
com.myDomain.SpecialIM.SpecialInputMode = "Special InputMode";
com.apple.inputmethod.Japanese.Katakana = "Special Katakana";

Back to Top 

2. Implement the CopyTextServiceInputModeList component call

The CopyTextServiceInputModeList component call must be implemented for input mode-savvy input methods. The input method essentially returns an up-to-date copy of the kComponentBundleInputModeDictKey dictionary found in the component's Info.plist. This call is made whenever an input mode from your input method is selected. It allows your input method to dynamically reassign the shortcuts desired for your input modes, in case they differ from the shortcuts that are hard coded in your Info.plist. This mechanism also allows the input method to let the user customize their desired shortcuts for your input modes.

Back to Top 

3. Implement kTextServiceInputModePropertyTag property of the SetTextServiceProperty component call

When one of your input modes is selected from the Text Input menu or programmatically, TSM will make the SetTextServiceProperty component call, specifying kTextServiceInputModePropertyTag as the property tag, and a CFStringRef containing your input mode specifier as the property value. See TextServices.h for details.

Back to Top 

4. Call TSMSelectInputMode to confirm the selection of an input mode when the System requests you switch to the specified input mode

When TSM requests a particular input mode be selected, the input method makes the final decision as to whether to allow the selection. To commit the selection or to alter it, the input method must call the TSMSelectInputMode API.

Note that TSMSelectInputMode takes a Component ID, not a ComponentInstance.

Back to Top 

5. Follow the recommendations of these Do's and Dont's for Input Modes

  • Do use pre-existing input mode specifiers defined in TextServices.h such as com.apple.inputmethod.Roman, com.apple.inputmethod.Japanese, etc...

  • Don't set multiple "primary in script" input modes. Define at most 1 input mode as primary for any given script.

Back to Top 

Q: My input method uses a private keyboard layout. How do I make it invisible to the System?

A: If you install a private keyboard layout in one of the bundles in .../Library/Keyboard Layouts/, you can specify the "TSMInvisibleKey" dictionary key (value is Boolean) in the KLInfo_<name> dictionary describing for your keyboard layout.

Back to Top 

Document Revision History

DateNotes
2005-06-24Regroups a collection of Text Services Manager (TSM) questions frequently asked by Input Method Developers