| 
									
										
										
										
											2024-05-05 21:32:23 +02:00
										 |  |  | #   Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #                        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): | 
					
						
							| 
									
										
										
										
											2024-05-17 06:13:24 -04:00
										 |  |  |     def __init__(self, keymap, verbose=False, invalid_cls=None, character_cls=None): | 
					
						
							| 
									
										
										
										
											2024-05-05 21:32:23 +02:00
										 |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-17 06:13:24 -04:00
										 |  |  |     def empty(self) -> bool: | 
					
						
							| 
									
										
										
										
											2024-05-05 21:32:23 +02:00
										 |  |  |         return not self.results |