버전 업그레이드
This commit is contained in:
123
utils/touch_keyboard.py
Normal file
123
utils/touch_keyboard.py
Normal file
@@ -0,0 +1,123 @@
|
||||
"""Windows 터치 키보드(TabTip) 표시/숨김 — ITipInvocation COM API 사용."""
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
from ctypes import wintypes
|
||||
|
||||
_TABTIP_PATHS = (
|
||||
r"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe",
|
||||
r"C:\Program Files (x86)\Common Files\microsoft shared\ink\TabTip.exe",
|
||||
)
|
||||
|
||||
CLSID_UIHostNoLaunch = (
|
||||
0x4CE576FA,
|
||||
0x83DC,
|
||||
0x4F88,
|
||||
(0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76),
|
||||
)
|
||||
IID_ITipInvocation = (
|
||||
0x37C994E7,
|
||||
0x432B,
|
||||
0x4834,
|
||||
(0xA2, 0xF7, 0xDC, 0xE1, 0xF1, 0x3B, 0x83, 0x4B),
|
||||
)
|
||||
|
||||
REGDB_E_CLASSNOTREG = -2147221164 # 0x80040154
|
||||
SW_SHOW = 5
|
||||
CLSCTX_LOCAL_SERVER = 4
|
||||
|
||||
|
||||
class GUID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Data1", wintypes.DWORD),
|
||||
("Data2", wintypes.WORD),
|
||||
("Data3", wintypes.WORD),
|
||||
("Data4", wintypes.BYTE * 8),
|
||||
]
|
||||
|
||||
def __init__(self, guid_tuple):
|
||||
super().__init__()
|
||||
self.Data1 = guid_tuple[0]
|
||||
self.Data2 = guid_tuple[1]
|
||||
self.Data3 = guid_tuple[2]
|
||||
self.Data4[:] = guid_tuple[3]
|
||||
|
||||
|
||||
def _tabtip_path() -> str | None:
|
||||
for path in _TABTIP_PATHS:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
def _launch_tabtip() -> bool:
|
||||
path = _tabtip_path()
|
||||
if not path:
|
||||
return False
|
||||
ret = ctypes.windll.shell32.ShellExecuteW(
|
||||
None, "open", path, None, None, SW_SHOW,
|
||||
)
|
||||
return ret > 32
|
||||
|
||||
|
||||
def _create_tip_invocation(ole32):
|
||||
clsid = GUID(CLSID_UIHostNoLaunch)
|
||||
iid = GUID(IID_ITipInvocation)
|
||||
obj = ctypes.c_void_p()
|
||||
hr = ole32.CoCreateInstance(
|
||||
ctypes.byref(clsid),
|
||||
None,
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
ctypes.byref(iid),
|
||||
ctypes.byref(obj),
|
||||
)
|
||||
if hr == REGDB_E_CLASSNOTREG:
|
||||
if not _launch_tabtip():
|
||||
return None
|
||||
hr = ole32.CoCreateInstance(
|
||||
ctypes.byref(clsid),
|
||||
None,
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
ctypes.byref(iid),
|
||||
ctypes.byref(obj),
|
||||
)
|
||||
if hr != 0:
|
||||
return None
|
||||
return obj
|
||||
|
||||
|
||||
def _toggle_touch_keyboard() -> bool:
|
||||
ole32 = ctypes.windll.ole32
|
||||
user32 = ctypes.windll.user32
|
||||
|
||||
obj = _create_tip_invocation(ole32)
|
||||
if not obj:
|
||||
return False
|
||||
|
||||
vtable_ptr = ctypes.cast(obj, ctypes.POINTER(ctypes.c_void_p)).contents.value
|
||||
vtable = ctypes.cast(vtable_ptr, ctypes.POINTER(ctypes.c_void_p))
|
||||
toggle_fn = ctypes.WINFUNCTYPE(
|
||||
ctypes.c_long, ctypes.c_void_p, wintypes.HWND,
|
||||
)(vtable[3])
|
||||
|
||||
hr = toggle_fn(obj, user32.GetDesktopWindow())
|
||||
release_fn = ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_void_p)(vtable[2])
|
||||
release_fn(obj)
|
||||
return hr == 0
|
||||
|
||||
|
||||
def show_touch_keyboard() -> bool:
|
||||
"""터치 키보드를 화면에 표시한다."""
|
||||
if sys.platform != "win32":
|
||||
return False
|
||||
ole32 = ctypes.windll.ole32
|
||||
ole32.CoInitialize(None)
|
||||
try:
|
||||
return _toggle_touch_keyboard()
|
||||
finally:
|
||||
ole32.CoUninitialize()
|
||||
|
||||
|
||||
def hide_touch_keyboard() -> bool:
|
||||
"""표시 중인 터치 키보드를 숨긴다 (Toggle)."""
|
||||
return show_touch_keyboard()
|
||||
Reference in New Issue
Block a user