Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change OSX keyboard layout("input source") programmatically via terminal or AppleScript?

I am currently switching input sources by running a GUI AppleScript through Alfred, and the GUI script can sometime take up to 1s to complete the change. It gets quite annoying at times.

I have come across Determine OS X keyboard layout (“input source”) in the terminal/a script. And I want to know since we can find out the current input source if there's a way to change input source programatically? I'd tried overwriting the com.apple.HIToolbox.plist but it does not change the input.

(I do realise there's mapping shortcut to input sources available in the system preference, however I prefer mapping keywords with Alfred)

like image 559
maxhungry Avatar asked May 19 '14 04:05

maxhungry


3 Answers

You can do it using the Text Input Services API:

NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : @"com.apple.keylayout.French" }, FALSE));
TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
OSStatus status = TISSelectInputSource(source);
if (status != noErr)
    /* handle error */;

The dictionary in the first line can use other properties for other criteria for picking an input source.

There's also NSTextInputContext. It has a selectedKeyboardInputSource which can be set to an input source ID to select a different input source. The issue there is that you need an instance of NSTextInputContext to work with and one of those exists only when you have a key window with a text view as its first responder.

like image 109
Ken Thomases Avatar answered Oct 27 '22 07:10

Ken Thomases


@Ken Thomases' solution is probably the most robust - but it requires creation of a command-line utility.

A non-GUI-scripting shell scripting / AppleScripting solution is unfortunately not an option: while it is possible to update the *.plist file that reflects the currently selected input source (keyboard layout) - ~/Library/Preferences/com.apple.HIToolbox.plist - the system will ignore the change.

However, the following GUI-scripting solution (based on this), while still involving visible action, is robust and reasonably fast on my machine (around 0.2 seconds):

(If you just wanted to cycle through installed layouts, using a keyboard shortcut defined in System Preferences is probably your best bet; the advantage of this solution is that you can target a specific layout.)

Note the prerequisites mentioned in the comments.

# Example call
my switchToInputSource("Spanish")

# Switches to the specified input source (keyboard layout) using GUI scripting.
# Prerequisites:
#   - The application running this script must be granted assisistive access.
#   - Showing the Input menu in the menu bar must be turned on 
# (System Preferences > Keyboard > Input Sources > Show Input menu in menu bar).
# Parameters:
#    name ... input source name, as displayed when you open the Input menu from
#             the menu bar; e.g.: "U.S."
# Example:
#   my switchToInputSource("Spanish")
on switchToInputSource(name)
    tell application "System Events" to tell process "SystemUIServer"
        tell (menu bar item 1 of menu bar 1 whose description is "text input")
            # !! Sadly, we must *visibly* select (open) the text-input menu-bar extra in order to
            # !! populate its menu with the available input sources.
            select
            tell menu 1
                # !! Curiously, using just `name` instead of `(get name)` didn't work: 'Access not allowed'.
                click (first menu item whose title = (get name))
            end tell
        end tell
    end tell
end switchToInputSource
like image 24
mklement0 Avatar answered Oct 27 '22 07:10

mklement0


Solution using Xcode Command Line Tools

For those, who would like to build @Ken Thomases' solution but without installing Xcode (which is several GiB and is totally useless to spend so much space on unless used seriously) it is possible to build it using the Xcode Command Line Tools.

There are several tutorials on the internet about how to install Xcode Command Line Tools. The point here is only that it takes fraction of the space compared to full-blown Xcode.

Once you have it installed, these are the steps:

  1. Create a file called whatever.m
  2. In whatever.m put the following:
#include <Carbon/Carbon.h>

int main (int argc, const char * argv[]) {
    NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : @"com.apple.keylayout.French" }, FALSE));
    TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
    OSStatus status = TISSelectInputSource(source);
    if (status != noErr)
        return -1;

    return 0;
}
  1. Replace French with your desired layout.
  2. Save the file
  3. Open terminal in the same folder as whatever.m is
  4. Run this command: clang -framework Carbon whatever.m -o whatever

Your application is created as whatever in the same folder and can be executed as: .\whatever

Additionally

I've never created any Objective-C programs, so this may be suboptimal, but I wanted an executable that can take the keyboard layout as a command line parameter. For anyone interested, here's the solution I came up with:

In step 2 use this code:

#import <Foundation/Foundation.h>
#include <Carbon/Carbon.h>

int main (int argc, const char * argv[]) {
    NSArray *arguments = [[NSProcessInfo processInfo] arguments];

    NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : [@"com.apple.keylayout." stringByAppendingString:arguments[1]] }, FALSE));
    TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
    OSStatus status = TISSelectInputSource(source);
    if (status != noErr)
        return -1;

    return 0;
}

In step 6. run this command: clang -framework Carbon -framework Foundation whatever.m -o whatever

You can now switch to any layout from the command line, e.g.: ./whatever British

Note: it only allows to switch to layouts already configured on your system!

like image 29
sbnc.eu Avatar answered Oct 27 '22 07:10

sbnc.eu