Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Toggle a specific input source with Applescript

Hello to all,


I am trying to write an Applescript to toggle a specific language input source. My main input sources are two, english and greek, but in many occasions i have to enable the "Greek polytonic" keyboard. So, after some search on the internet, I wrote this:


tell application "System Preferences"


activate

reveal anchor "InputMenu" of pane id "com.apple.Localization"

endtell


tell application "System Events" to tell process "System Preferences"


clickcheckboxofrow76oftable1ofscroll area1oftab group1ofwindow1

endtell


quit application "System Preferences"


It compiles OK, but when I run it what happens is that the selected checkbox (the "Greek Polytonic" one) gets checked and INSTANTLY unchecked.


Why does this happen?


Thanks in advance


Dimitris

Applescript-OTHER, Mac OS X (10.5.8)

Posted on Nov 28, 2013 2:53 AM

Reply
11 replies

Nov 29, 2013 6:57 PM in response to milosisd

Hello


I have confirmed the odd behaviour under 10.5.8. It seems to me the required accessibility is defective under 10.5.8. The GUI scripting code works fine under 10.6.8 but the row index of the enabled input source will change to some smaller number when you reopen the System Preferences.app.


Anyway, I've written a simple command line utility in C, which lets you manipulate the text input source. Here's the recipe for you.



# Recipe.


A) To compile and test the C programme.


A1) Copy the code listed below as main.c into new document of text editor (e.g. TextEdit) and save it as plain text named "main.c" on desktop.


A2) In Terminal.app, run the following (type each line followed by return) to create an executable file named "textinputsource" on desktop:


cd ~/Desktop
gcc -framework Carbon -o textinputsource main.c


A3) In Terminal.app, type the following and return to toggle the enabled/disabled state of the specified input source:


./textinputsource -t 'Greek Polytonic'



B) To create an AppleScript wrapper to call this utility.


B1) Create a new AppleScript script with the following contents and save it as a script bundle or an application bundle:


set p to (path to resource "textinputsource")'s POSIX path
do shell script p's quoted form & " -t 'Greek Polytonic'"


B2) Show package contents (via contextual menu) of the saved bundle and put the executable file "textinputsource" loose in its Contents/Resources directory. Now you can run the script bundle or the application bundle to toggle the input source.



# Notes.


• The utility programme has some other options. E.g., you can select the specified source by -s option. See comments in source code for details.


• You need to have Developer Tools installed to build the programme.


• Tested under 10.5.8 and 10.6.8 but no warranties of any kind.


• I noticed that System Preferences.app won't update the check box live when the enabled/disabled state is changed by this utility. You'd need to rerun the System Preferences to reflect the change in its GUI.


• This is freeware you may use and modify as you like. 🙂



# File


main.c

-----------------------------------------------------------

/*    
    file
        main.c

    function
        to manipulate text input source, i.e., 
            - print currently selected source,
            - select specified source (enable it as needed)
            - enable specified source, 
            - disable specified source,
            - toggle enabled/disabled state of specified source.

    compile
        gcc -framework Carbon -o textinputsource main.c

    usage e.g.
        ./textinputsource [-s|e|d|t name]

        given no arguments, it will print the current source name.
        options:
            -s : select source name (enable it as needed)
            -e : enable source name
            -d : disable source name
            -t : toggle enabled/disabled on source name
*/

#include <Carbon/Carbon.h>
#include <libgen.h>    // basename

TISInputSourceRef getInputSourceByName(char *);

int
main (int argc, char * argv[])
{
    int ret = -1;
    int c;
    TISInputSourceRef tis;
    CFStringRef name;
    OSStatus err = 0;

    while ((c = getopt(argc, argv, "s:e:d:t:h")) != -1)
    {
        switch (c)
        {
            case 's':
                tis = getInputSourceByName(optarg);
                if (tis)
                {
                    CFBooleanRef enabled = TISGetInputSourceProperty(tis, kTISPropertyInputSourceIsEnabled);
                    if (enabled == kCFBooleanFalse)
                        TISEnableInputSource(tis);
                    err = TISSelectInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'e':
                tis = getInputSourceByName(optarg);
                if (tis)
                {
                    err = TISEnableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'd':
                tis = getInputSourceByName(optarg);
                if (tis)
                {
                    err = TISDisableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 't':
                tis = getInputSourceByName(optarg);
                if (tis)
                {
                    CFBooleanRef enabled = TISGetInputSourceProperty(tis, kTISPropertyInputSourceIsEnabled);
                    if (enabled == kCFBooleanTrue)
                        err = TISDisableInputSource(tis);
                    else
                        err = TISEnableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'h':
            case '?':
            default:
                fprintf(stderr, "Usage: %s %s\n\t\%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
                    basename(argv[0]),
                    "[-s|e|d|t name]",
                    "-s : select source name (enable it as needed)",
                    "-e : enable source name",
                    "-d : disable source name",
                    "-t : toggle enabled/disabled of source name",
                    "no arguments : print current source name"
                );
                ret = 1;
                break;
        }
    }
    if (ret == -1) // no args: print current keyboard input source
    {
        tis = TISCopyCurrentKeyboardInputSource();
        name = TISGetInputSourceProperty(tis, kTISPropertyLocalizedName);
        int len = CFStringGetLength(name) * 4 + 1;
        char cname[len];
        bool b = CFStringGetCString(name, cname, len, kCFStringEncodingUTF8);
        printf("%s\n", b ? cname : "");
        ret = b ? 0 : 1;
    }
    if (err != noErr)
        fprintf(stderr, "Text Input Source Services error: %d\n", (int) err);
    return ret;
}

TISInputSourceRef
getInputSourceByName(char *cname)
{
    //     char *cname : input source name in UTF-8 terminated by null character
    //     return TISInputSourceRef or NULL : text input source reference (retained)

    CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, cname, kCFStringEncodingUTF8);
    CFStringRef keys[] = { kTISPropertyLocalizedName };
    CFStringRef values[] = { name };
    CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, NULL, NULL);
    CFArrayRef array = TISCreateInputSourceList(dict, true);
    CFRelease(dict);
    CFRelease(name);
    if (!array)
    {
        fprintf(stderr, "No such text input source: %s\n", optarg);
        return NULL;
    }
    TISInputSourceRef tis = (TISInputSourceRef) CFArrayGetValueAtIndex(array, 0);
    CFRetain(tis);
    CFRelease(array);
    return tis;
}

-----------------------------------------------------------


Hope this may help,

H

Nov 30, 2013 2:54 PM in response to milosisd

My pleasure! So glad to hear it helped. 🙂


Just for future reference, I revised the code a bit so as to a) fix a possible memory leak in getting current input source name, b) change the name of function getInputSourceByName() to createInputSourceByName() to follow the naming convention of Core Foundation framework and c) use cname in lieu of optarg in fprintf() statment in the function to rectify the overlooked residue of earlier test version. These changes do not affect the result of the programme. Revised code is as follows.


