macOS Catalina - Onscreen (virtual) Keyboard not working

409 views Asked by At

I have been using below code to show the virtual onscreen keyboard and so far working good till 10.14 but it's no longer working on Catalina (10.15). On Catalina it is no longer able to create Input sources and it is always empty -

+ (void) showHideVirtualKeyboard:(BOOL) shouldShow {


    NSDictionary *property = [NSDictionary dictionaryWithObject:(NSString*)kTISTypeKeyboardViewer
                                                         forKey:(NSString*)kTISPropertyInputSourceType];
    NSLog(@"showHideVirtualKeyboard my dictionary is - %@",property);
    NSArray *sources = (__bridge NSArray*)TISCreateInputSourceList((__bridge CFDictionaryRef)property, false);
    if([sources count] == 0) {
        DBLogError(@"ToggleKeyboard: no input keyboard source type found...");
        DBLogError(@"ToggleKeyboard: Trying to create keyboard soruce type");
        sources = (__bridge NSArray*)TISCreateInputSourceList((__bridge CFDictionaryRef)property, true);
        if ([sources count]==0) {
            DBLogError(@"ToggleKeyboard: Still can't find any input sources so nothing to...");
            if(sources)
                CFRelease((CFTypeRef)sources);
            return;
        }
    }

    TISInputSourceRef keyboardViewer = (__bridge TISInputSourceRef)[sources objectAtIndex:0];
    //DBLogInfo(@"********** received sources are %@",keyboardViewer);
    int osStatus;
    //let's show hide keyboard
    if (shouldShow == YES){
        CFBooleanRef enabled = TISGetInputSourceProperty(keyboardViewer, kTISPropertyInputSourceIsEnabled);
        if (enabled == kCFBooleanFalse)
            TISEnableInputSource(keyboardViewer);
        // DBLogInfo(@"kTISPropertyInputSourceIsEnabled = %@",(enabled == kCFBooleanFalse)?@"false":@"true");
        osStatus = TISSelectInputSource(keyboardViewer);
    }
    else
        osStatus = TISDeselectInputSource(keyboardViewer);

    if(osStatus !=noErr)
        DBLogInfo(@"ToggleKeyboard: Received errored OSStatus and it is (%d) ",osStatus);
    if(sources)
        CFRelease((CFTypeRef)sources);
}

Please advise if anybody has faced similar issue and any solution / workaround is available.

Thanks.

2

There are 2 answers

0
dparakh On

With Catalina, Apple is now using the Accessibility Keyboard as the virtual keyboard (Keyboard Viewer). And the only way that I could figure out to show this programmatically seems to be with an Apple Script.

Something like this should work(can be invoked with NSAppleScript or by executing osascript from C/C++ code):

activate application "System Preferences"
tell application "System Preferences"
    reveal anchor "Virtual_Keyboard" in pane id "com.apple.preference.universalaccess"
end tell

tell application "System Events"
    tell process "System Preferences"
        repeat 20 times
            if (exists checkbox "Enable Accessibility Keyboard" of tab group 1 of group 1 of window 1) then
                click checkbox "Enable Accessibility Keyboard" of tab group 1 of group 1 of window 1
                exit repeat
            end if
            delay 1
        end repeat
    end tell
end tell
tell application "System Preferences" to quit

Note that to use this the user will need to authorize your app to use Automation and Accessibility. Further, your app should also declare a purpose string (NSAppleEventsUsageDescription) in the Info.plist

0
RG_ On

I got this version to work in Ventura 13.2.1:

tell application "System Settings"
    activate
    delay 0.2
end tell
tell application "System Events"
    tell application process "System Settings"
        tell outline 1 of scroll area 1 of group 1 of splitter group 1 of group 1 of window 1
            select row 16
            delay 0.2
        end tell
        tell group 3 of scroll area 1 of group 1 of group 2 of splitter group 1 of group 1 of window 1
            click button 2
            delay 0.2
            click checkbox "Accessibility Keyboard"
        end tell
    end tell
end tell
tell application "System Settings" to quit

This is pretty brittle, I would not expect it to survive another OS upgrade. You may also need longer delays. The approach taken by dparakh, using repeats instead of delays is probably better.