The dynamic input area is simply a window that displays the user interface of a separate executable called a pinlet. A pinlet receives user input in the form of pen taps or pen strokes and converts that input into character data that an application can use.
Pinlets come in two general types. Keyboard pinlets offer a user interface where users tap the characters they want to enter. Handwriting recognition pinlets interpret pen strokes as handwriting. Handwriting recognition pinlets typically use a handwriting recognition engine. You may use the provided Graffiti® 2 engine or replace it with one of your own.
A single device may have more than one pinlet installed on it, and users may control which pinlet they want to use.
This chapter provides guidelines for implementing pinlets. It covers:
How Pinlets Work
Starting Up and Shutting Down a Pinlet
Presenting a User Interface
Interpreting Pen Strokes
Specifying the Default Pinlet
How Pinlets Work
In most cases, pinlets interact only with the Pen Input Manager. The Pen Input Manager provides a window into which the pinlet draws its user interface. Through this window, the pinlet receives all pen events. The pinlet converts the pen events to character data and passes that back to the Pen Input Manager. Handwriting recognition pinlets may also use a handwriting recognition engine to interpret pen strokes. (See Figure 3.1.)
Figure 3.1 Flow of data through pinlets

Some systems use a Front-End Processor (FEP) to convert characters. For example, many existing Japanese devices use handwriting recognition engines to convert pen strokes to Romaji (the Roman alphabet). On these devices, text fields send sequences of characters to the FEP, which converts the characters to Hiragana or Katakana characters. The user can perform a further translation from these alphabets into Kanji. The character recognition performed by the Pen Input Manager is completely separate from the FEP. If you were to add the Pen Input Manager and pinlets to a device with a FEP, the Pen Input Manager still sends its events to Palm OS®, and the text field code in Palm OS sends those characters to the FEP (see Figure 3.2).
Figure 3.2 Flow of data with a FEP

