0

My editing steps:

  1. Type some Chinese words in insert mode.
  2. Press <esc> back to normal mode.
  3. Press <shift> change input method to English.
  4. Press keys in normal mode.

I want implement "Press <esc> back to normal mode and also change input method to English.".

In windows, Xshell have advance option implement just what I want. But after OS update, Xshell does not work right.

I also need this function in Linux. I has a Raspberry pi 4 running Raspbian OS. I use Terminator in Raspbian.

How can I implement this in Win10 and Raspbian?

randomeval
  • 599
  • 4
  • 13
  • 1
    Would `:help 'langmap'` be of help? – romainl Mar 08 '22 at 06:30
  • One could use the `InsertEnter` and `InsertLeave` autocommands, to launch external scripts to change the input method. These would be triggered when Entering and Leaving insert mode. – bert Mar 10 '22 at 04:34

1 Answers1

0

I found a way to work on windows10. (Have not test other windows os).

In my win10 settings, I only have chinese pinyin input method, no others.

The idea is below.

  1. use pynput listen to <esc> key release.
  2. use pywin32 and ctypes to find current focus window.
  3. use ctypes with imm32 DLL to get current language type.
  4. use pywin32 send message to focus window change language to english if current is chinese.

The code puts below.


#!/usr/bin/env python3

# pip install pynput
# pip install pywin32

from pynput.keyboard import Key, Listener, Controller
from os import system
import os
import signal
import ctypes
from ctypes import wintypes
import win32api
import win32process
import win32gui
import time

KEY = Key.esc
keyboard = Controller()

user32 = ctypes.WinDLL(name="user32")
imm32 = ctypes.WinDLL(name="imm32")

class GUITHREADINFO(ctypes.Structure):
    _fields_ = [
        ("cbSize", wintypes.DWORD),
        ("flags", wintypes.DWORD),
        ("hwndActive", wintypes.HWND),
        ("hwndFocus", wintypes.HWND),
        ("hwndCapture", wintypes.HWND),
        ("hwndMenuOwner", wintypes.HWND),
        ("hwndMoveSize", wintypes.HWND),
        ("hwndCaret", wintypes.HWND),
        ("rcCaret", wintypes.RECT),

    ]

    def __str__(self):
        ret = "\n" + self.__repr__()
        start_format = "\n  {0:s}: "
        for field_name, _ in self. _fields_[:-1]:
            field_value = getattr(self, field_name)
            field_format = start_format + ("0d{1:016d}" if field_value else "{1:}")
            ret += field_format.format(field_name, field_value)
        rc_caret = getattr(self, self. _fields_[-1][0])
        ret += (start_format + "({1:d}, {2:d}, {3:d}, {4:d})").format(self. _fields_[-1][0], rc_caret.top, rc_caret.left, rc_caret.right, rc_caret.bottom)
        return ret


def handler(signum, frame):
    print("get signal")
    os._exit(-1)

def GetGUIThreadInfo(win32, tid, info):
    GetGUIThreadInfo_func = getattr(win32, "GetGUIThreadInfo")
    GetGUIThreadInfo_func.argtypes = [wintypes.DWORD, ctypes.POINTER(GUITHREADINFO)]
    GetGUIThreadInfo_func.restype = wintypes.BOOL
    info.cbSize = ctypes.sizeof(GUITHREADINFO)
    return GetGUIThreadInfo_func(tid, ctypes.byref(info))

def set_window_english(h_wnd, tmp):
    h_imc = imm32.ImmGetDefaultIMEWnd(h_wnd)
    WM_IME_CONTROL=643
    IMC_GETCONVERSIONMODE=0x01
    status = win32api.SendMessage(h_imc, WM_IME_CONTROL, IMC_GETCONVERSIONMODE, 0)
    # status != 0: means chinese.
    # status == 0: means english.
    print(h_wnd, win32gui.GetWindowText(h_wnd), win32gui.GetClassName(h_wnd), status)
    if status != 0:
        # IMC_SETCONVERSIONMODE=2
        ret = win32api.SendMessage(h_imc, WM_IME_CONTROL, 2, 0) 
    
def change_to_english():
    h_wnd = win32gui.GetForegroundWindow()
    tid, pid = win32process.GetWindowThreadProcessId(h_wnd)
    info = GUITHREADINFO()
    if GetGUIThreadInfo(user32, tid, info):
        #print(info)
        #print(win32gui.GetWindowText(info.hwndFocus), win32gui.GetClassName(info.hwndFocus))
        set_window_english(info.hwndFocus, None)

def on_press(key):
    pass
        
def on_release(key):
    if key == KEY:
        change_to_english()
       
signal.signal(signal.SIGINT, handler)

listener = Listener(
        #on_press=on_press,
        on_release=on_release)

listener.start()
while True:
    #pass
    time.sleep(100)

The difficult part is to get current focus window.

win32gui.FindWindow and win32gui.FindWindowEx can't search the child windows.

win32gui.EnumChildWindow can do the work, but it's too slow for xshell.

GetFocus just return 0, I don't know why.

GetGUIThreadInfo works well, but not wrapped by pywin32. I found a way to wrap it in this link. This is the method I use.

randomeval
  • 599
  • 4
  • 13