# Copyright 2000-2004 Michael Hudson-Doyle # # All Rights Reserved # # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose is hereby granted without fee, # provided that the above copyright notice appear in all copies and # that both that copyright notice and this permission notice appear in # supporting documentation. # # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # (naming modules after builtin functions is not such a hot idea...) # an KeyTrans instance translates Event objects into Command objects # hmm, at what level do we want [C-i] and [tab] to be equivalent? # [meta-a] and [esc a]? obviously, these are going to be equivalent # for the UnixConsole, but should they be for PygameConsole? # it would in any situation seem to be a bad idea to bind, say, [tab] # and [C-i] to *different* things... but should binding one bind the # other? # executive, temporary decision: [tab] and [C-i] are distinct, but # [meta-key] is identified with [esc key]. We demand that any console # class does quite a lot towards emulating a unix terminal. from __future__ import annotations from abc import ABC, abstractmethod import unicodedata from collections import deque # types if False: from .types import EventTuple class InputTranslator(ABC): @abstractmethod def push(self, evt: EventTuple) -> None: pass @abstractmethod def get(self) -> EventTuple | None: return None @abstractmethod def empty(self) -> bool: return True class KeymapTranslator(InputTranslator): def __init__(self, keymap, verbose=0, invalid_cls=None, character_cls=None): self.verbose = verbose from .keymap import compile_keymap, parse_keys self.keymap = keymap self.invalid_cls = invalid_cls self.character_cls = character_cls d = {} for keyspec, command in keymap: keyseq = tuple(parse_keys(keyspec)) d[keyseq] = command if self.verbose: print(d) self.k = self.ck = compile_keymap(d, ()) self.results = deque() self.stack = [] def push(self, evt): if self.verbose: print("pushed", evt.data, end="") key = evt.data d = self.k.get(key) if isinstance(d, dict): if self.verbose: print("transition") self.stack.append(key) self.k = d else: if d is None: if self.verbose: print("invalid") if self.stack or len(key) > 1 or unicodedata.category(key) == "C": self.results.append((self.invalid_cls, self.stack + [key])) else: # small optimization: self.k[key] = self.character_cls self.results.append((self.character_cls, [key])) else: if self.verbose: print("matched", d) self.results.append((d, self.stack + [key])) self.stack = [] self.k = self.ck def get(self): if self.results: return self.results.popleft() else: return None def empty(self): return not self.results