In rare circumstances, a pinlet depends so much on a FEP that it should only be made active if that FEP is active. For this reason, the pinlet has a FEP class attribute to specify which FEPs the pinlet works with. If the FEP class attribute is defined, the status bar does not advertise the pinlet as available unless it works with a FEP that is currently enabled.
A pinlet represents a single means of receiving pen input, and only one pinlet is active at a time. Writing characters is one means of receiving input that is controlled by one pinlet. Tapping characters on an on-screen keyboard is another means of receiving input and thus is controlled by another pinlet. You may have more than one means of receiving input within one pinlet if one pinlet can use multiple FEPs, but in all other cases, you should design one pinlet per character input system.
Building Pinlets and Handwriting Recognition Engines
To build a pinlet, create a shared library of type 'pnlt'
(defined by the constant sysFileTPinletApp
).
A handwriting recognition engine is also a shared library. If your pinlet is a handwriting recognition pinlet, link it with the engine you want to use. If you want to use the Graffiti 2 engine, link with the Graffiti2Lib
library, which is included in the SDK.
If you want to replace the Graffiti 2 engine with your own, create a shared library project with type 'libr'
for the engine and export all of the functions described in Chapter 7, "Handwriting Recognition Engine." Then link your pinlet with this library.
You do not have to separate your handwriting recognition code from your pinlet. You can interpret strokes in the pinlet directly if you don't intend to reuse or share your handwriting recognition engine.
Starting Up and Shutting Down a Pinlet
Programmatically, a pinlet looks very similar to a Palm OS application: it starts up by receiving and responding to launch codes, and it has an identical event loop that receives the same types of events that an application does.
Startup
The Pen Input Manager starts up a pinlet when the user selects that pinlet from the status bar or an application calls the PINSwitchToPinlet()
function.
To start up the pinlet, the Pen Input Manager sends launch codes to the pinlet's PilotMain()
function:
uint32_t PilotMain(uint16_tcmd
, void *cmdPBP
, uint16_tlaunchFlags
)
The pinlet must respond to the following launch codes:
-
sysPinletLaunchCmdLoadProcPtrs
- The Pen Input Manager calls pinlet functions to set and retrieve the input mode, display a help dialog, and clear the input state. The Pen Input Manager sends this launch code to retrieve pointers to those pinlet functions. The pinlet should return its function pointers in a
PinletAPIType
structure and pass it back as thecmdPBP
parameter. (The rest of this chapter describes how and when those functions are called.) -
sysAppLaunchCmdPinletLaunch
- The pinlet has become the active pinlet, and it should initialize itself.
When the pinlet is launched, it should:
- Initialize its state.
- Display its user interface.
- If it is a handwriting recognition pinlet, it should start up the engine it uses as described in "Starting up the Handwriting Recognition Engine."
- Start its event loop.
The event loop is identical to the event loop used in an application.
Starting up the Handwriting Recognition Engine
To start up the handwriting recognition engine, you call HWRInit()
and pass it a HWRConfig
structure. This structure identifies the following:
- The number of horizontal and vertical pixels per inch.
- The bounds of the area into which the user is allowed to draw strokes.
- The number and bounds of any special areas for special input modes, excluding the writing area for the normal input mode.
For example, the Graffiti 2 engine in normal mode translates strokes into lowercase letters. The virtual silkscreen pinlet has two writing areas: one for letters and one for numbers. It specifies only one mode area in its
HWRInit()
call because one of its writing areas is for the normal mode.
Note that much of this information is dependent on the size of the pinlet's form. In Palm OS Cobalt, you do not know a form's size until runtime when your pinlet receives the winResizedEvent
. Therefore, you may have to wait until you receive that event before you can start up the handwriting recognition engine. This is acceptable as long as you call HWRInit()
before you call any other handwriting function.
Shutdown
When the user switches to a new pinlet, the Pen Input Manager sends the appStopEvent
to your pinlet. In response to this event, the pinlet must shut down its event loop, clear all state, and exit. If your pinlet works with a handwriting recognition engine, you should call HWRShutdown()
to shut down the engine.
You don't have to worry that you're getting an appStopEvent
intended for some other application or process. The pinlet runs in its own thread and has its own event queue. It only receives events that are intended for it.
Presenting a User Interface
You create a pinlet's user interface in a resource file in the same way that you create a user interface for an application. The pinlet's user interface consists of one or more forms containing one or more user interface controls.
The resource file must also contain the following resources that are unique to pinlets:
Main Pinlet Form
Pinlet Style
Internal Pinlet Name
Status Bar Icons and Name
FEP Creator ID
Help Dialog
Input Mode Indicator
Main Pinlet Form
The main form for your pinlet must be update-based. That is, it must contain a WINDOW_CONSTRAINTS_RESOURCE
that does not have the back-buffer attribute set. Palm OS places the pinlet form in the appropriate window layer. If you try to specify the window layer or any other window creation attributes in the WINDOW_CONSTRAINTS_RESOURCE
, they are ignored.
Try to design this form so that a minimum and preferred size of 65 standard coordinates high or less works well. This allows the application to be 160 coordinates high, which is the height used by all legacy application windows. Users prefer to see as much of the application as possible, so your pinlet should be as small as possible while still being usable.
IMPORTANT: Pinlets cannot display other windows. This means that you cannot include a pop-up list in a pinlet because a pop-up list is a window.
Remember that in Palm OS Cobalt, the system controls the size of each window. You cannot guarantee that your pinlet is a specific size. The form's event handler must respond to the winResizedEvent
to find out what size it actually is, and you must only draw the pinlet's form in response to the frmUpdateEvent
.
NOTE: When the Pen Input Manager closes the input area, the pinlet receives a
winResizedEvent
specifying a size of 0.
Pinlet Style
The Pen Input Manager needs to know the style of the pinlet. This pinlet style is used as a possible return value to PINGetPinletInfo()
. Include in your pinlet's resource file a SOFT_CONSTANT_RESOURCE
with ID 1000. Use one of the constants described in "Pinlet Styles" for its value.
Internal Pinlet Name
The Pen Input Manager uses an internal pinlet name to identify each pinlet. Create a STRING_RESOURCE
of ID 1001 and specify a name of the form:
com.companyName
.pinlet.pinletName
where companyName
is the name of your organization and could be used for all pinlets that you write. pinletName
is the unique name for your pinlet.
This name is returned by the functions PINGetCurrentPinletName()
and PINGetPinletInfo()
and used as input to PINSwitchToPinlet()
. It is never shown externally, so there is no need to localize this name.
Status Bar Icons and Name
The status bar displays an icon and name for all pinlets installed on the device. If the user holds the pen down on the input area icon, a list of the names and icons of all pinlets are displayed. This is how the user switches between pinlets.
Use the APP_ICON_BITMAP_RESOURCE
resource type with ID 1001 to specify the status bar icon. This icon should be 15 standard coordinates wide by 11 coordinates high.
The external pinlet name is contained in an APP_ICON_NAME_RESOURCE
with ID 1000. Ideally, the width of this name is no more than 100 coordinates, but it can be as wide as the screen if necessary. Because the status bar displays this name, it is localizable.
FEP Creator ID
If the pinlet is associated with a FEP, you must supply an SOFT_CONSTANT_RESOURCE
with ID 1001 that gives the creator ID of the FEP.
Help Dialog
The function PINShowReferenceDialog()
calls PinletShowReferenceDialogProcPtr()
to display a help dialog that specifies how to enter characters.
Pinlets that use a handwriting recognition engine can call through to HWRShowReferenceDialog()
, which displays the help for you. If you're not using a handwriting recognition engine, you should supply a help dialog in your resource file.
In the WINDOW_CONSTRAINTS_RESOURCE
for the help dialog, specify winLayerPriority as the window layer. This ensures that the help dialog appears on top of the application form if necessary.
The Edit menu used in most Palm OS applications has a Graffiti 2 Help menu item that displays help for the Graffiti 2 engine only. If you use a different handwriting recognition engine and want to display help for it, include a control on the pinlet that does so.
IMPORTANT: Pinlets cannot display more than one window. To launch a help dialog, you must spawn a separate thread, give that thread a user interface context, and then display the dialog in that thread. See Exploring Palm OS: System Management for more information on multithreading.
Input Mode Indicator
The pinlet should display some visual indication of the current input mode. This indication is typically only given for unusual modes (anything other than normal). See "Considering the Input Modes" for more information.
Interpreting Pen Strokes
The pinlet receives pen events, translates the events into characters, and passes the characters to the Pen Input Manager. The pinlet must decide how to interpret the pen strokes. It should respect the input mode set by the user or application, and it might need to store internal state or set timers when interpreting the pen events. It should respond to application requests to clear that internal state. This section discusses these issues:
Receiving Pen Events
Sending Results to Pen Input Manager
Considering the Input Modes
Handling Multistroke Characters
Implementing Live Ink
Receiving Pen Events
Your pinlet has a form with its own event handler, just like an application has a form with an event handler. This event handler should check for and interpret user input. How it does so depends on the type of pinlet you are writing and how you've designed it. If you're doing a keyboard pinlet and each key is implemented as a button, you'll receive ctlSelectEvent
s
for each button the user taps.
If you are implementing a handwriting recognition pinlet, you should do something like the following:
- On
penDownEvent
, check to see if the pen is down within the writing area. If so, track the pen. - On
penMoveEvent
s, record the points sent in the event. - On the
penUpEvent
, callHWRProcessStroke()
, sending it the points you recorded upon eachpenMoveEvent
.The handwriting recognition engine returns a
HWRResult
containing characters, an input mode indication, an inking hint, and a Boolean that indicates if a timeout should be set. More details about each of these are given later in this chapter.
TIP: If you're using a gadget to track the pen, call
FrmSetPenTracking()
in the gadget's event handler in response to frmGadgetEnterEvent
. See the description of FormGadgetHandlerType()
for more information.
Sending Results to Pen Input Manager
To send the character input to the Pen Input Manager, use the call PINFeedChar()
or PINFeedString()
. You must supply UTF8 data to these functions.
If you are writing a handwriting recognition pinlet that uses the Graffiti 2 engine, that engine returns characters in the device's native encoding. Check the flags
field of each character that the engine has returned. If it is pinCharFlagVirtual, the character is a virtual character and you can pass that directly to PINFeedChar()
without conversion. If no modifiers are set, the character is textual data. Use TxtConvertEncoding()
to convert it to UTF8.
Considering the Input Modes
As described in the section "Setting the Pinlet Input Mode," the input mode affects how pen events are converted to characters.
The pinlet receives a call to the function PinletSetInputModeProcPtr()
when the application changes the input mode. It should also implement PinletGetInputModeProcPtr()
, which is used to retrieve the current input mode from the pinlet.
If the pinlet uses a handwriting recognition engine, these functions can simply call through to the functions HWRSetInputMode()
and HWRGetInputMode()
. For the set function, it must also invalidate the display so that it is redrawn in response to the frmUpdateEvent
to indicate the change in mode.
Note that the engine does not have to respect all input modes. Some modes might not make sense, in which case the engine sets the mode to a reasonably close value. For example, if a handwriting recognition engine does not implement caps lock mode, it might set the mode to shift instead. If the engine interprets the Latin alphabet and receives a request to switch to Hiragana, it could just remain in the default mode. For this reason, the pinlet should always call HWRGetInputMode()
after calling HWRSetInputMode()
to see if the change actually took place before invalidating the display.
If the pinlet performs the conversion to character data itself, it should take the input mode into consideration. It should store the input mode in such a way that it will be taken into consideration for the next series of pen events. Like the handwriting recognition engine, the pinlet does not have to respect all input modes.
Many handwriting recognition pinlets will have multiple "mode areas," that is, areas in which the pen stroke is interpreted in a certain way such as numeric or shifted. The effect of PinletSetInputModeProcPtr()
on these mode areas is implementation-defined.
In addition to receiving PinletSetInputModeProcPtr()
, a handwriting recognition pinlet may have to change its mode in response to the return value for HWRProcessStroke()
. If the user has entered a stroke that changes the input mode, the engine sets the inputMode
field of the returned structure. Pinlets need to check this value and change their input mode accordingly.
Handling Multistroke Characters
Handwriting recognition pinlets might need to deal with multistroke characters. Consider the K character in Graffiti 2 writing. This character takes two strokes to draw, and the first stroke is identical to the stroke for an L character.
If the pinlet is working with a handwriting recognition engine, it should send each stroke to the engine in separate HWRProcessStroke()
calls. The handwriting recognition engine determines what to do with the information. It might do either or both of the following:
- If the first stroke could be interpreted as a character by itself, the engine might return the character but set the
uncertain
field in the structure to indicate that the character may later have to be erased. - It might request a timeout value be set by returning
true
for thetimeout
field. The pinlet should set a timeout and if that time period elapses with no other strokes being received, callHWRTimeout()
.
If the handwriting recognition engine uses the uncertain
field, it might process an ambiguous character such as the stroke for the letter L as described in Table 3.1.
Table 3.1 Processing an L stroke option 1
Returns an • a
chars array containing the L character• the
uncertain field set to 1• the timeout field set to true |
||
• Stores the L character.
• Calls
TimGetTicks() and records the time. • Passes a timeout value to
EvtGetEvent() . • Upon each nilEvent , checks to see if timeout period has elapsed. |
Then, it would process the second potential stroke as described in Table 3.2. Early versions of the Graffiti 2 engine worked in this manner.
Table 3.2 Processing a stroke after the L stroke option 1
Returns an • a
chars array containing the K character• the
deleteUncertain field set to 1• the
timeout field set to false |
||
|
||
Draws a different character (instead of the second stroke for the K character) |
Returns an • a
chars array containing the new character• the
timeout field set to false |
|
|
||
|
Returns an |
The current Graffiti 2 engine does not use the uncertain
field. It processes the L character as described in Table 3.3.
Table 3.3 Processing an L stroke option 2
Returns an • an empty
chars array • the
timeout field set to true |
||
• Calls
TimGetTicks() and records the time. • Passes a timeout value to
EvtGetEvent() . • Upon
nilEvent , check to see if timeout period has elapsed. |
Then it processes the next stroke as described in Table 3.4.
Table 3.4 Processing a stroke after the L stroke option 2
Returns an • a
chars array containing the K character• the
timeout field set to false |
||
|
||
Draws a different character (instead of the second stroke for the K character) |
Returns an • a
chars array containing the L character and the new character• the
timeout field set to false |
|
|
||
Calls |
Returns an • a
chars array containing the L character • the timeout field set to false |
|
• Resets the timeout.
• Calls PINFeedChar() with the L character. |
Another case that might occur in both scenarios is that the pinlet may receive a PinletClearStateProcPtr()
call indicating all internal state should be cleared. This occurs when the user has moved to a new text field, tapped a control, switched applications, or performed any other action that indicates that the pinlet should start over when interpreting the next set of events. In response to this call, the pinlet should:
- Clear its timeout, its input mode, and any other internal state that it keeps.
- Call
HWRClearInputState()
to have the handwriting recognition engine do the same.
Implementing Live Ink
Live ink is a popular feature with handwriting recognition pinlets in which the user's pen movement is echoed on the screen. This feature helps users understand which character their strokes become.
If you want to implement a live ink feature, do so in the pinlet. The handwriting recognition engine helps with this feature by returning information in the inkHint
field. The handwriting recognition engine provides one of the following values:
-
hwrInkHintNone
- The pinlet should erase the last stroke drawn. Any strokes that were previously kept are still kept. This is the default behavior.
-
hwrInkHintEraseAll
- The pinlet should erase all strokes currently being displayed. This is typically sent when the engine has successfully converted a character.
-
hwrInkHintKeepAll
- The pinlet should retain all strokes currently being displayed.
-
hwrInkHintKeepLastOnly
- The user is in the middle of a multistroke character. The pinlet should display only the last stroke. It should erase any previous strokes.
Specifying the Default Pinlet
The PalmSource-provided pinlets have buttons on the right side that allow the user to switch between the default handwriting recognition pinlet and the different panes of the default keyboard pinlet. All other pinlets are only available from the pop-up menu in the status bar.
Figure 3.3 Default pinlet buttons

