Thursday, July 30, 2009

Text input on 3.0, Part 4 — UIKBKeylistReference

  • When I was preparing this whole stuff many wrong directions were taken and generates quite a number of utilities that could be useful later. Who knows? Head to if you are interested.
  • You will need UIKB*.h to compile my examples. The headers can be found in my other project page at github. However, besides ActorKit no header there is actually complete (yet).

Hopefully it's the last of the series. I'm now making a convert from layout.plist to .keyboard files. This is a major foundation for backward-compatibility with previous keyboards, and also allows you to use custom keyboards on 3.0 without iKeyEx (but, without all the benefits like unlimited numbers of keyboards). The basic idea is described in Part 1.


Unlike the old UIKeyboardLayoutRoman, the new UIKeyboardLayoutStar is "scalable", that means this keyboard will still look sharp even if used on a 10" tablet PC. This is because the whole UIKeyboardLayoutStar is drawn at run time with Rounded rectangles and Gradients and Shadows and stuff. It's vector graphics. (In constrast, UIKeyboardLayoutRoman is precomposed into a PNG image.)

But even the layout has the potential to be scalable, all keyboards I've created before (using Star) is not, because I haven't told it what to scale. It's like an iPhone app, most of them can be easily presented in landscape mode, but if the programmer doesn't implement the -shouldAutorotateToInterfaceOrientation: method the app will always stuck in portrait mode.

As I've repeated many times, keyboard construction occupies 70% mass of UIKit, and that's roughly 8 MiB of code. There are 148 keyboards defined in UIKit so far, so each keyboard will be defined by 55 KiB of code and data. And unfortunately, most of these are code. Now, ARM's register-relative addressing mode allows a maximum span of 11 bits, i.e. 8 KiB, but there's 55 KiB of code! That means each single function must be chopped into chunks by the compiler. To the analyzing decompiler (ravel-arm), it is a difficult situation, because an unconditional branch at the end may mean a tail-function-call. The data after may be interpreted as code, and ruins all stored computation in between. This is happening in the keyboard-defining functions, so by static analyzing the code I can only get to how Apple defines keys up to the Capital Letter U (that's Q, W, E, R, T, Y, U), and then a branch across some mis-codified data, and all selector and class info are lost.

So? Initially I dumped all 148 keyboards and analyzed the resulting archive, but this doesn't tell us the implementation detail. Eventually I hooked objc_msgSend to give what's being called (since I knew only Obj-C functions are called and no structs are returned, I've ignored all other messaging variants).

The result is over 9000 bytes and I can't pastie it. But I can pastie part of the decompiled result. The UIKBKeylistReference part is what I was missing. Now let's plug in the keyboard, and we get... a dangling Q as we've expected.

But if we resize the keyboard from 320x216 to 240x216? Yes, the key is correctly scaled down:

And removing the references even make the dimensions wrong, so the scaling is really the reference in effect.

(When the reference is defined, the original geometry of the keys will be overridden).
(Also note that this keyboard cannot be scaled vertically, because the vertical measures of the rows are absolute values (pixels).)

One puzzling property of references is the "flag". Yes we know it must be bitfield of some sort, but what? If we decompile the class UIKBKeylistReference we get:
  • 1 = isKeysetReference
  • 2 = isKeylistReference
  • 4 = isKeysReference
  • 8 = isNamedKeyReference
  • 0x10 = isKeyIndexReference
  • 0x20 = isKeyIndexRangeReference
  • 0x40 = isGeometryReference
  • 0x80 = isAttributesReference

Apparently it refers to while the name elements interpreted.

Why we care about scaling? One, it allows us to define landscape and portrait keyboards with the same content (the keys), and to switch between the two we just need to change the outermost dimension, and the rest will be seamlessly changed. And two, who said the keyboard size must be fixed at 320x216 / 480x162? What if there is really an Apple tablet and uses the iPhoneOS? Or there's an iPhone mini? The resolution can't be the same, so the keyboard size must be altered as well. On 2.x we're forced to hard-code these 4 numbers because UIKeyboardLayoutRoman expects us to do so. Now having the better solution with UIKeyboardLayoutStar, I'd rather spend more time to take advantage of it and make iKeyEx more robust in the future.

1 comment:

  1. I miss hClipboard and 5-Key so much, I'm always checking for updates and this is sounding promising. I wish I understood any of this, but thanks for the hard work and good luck.