-------------------------------------------------------

/*    
    file
        main.c
    
    function
        to manipulate text input source, i.e., 
            - print currently selected source,
            - select specified source (enable it as needed)
            - enable specified source, 
            - disable specified source,
            - toggle enabled/disabled state of specified source.
    
    compile
        gcc -framework Carbon -o textinputsource main.c

    usage e.g.
        ./textinputsource [-s|e|d|t name]

        given no arguments, it will print the current source name.
        options:
            -s : select source name (enable it as needed)
            -e : enable source name
            -d : disable source name
            -t : toggle enabled/disabled on source name
*/

#include <Carbon/Carbon.h>
#include <libgen.h>    // basename

TISInputSourceRef createInputSourceByName(char *);

int
main (int argc, char * argv[])
{
    int ret = -1;
    int c;
    TISInputSourceRef tis;
    CFStringRef name;
    OSStatus err = 0;
    
    while ((c = getopt(argc, argv, "s:e:d:t:h")) != -1)
    {
        switch (c)
        {
            case 's':
                tis = createInputSourceByName(optarg);
                if (tis)
                {
                    CFBooleanRef enabled = TISGetInputSourceProperty(tis, kTISPropertyInputSourceIsEnabled);
                    if (enabled == kCFBooleanFalse)
                        TISEnableInputSource(tis);
                    err = TISSelectInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'e':
                tis = createInputSourceByName(optarg);
                if (tis)
                {
                    err = TISEnableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'd':
                tis = createInputSourceByName(optarg);
                if (tis)
                {
                    err = TISDisableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 't':
                tis = createInputSourceByName(optarg);
                if (tis)
                {
                    CFBooleanRef enabled = TISGetInputSourceProperty(tis, kTISPropertyInputSourceIsEnabled);
                    if (enabled == kCFBooleanTrue)
                        err = TISDisableInputSource(tis);
                    else
                        err = TISEnableInputSource(tis);
                    CFRelease(tis);
                }
                ret = tis ? (int) err : 1;
                break;
            case 'h':
            case '?':
            default:
                fprintf(stderr, "Usage: %s %s\n\t\%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
                    basename(argv[0]),
                    "[-s|e|d|t name]",
                    "-s : select source name (enable it as needed)",
                    "-e : enable source name",
                    "-d : disable source name",
                    "-t : toggle enabled/disabled of source name",
                    "no arguments : print current source name"
                );
                ret = 1;
                break;
        }
    }
    if (ret == -1) // no args: print current keyboard input source
    {
        tis = TISCopyCurrentKeyboardInputSource();
        name = TISGetInputSourceProperty(tis, kTISPropertyLocalizedName);
        CFRelease(tis);
        int len = CFStringGetLength(name) * 4 + 1;
        char cname[len];
        bool b = CFStringGetCString(name, cname, len, kCFStringEncodingUTF8);
        printf("%s\n", b ? cname : "");
        ret = b ? 0 : 1;
    }
    if (err != noErr)
        fprintf(stderr, "Text Input Source Services error: %d\n", (int) err);
    return ret;
}

TISInputSourceRef
createInputSourceByName(char *cname)
{
    //     char *cname : input source name in UTF-8 terminated by null character
    //     return TISInputSourceRef or NULL : text input source reference (retained)
    
    CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, cname, kCFStringEncodingUTF8);
    CFStringRef keys[] = { kTISPropertyLocalizedName };
    CFStringRef values[] = { name };
    CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, NULL, NULL);
    CFArrayRef array = TISCreateInputSourceList(dict, true);
    CFRelease(dict);
    CFRelease(name);
    if (!array)
    {
        fprintf(stderr, "No such text input source: %s\n", cname);
        return NULL;
    }
    TISInputSourceRef tis = (TISInputSourceRef) CFArrayGetValueAtIndex(array, 0);
    CFRetain(tis);
    CFRelease(array);
    return tis;
}

-------------------------------------------------------



Best wishes from Japan,

Hiroto

Toggle a specific input source with Applescript

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.