If you want users to be able to access your pinlet from these buttons, you can set your pinlet as either the default handwriting recognition pinlet or the default keyboard pinlet. To do so, use PINSetDefaultPinlet()
.
Guidelines for Default Pinlets
If you want your pinlet to be selected as a default, follow these guidelines:
- Users should be allowed to decide which pinlets they want to be the default. You might install a separate application that allows the user to set preferences for your pinlet and allows them to specify your pinlet as the default.
- There are only two possible default pinlets, one handwriting and one keyboard. The three buttons for keyboard pinlets access different input modes of a single default keyboard pinlet, as described in Figure 3.3.
- If your pinlet does something other than recognize handwriting or display a keyboard, do not set it as a default pinlet. If, for example, your pinlet is written specifically to work with a certain set of applications, you should have the applications call
PINSwitchToPinlet()
to make that pinlet active. Do not set the default pinlet.
User Interface Considerations
If you want your pinlet to be chosen as a default, its user interface should include a way to select the other style of default pinlet. That is, if yours is a handwriting recognition pinlet, it should include a button that allows the user to switch to the keyboard pinlet. If yours is a keyboard pinlet, it must include a way to select the default handwriting recognition pinlet.
On devices with no dynamic input area when the user opens the keyboard dialog, the default keyboard pinlet is displayed in the dialog. In this case, the user interface must not include a button that switches to the default handwriting recognition pinlet. When a keyboard pinlet is displayed in a dialog, it is opened with the input mode pinInputAreaNone
.
There is no way to replace the images that appear in the buttons shown in Figure 3.3. Therefore, if you are replacing the default keyboard pinlet, you cannot replace the icons in the default handwriting recognition pinlet with your own icons.
Summary
|
|
|
|