macOSXのinput source の確認・切替のメモ

# macOSXのinput source の確認・切替のメモ

# Summary

`macOSX`の`input source` の確認と切替のメモ

## Information source

about Cocoa text api

[Apple Developer > documentation > AppKit > TextKit > NSTextInputContext](https://developer.apple.com/documentation/appkit/nstextinputcontext?language=objc)


about pyobjc

[introduction](https://pythonhosted.org/pyobjc/index.html)

## Python's library

```sh
$ pip3 install pyobjc
$ pip3 install pyobjc-core
$ pip3 install pyobjc-framework-Cocoa
```

## Environment

```sh
(ins)$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.13.6
BuildVersion:   17G65

(ins)$ python3 --version
Python 3.7.0

(ins)$ pip3 list
Package                Version
---------------------- -------
pyobjc                 4.2.2  
pyobjc-core            4.2.2  
pyobjc-framework-Cocoa 4.2.2  
```

## こんな感じ

`input source` の確認

```python
from AppKit import NSTextInputContext , NSTextView

txt = NSTextInputContext.alloc().initWithClient_( NSTextView.new() )
print( txt.selectedKeyboardInputSource() )

# => com.apple.keylayout.US
```

`input source` の切替

```python
from AppKit import NSTextInputContext , NSTextView

txt = NSTextInputContext.alloc().initWithClient_( NSTextView.new() )
txt.setValue_forKey_('com.apple.inputmethod.Kotoeri.Japanese', 'selectedKeyboardInputSource')

# => Kotoeri に切り替わる
```

`input source` 切替の通知

```python
import Foundation
from AppKit import NSObject, NSTextInputContext , NSTextView, NSTextInputContextKeyboardSelectionDidChangeNotification
from PyObjCTools import AppHelper
import objc

class Observer(NSObject):

    # initialize method
    def initWithValue_(self, txtObj):
        self = objc.super(Observer, self).init()
        self.text_input_context = txtObj
        return self
    
    # add handler
    def bundle_(self, aNotification):
        print( self.text_input_context.selectedKeyboardInputSource() )

# create Cocoa's text obj
txtObj = NSTextInputContext.alloc().initWithClient_( NSTextView.new() )

# createt observer obj
obs = Observer.new().initWithValue_( txtObj )

# create notify obj
notify_obj = Foundation.NSNotificationCenter.defaultCenter()

# add event to notify obj
notify_obj.addObserver_selector_name_object_(
      obs
    , 'bundle:'
    , NSTextInputContextKeyboardSelectionDidChangeNotification
    , None
)

# do console-loop
AppHelper.runConsoleEventLoop()

# --- Kotoeri に切り替える ---
# => com.apple.inputmethod.Kotoeri.Japanese
# --- US に切り替える ---
# => com.apple.keylayout.US
```

## 課題

通知を使わずに現在の `input source` を知る方法がわからない

下記のコードは最初の `input source` しかわからない

```python
from AppKit import NSTextInputContext , NSTextView

def loop():

    txt = NSTextInputContext.alloc().initWithClient_( NSTextView.new() )

    while True:
        if input() == '':
            break
        else:
            print( txt.selectedKeyboardInputSource() )

loop()


# a
# => com.apple.keylayout.US
# あ
# => com.apple.keylayout.US
# a
# => com.apple.keylayout.US
# あ
# => com.apple.keylayout.US
```


## teratailで質問なげたった

[(macOSX) selectedKeyboardInputSource がうまくはたらないです](https://teratail.com/questions/141913)


## Carbon's way

`input source` の確認

```python
from Foundation import NSBundle
import objc

# use HITToolbox's functions
HIToolbox_bundle = NSBundle.bundleWithIdentifier_("com.apple.HIToolbox")

# bundle many...
HIToolbox_functions = [ ('TISGetInputSourceProperty', b'@@@'), ('TISCopyCurrentKeyboardInputSource', b'@') ]
HIToolbox_constants = [ ('kTISPropertyInputModeID', b'@') ]

objc.loadBundleFunctions(HIToolbox_bundle, globals(), HIToolbox_functions)
objc.loadBundleVariables(HIToolbox_bundle, globals(), HIToolbox_constants)

print( TISGetInputSourceProperty(TISCopyCurrentKeyboardInputSource() , kTISPropertyInputModeID ) )
```

`input source` の切替

```python
import CoreFoundation
import ctypes
import ctypes.util

# add carbon lib
carbon = ctypes.cdll.LoadLibrary(ctypes.util.find_library('Carbon'))

# re-define arg and return types
carbon.TISSelectInputSource.restype               = ctypes.c_void_p
carbon.TISSelectInputSource.argtypes              = [ctypes.c_void_p]
carbon.TISCopyInputSourceForLanguage.argtypes     = [ctypes.c_void_p]
carbon.TISCopyInputSourceForLanguage.restype      = ctypes.c_void_p

lang = 'en'
carbon.TISSelectInputSource(
    carbon.TISCopyInputSourceForLanguage(
        CoreFoundation.CFSTR(lang).__c_void_p__())
)
```

## Carbon's references

[pudquick/input_sources.py](https://gist.github.com/pudquick/cff1ecdc02b4cabe5aa0dc6919d97c6d)

[Khande/auto_switch_kb.py](https://gist.github.com/Khande/76f24ba90607fb5d54185bd8e4520de6)

[How can we reading / coerce CFArray and CFString etc values from within OS X JXA?](https://stackoverflow.com/a/35007079)