mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-13 12:19:25 +00:00
549 lines
12 KiB
Python
549 lines
12 KiB
Python
import ctypes
|
|
import ctypes.util
|
|
import operator
|
|
import os
|
|
from enum import auto, IntEnum, IntFlag
|
|
from ctypes import (c_uint8,
|
|
c_uint16, c_uint32,
|
|
c_size_t,
|
|
c_void_p, c_char_p,
|
|
Structure, Union, POINTER, CFUNCTYPE)
|
|
from typing import Any, Tuple
|
|
|
|
CP = c_uint16
|
|
USV = c_uint32
|
|
VirtualKey = c_uint16
|
|
|
|
libkbp = ctypes.cdll.LoadLibrary(ctypes.util.find_library('kmnkbp0'))
|
|
|
|
|
|
# Error handling
|
|
# ==============
|
|
class StatusCode(IntEnum):
|
|
OK = 0
|
|
NO_MEM = auto()
|
|
IO_ERROR = auto()
|
|
INVALID_ARGUMENT = auto()
|
|
KEY_ERROR = auto()
|
|
OS_ERROR = 0x80000000
|
|
|
|
|
|
Status = c_uint32
|
|
|
|
|
|
def __map_oserror(code: Status) -> str:
|
|
code = StatusCode.OS_ERROR ^ code
|
|
msg = '{0!s}: OS error code (' + code + '): ' + os.strerror(code)
|
|
return msg, lambda m: OSError(code, m)
|
|
|
|
|
|
__exceptions_map = [
|
|
(None, '{0!s}: Success'),
|
|
(MemoryError, '{0!s}: memory allocation failed in: {1!s}'),
|
|
(RuntimeError, '{0!s}: IO Error: {2!s}'),
|
|
(ValueError, '{0!s}: Invalid argument passed to: {1!s}'),
|
|
(LookupError, '{0!s}: Item does not exist in: {2!s}'),
|
|
(OSError, __map_oserror)]
|
|
|
|
|
|
def status_code(code: Status, func, args):
|
|
if code == StatusCode.OK: return args
|
|
exc, msg = __exceptions_map[code]
|
|
if callable(msg): msg = msg(code)
|
|
raise exc(msg.format(libkbp._name, func._name_, *args))
|
|
|
|
|
|
def null_check(code, func, args):
|
|
if code is not None: return args
|
|
raise KeyError(args[1] + ': Not found in collection')
|
|
|
|
|
|
class Dir(IntEnum):
|
|
IN = auto()
|
|
OUT = auto()
|
|
OPT = auto()
|
|
|
|
|
|
def __method(iface: str, method: str, result,
|
|
*args: Tuple[Any, Dir, str], **kwds):
|
|
proto = CFUNCTYPE(result, *map(operator.itemgetter(0), args))
|
|
params = [a[1:] for a in args]
|
|
c_name = iface+'_'+method if iface else method
|
|
f = proto(('km_kbp_' + c_name, libkbp), params)
|
|
if 'errcheck' in kwds: f.errcheck = kwds.get('errcheck')
|
|
globals()[c_name] = f
|
|
|
|
|
|
# Context processing
|
|
# ==================
|
|
Context_p = c_void_p
|
|
|
|
|
|
class ContextType(IntEnum):
|
|
END = 0
|
|
CHAR = auto()
|
|
MARKER = auto()
|
|
|
|
|
|
class ContextItem(Structure):
|
|
class __ContextValue(Union):
|
|
_fields_ = (('character', c_char_p),
|
|
('marker', c_char_p))
|
|
_anonymous_ = ('value',)
|
|
_fields_ = (('type', c_uint8),
|
|
('value', __ContextValue))
|
|
|
|
|
|
__method('context_items', 'from_utf16', Status,
|
|
(POINTER(CP), Dir.IN, 'text'),
|
|
(POINTER(ContextItem), Dir.OUT, 'context_items'),
|
|
errcheck=status_code)
|
|
|
|
__method('context_items', 'to_utf16', c_size_t,
|
|
(POINTER(ContextItem), Dir.IN, 'context_items'),
|
|
(POINTER(CP), Dir.OUT | Dir.OPT, 'buffer'),
|
|
(c_size_t, Dir.OPT, 'buffer_size'))
|
|
|
|
__method('context_items', 'dispose', None,
|
|
(POINTER(ContextItem), Dir.IN, 'context_items'))
|
|
|
|
__method('context', 'set', Status,
|
|
(Context_p, Dir.IN, 'context'),
|
|
(POINTER(ContextItem), Dir.IN, 'context_items'),
|
|
errcheck=status_code)
|
|
|
|
__method('context', 'get', POINTER(ContextItem),
|
|
(Context_p, Dir.IN, 'context'))
|
|
|
|
__method('context', 'clear', None, (Context_p, Dir.IN, 'context'))
|
|
|
|
__method('context', 'length', c_size_t, (Context_p, Dir.IN, 'context'))
|
|
|
|
__method('context', 'append', None,
|
|
(Context_p, Dir.IN, 'context'),
|
|
(POINTER(ContextItem), Dir.IN, 'context_items'))
|
|
|
|
__method('context', 'shrink', None,
|
|
(Context_p, Dir.IN, 'context'),
|
|
(c_size_t, Dir.IN, 'num'),
|
|
(POINTER(ContextItem), Dir.IN, 'prefix'))
|
|
|
|
|
|
class ActionItem(Structure):
|
|
class __ActionItem(Union):
|
|
_fields_ = (('marker', c_size_t),
|
|
('option', c_char_p),
|
|
('character', USV),
|
|
('vkey', VirtualKey))
|
|
_anonymous_ = ('data',)
|
|
_fields_ = (('type', c_uint8),
|
|
('reserved', c_uint8*3),
|
|
('data', __ActionItem))
|
|
|
|
|
|
class ActionType(IntEnum):
|
|
END = 0 # Marks end of action items list.
|
|
CHAR = 1
|
|
MARKER = 2 # correlates to kmn's "deadkey" markers
|
|
ALERT = 3
|
|
BACK = 4
|
|
PERSIST_OPT = 5
|
|
RESET_OPT = 6
|
|
VKEYDOWN = 7
|
|
VKEYUP = 8
|
|
VSHIFTDOWN = 9
|
|
VSHIFTUP = 10
|
|
MAX_TYPE_ID = auto()
|
|
|
|
|
|
# Option processing
|
|
# =================
|
|
OptionSet_p = c_void_p
|
|
|
|
|
|
class OptionScope(IntEnum):
|
|
UNKNOWN = auto()
|
|
KEYBOARD = auto()
|
|
ENVIRONMENT = auto()
|
|
|
|
|
|
class Option(Structure):
|
|
_fields_ = (('key', c_char_p),
|
|
('value', c_char_p),
|
|
('scope', c_uint8))
|
|
|
|
|
|
Option.END = Option(None, None)
|
|
|
|
__method('option_set', 'length', c_size_t, (OptionSet_p, Dir.IN, 'opts'))
|
|
__method('option_set', 'lookup', POINTER(Option),
|
|
(OptionSet_p, Dir.IN, 'opts'),
|
|
(c_char_p, Dir.IN, 'key'),
|
|
errcheck=null_check)
|
|
__method('option_set', 'update', Status,
|
|
(OptionSet_p, Dir.IN, 'opts'),
|
|
(POINTER(Option), Dir.IN, 'new_opts'),
|
|
errcheck=status_code)
|
|
__method('option_set', 'to_json', Status,
|
|
(OptionSet_p, Dir.IN, 'opts'),
|
|
(c_char_p, Dir.OUT | Dir.OPT, 'buffer'),
|
|
(c_size_t, Dir.IN | Dir.OUT, 'space'),
|
|
errcheck=status_code)
|
|
|
|
|
|
# Keyboards
|
|
# =========
|
|
Keyboard_p = c_void_p
|
|
|
|
|
|
class KeyboardAttrs(Structure):
|
|
_fields_ = (('version_string', c_char_p),
|
|
('id', c_char_p),
|
|
('folder_path', c_char_p),
|
|
('default_options', OptionSet_p))
|
|
|
|
|
|
__method('keyboard', 'load', Status,
|
|
(c_char_p, Dir.IN, 'path'),
|
|
(POINTER(Keyboard_p), Dir.OUT, 'kb'),
|
|
errcheck=status_code)
|
|
__method('keyboard', 'dispose', None, (Keyboard_p, Dir.IN, 'kb'))
|
|
__method('keyboard', 'get_attrs', POINTER(KeyboardAttrs),
|
|
(Keyboard_p, Dir.IN, 'keyboard'))
|
|
|
|
|
|
# State processing
|
|
# ================
|
|
State_p = c_void_p
|
|
|
|
__method('state', 'create', Status,
|
|
(Keyboard_p, Dir.IN, 'keyboard'),
|
|
(POINTER(Option), Dir.IN, 'env',)
|
|
(POINTER(State_p), Dir.OUT, 'out'),
|
|
errcheck=status_code)
|
|
__method('state', 'clone', Status,
|
|
(State_p, Dir.IN, 'state'),
|
|
(POINTER(State_p), Dir.OUT, 'out'),
|
|
errcheck=status_code)
|
|
__method('state', 'dispose', None, (State_p, Dir.IN, 'state'))
|
|
__method('state', 'context', Context_p, (State_p, Dir.IN, 'state'))
|
|
__method('state', 'options', OptionSet_p, (State_p, Dir.IN, 'state'))
|
|
__method('state', 'action_items', POINTER(ActionItem),
|
|
(State_p, Dir.IN, 'state'),
|
|
(POINTER(c_size_t), Dir.OUT, 'num_items'))
|
|
__method('state', 'to_json', Status,
|
|
(State_p, Dir.IN, 'state'),
|
|
(c_char_p, Dir.OUT | Dir.OPT, 'buffer'),
|
|
(c_size_t, Dir.IN | Dir.OUT, 'space'),
|
|
errcheck=status_code)
|
|
|
|
|
|
# Processor
|
|
# =========
|
|
class Attributes(Structure):
|
|
_fields_ = (('max_context', c_size_t),
|
|
('current', c_uint16),
|
|
('revision', c_uint16),
|
|
('age', c_uint16),
|
|
('technology', c_uint16),
|
|
('vendor', c_char_p))
|
|
|
|
|
|
class Tech(IntFlag):
|
|
UNSPECIFIED = 0
|
|
KMN = 1
|
|
LDML = 2
|
|
|
|
|
|
__method(None, 'get_engine_attrs', POINTER(Attributes))
|
|
__method(None, 'process_events', Status,
|
|
(State_p, Dir.IN, 'state'),
|
|
(VirtualKey, Dir.IN, 'vkey'),
|
|
(c_uint16, Dir.IN, 'modifier_state'))
|
|
|
|
|
|
class Modifier(IntFlag):
|
|
LCTRL = 1 << 0
|
|
RCTRL = 1 << 1
|
|
LALT = 1 << 2
|
|
RALT = 1 << 3
|
|
SHIFT = 1 << 4
|
|
CTRL = 1 << 5
|
|
ALT = 1 << 6
|
|
CAPS = 1 << 7
|
|
NOCAPS = 1 << 8
|
|
NUMLOCK = 1 << 9
|
|
NONUMLOCK = 1 << 10
|
|
SCROLLOCK = 1 << 11
|
|
NOSCROLLOCK = 1 << 12
|
|
VIRTUALKEY = 1 << 13
|
|
|
|
|
|
class ModifierMask(IntFlag):
|
|
ALL = 0x7f
|
|
ALT_GR_SIM = Modifier.LCTRL | Modifier.LALT
|
|
CHIRAL = 0x1f
|
|
IS_CHIRAL = 0x0f
|
|
NON_CHIRAL = 0x7f
|
|
CAPS = 0x0300
|
|
NUMLOCK = 0x0C00
|
|
SCROLLLOCK = 0x3000
|
|
|
|
|
|
class VKey(IntEnum):
|
|
_00 = auto()
|
|
LBUTTON = auto()
|
|
RBUTTON = auto()
|
|
CANCEL = auto()
|
|
MBUTTON = auto()
|
|
_05 = auto()
|
|
_06 = auto()
|
|
_07 = auto()
|
|
BKSP = auto()
|
|
KTAB = auto()
|
|
_0A = auto()
|
|
_0B = auto()
|
|
KP5 = auto()
|
|
ENTER = auto()
|
|
_0E = auto()
|
|
_0F = auto()
|
|
SHIFT = auto()
|
|
CONTROL = auto()
|
|
ALT = auto()
|
|
PAUSE = auto()
|
|
CAPS = auto()
|
|
_15 = auto()
|
|
_16 = auto()
|
|
_17 = auto()
|
|
_18 = auto()
|
|
_19 = auto()
|
|
_1A = auto()
|
|
ESC = auto()
|
|
_1C = auto()
|
|
_1D = auto()
|
|
_1E = auto()
|
|
_1F = auto()
|
|
SPACE = auto()
|
|
PGUP = auto()
|
|
PGDN = auto()
|
|
END = auto()
|
|
HOME = auto()
|
|
LEFT = auto()
|
|
UP = auto()
|
|
RIGHT = auto()
|
|
DOWN = auto()
|
|
SEL = auto()
|
|
PRINT = auto()
|
|
EXEC = auto()
|
|
PRTSCN = auto()
|
|
INS = auto()
|
|
DEL = auto()
|
|
HELP = auto()
|
|
K0 = auto()
|
|
K1 = auto()
|
|
K2 = auto()
|
|
K3 = auto()
|
|
K4 = auto()
|
|
K5 = auto()
|
|
K6 = auto()
|
|
K7 = auto()
|
|
K8 = auto()
|
|
K9 = auto()
|
|
_3A = auto()
|
|
_3B = auto()
|
|
_3C = auto()
|
|
_3D = auto()
|
|
_3E = auto()
|
|
_3F = auto()
|
|
_40 = auto()
|
|
KA = auto()
|
|
KB = auto()
|
|
KC = auto()
|
|
KD = auto()
|
|
KE = auto()
|
|
KF = auto()
|
|
KG = auto()
|
|
KH = auto()
|
|
KI = auto()
|
|
KJ = auto()
|
|
KK = auto()
|
|
KL = auto()
|
|
KM = auto()
|
|
KN = auto()
|
|
KO = auto()
|
|
KP = auto()
|
|
KQ = auto()
|
|
KR = auto()
|
|
KS = auto()
|
|
KT = auto()
|
|
KU = auto()
|
|
KV = auto()
|
|
KW = auto()
|
|
KX = auto()
|
|
KY = auto()
|
|
KZ = auto()
|
|
_5B = auto()
|
|
_5C = auto()
|
|
_5D = auto()
|
|
_5E = auto()
|
|
_5F = auto()
|
|
NP0 = auto()
|
|
NP1 = auto()
|
|
NP2 = auto()
|
|
NP3 = auto()
|
|
NP4 = auto()
|
|
NP5 = auto()
|
|
NP6 = auto()
|
|
NP7 = auto()
|
|
NP8 = auto()
|
|
NP9 = auto()
|
|
NPSTAR = auto()
|
|
NPPLUS = auto()
|
|
SEPARATOR = auto()
|
|
NPMINUS = auto()
|
|
NPDOT = auto()
|
|
NPSLASH = auto()
|
|
F1 = auto()
|
|
F2 = auto()
|
|
F3 = auto()
|
|
F4 = auto()
|
|
F5 = auto()
|
|
F6 = auto()
|
|
F7 = auto()
|
|
F8 = auto()
|
|
F9 = auto()
|
|
F10 = auto()
|
|
F11 = auto()
|
|
F12 = auto()
|
|
F13 = auto()
|
|
F14 = auto()
|
|
F15 = auto()
|
|
F16 = auto()
|
|
F17 = auto()
|
|
F18 = auto()
|
|
F19 = auto()
|
|
F20 = auto()
|
|
F21 = auto()
|
|
F22 = auto()
|
|
F23 = auto()
|
|
F24 = auto()
|
|
_88 = auto()
|
|
_89 = auto()
|
|
_8A = auto()
|
|
_8B = auto()
|
|
_8C = auto()
|
|
_8D = auto()
|
|
_8E = auto()
|
|
_8F = auto()
|
|
NUMLOCK = auto()
|
|
SCROLL = auto()
|
|
_92 = auto()
|
|
_93 = auto()
|
|
_94 = auto()
|
|
_95 = auto()
|
|
_96 = auto()
|
|
_97 = auto()
|
|
_98 = auto()
|
|
_99 = auto()
|
|
_9A = auto()
|
|
_9B = auto()
|
|
_9C = auto()
|
|
_9D = auto()
|
|
_9E = auto()
|
|
_9F = auto()
|
|
_A0 = auto()
|
|
_A1 = auto()
|
|
_A2 = auto()
|
|
_A3 = auto()
|
|
_A4 = auto()
|
|
_A5 = auto()
|
|
_A6 = auto()
|
|
_A7 = auto()
|
|
_A8 = auto()
|
|
_A9 = auto()
|
|
_AA = auto()
|
|
_AB = auto()
|
|
_AC = auto()
|
|
_AD = auto()
|
|
_AE = auto()
|
|
_AF = auto()
|
|
_B0 = auto()
|
|
_B1 = auto()
|
|
_B2 = auto()
|
|
_B3 = auto()
|
|
_B4 = auto()
|
|
_B5 = auto()
|
|
_B6 = auto()
|
|
_B7 = auto()
|
|
_B8 = auto()
|
|
_B9 = auto()
|
|
COLON = auto()
|
|
EQUAL = auto()
|
|
COMMA = auto()
|
|
HYPHEN = auto()
|
|
PERIOD = auto()
|
|
SLASH = auto()
|
|
BKQUOTE = auto()
|
|
_C1 = auto()
|
|
_C2 = auto()
|
|
_C3 = auto()
|
|
_C4 = auto()
|
|
_C5 = auto()
|
|
_C6 = auto()
|
|
_C7 = auto()
|
|
_C8 = auto()
|
|
_C9 = auto()
|
|
_CA = auto()
|
|
_CB = auto()
|
|
_CC = auto()
|
|
_CD = auto()
|
|
_CE = auto()
|
|
_CF = auto()
|
|
_D0 = auto()
|
|
_D1 = auto()
|
|
_D2 = auto()
|
|
_D3 = auto()
|
|
_D4 = auto()
|
|
_D5 = auto()
|
|
_D6 = auto()
|
|
_D7 = auto()
|
|
_D8 = auto()
|
|
_D9 = auto()
|
|
_DA = auto()
|
|
LBRKT = auto()
|
|
BKSLASH = auto()
|
|
RBRKT = auto()
|
|
QUOTE = auto()
|
|
oDF = auto()
|
|
oE0 = auto()
|
|
oE1 = auto()
|
|
oE2 = auto()
|
|
oE3 = auto()
|
|
oE4 = auto()
|
|
_E5 = auto()
|
|
oE6 = auto()
|
|
_E7 = auto()
|
|
_E8 = auto()
|
|
oE9 = auto()
|
|
oEA = auto()
|
|
oEB = auto()
|
|
oEC = auto()
|
|
oED = auto()
|
|
oEE = auto()
|
|
oEF = auto()
|
|
oF0 = auto()
|
|
oF1 = auto()
|
|
oF2 = auto()
|
|
oF3 = auto()
|
|
oF4 = auto()
|
|
oF5 = auto()
|
|
_F6 = auto()
|
|
_F7 = auto()
|
|
_F8 = auto()
|
|
_F9 = auto()
|
|
_FA = auto()
|
|
_FB = auto()
|
|
_FC = auto()
|
|
_FD = auto()
|
|
_FE = auto()
|
|
_FF = auto()
|