Search code examples
objective-cmacosapplescriptjavascript-automation

Can the ObjC function UCKeyTranslate be used from JavaScript for Automation?


In an osascript/JXA context, I need to map macOS keyCodes and modifiers to Unicode strings (for any keyboard layout, specified at run-time)

The obvious candidate appears to be UCKeyTranslate, but my attempts to get a reference to it from a JavaScript for Automation context have not been successful so far.

Does anybody know how to do this, perhaps in terms of ObjC.bindFunction ?


When ObjC libraries are imported into the 'JavaScript for Automation' (or 'JXA') JSContext, a global $ symbol is decorated with their methods.

So, for example, evaluating $.NSPasteboard in a vanilla JXA context evaluates to undefined, but after ObjC.import('Appkit'), we can write, and successfully evaluate, expressions like:

ObjC.deepUnwrap(
    $.NSPasteboard
    .generalPasteboard
    .propertyListForType(clipType)
);

In this case however, $.UCKeyTranslate remains undefined in the JSContext after:

ObjC.import("CoreServices")
ObjC.import("CarbonCore")

In such cases we can sometimes bind the names of ObjC functions to a JSContext name using ObjC.bindFunction, for example, although it would be redundant, we could successfully write and make use of the names function names 'sin' and 'cos' after writing.

['sin', 'cos'].forEach(
    k => ObjC.bindFunction(k, ['double', ['double']]));

In this case, however, neither the ObjC.import nor ObjC.bindFunction routes have so far defined the name $.UCKeyTranslate or UCKeyTranslate in the JXA JSContext.


Solution

  • One way to go would be to make a Swift or ObjC helper library (or a command line tool), and then use that from JXA. Check some example code to start.

    If you wrap this call in an ObjC object method inside a framework, it's possible to ObjC.import() it and use in your scripts. If you choose to do a command line app, then call it as a shell command.

    Using ObjC.bindFunction seems a bit too complicated, because you'd have to bind a few other functions that would get you a UCKeyboardLayout instance, and define a bunch of magic constants, plus do some necessary mapping of strings and pointers.