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.
- use
pynput listen to <esc> key release.
- use
pywin32 and ctypes to find current focus window.
- use
ctypes with imm32 DLL to get current language type.
- 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.