_suppress.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. from threading import Lock, Thread
  2. from timeit import default_timer as timer
  3. from ._keyboard_event import normalize_name
  4. import re
  5. class KeyTable(object):
  6. _keys = {}
  7. _write = Lock() # Required to edit keys
  8. _table = {}
  9. _time = -1
  10. _elapsed = 0 # Maximum time that has elapsed so far in the sequence
  11. _read = Lock() # Required to edit table
  12. _in_sequence = False
  13. _keys_suppressed = [] # List of keys that have been suppressed so far in the sequence
  14. _disable = False # Disables key suppression during replay to avoid infinite loop
  15. SEQUENCE_END = 2 # Delimeter that signifies the end of the sequence
  16. def __init__(self, press_key, release_key):
  17. self.press_key = press_key
  18. self.release_key = release_key
  19. def is_allowed(self, key, is_up, advance=True):
  20. """
  21. The goal of this function is to be very fast. This is accomplished
  22. through the table structure, which ensures that we only need to
  23. check whether `key is in self._table` and change what variable
  24. is referenced by `self._table`.
  25. Unfortunately, handling timeouts properly has added significantly to
  26. the logic required, but the function should still be well within required
  27. time limits.
  28. """
  29. if self._disable:
  30. return True
  31. if key != self.SEQUENCE_END:
  32. key = re.sub('(left|right) ', '', key)
  33. time = timer()
  34. if self._time == -1:
  35. elapsed = 0
  36. else:
  37. elapsed = time - self._time
  38. if self._elapsed > elapsed:
  39. elapsed = self._elapsed
  40. if is_up:
  41. if self._in_sequence:
  42. if key != self.SEQUENCE_END:
  43. self._keys_suppressed.append((key, is_up))
  44. return False
  45. else:
  46. advance = False
  47. in_sequence = key in self._table and elapsed < self._table[key][0]
  48. in_keys = key in self._keys
  49. suppress = in_sequence or in_keys
  50. if advance:
  51. self._read.acquire()
  52. if in_sequence and self._table[key][2]:
  53. del self._keys_suppressed[:]
  54. if in_sequence and self._table[key][1]:
  55. self._table = self._table[key][1]
  56. if self._time != -1:
  57. self._elapsed = elapsed
  58. self._time = -1
  59. elif in_keys and self._keys[key][1]:
  60. self._table = self._keys[key][1]
  61. if self._time != -1:
  62. self._elapsed = elapsed
  63. self._time = -1
  64. self._replay_keys()
  65. del self._keys_suppressed[:]
  66. else:
  67. self._table = self._keys
  68. self._time = -1
  69. self._elapsed = -1
  70. self._replay_keys()
  71. del self._keys_suppressed[:]
  72. self._in_sequence = in_sequence
  73. self._read.release()
  74. if key != self.SEQUENCE_END and suppress:
  75. self._keys_suppressed.append((key, is_up))
  76. return not suppress
  77. def complete_sequence(self):
  78. if self.SEQUENCE_END in self._table:
  79. self.is_allowed(self.SEQUENCE_END, False)
  80. self._read.acquire()
  81. self._time = timer()
  82. self._read.release()
  83. else:
  84. self._read.acquire()
  85. self._time = -1
  86. self._elapsed = 0
  87. self._table = self._keys
  88. self._replay_keys()
  89. del self._keys_suppressed[:]
  90. self._read.release()
  91. def _replay_keys(self):
  92. self._disable = True
  93. for key, is_up in self._keys_suppressed:
  94. if is_up:
  95. self.release_key(key)
  96. else:
  97. self.press_key(key)
  98. self._disable = False
  99. def _refresh(self):
  100. self._read.acquire()
  101. self._disable = False
  102. self._table = self._keys
  103. self._read.release()
  104. def _acquire_table(self, sequence, table, timeout):
  105. """
  106. Returns a flat (single level) dictionary
  107. :param sequence:
  108. :param table:
  109. :return:
  110. """
  111. el = sequence.pop(0)
  112. if el not in table:
  113. table[el] = (timeout, {}, False)
  114. if table[el][0] < timeout:
  115. table[el][0] = timeout
  116. if sequence:
  117. return self._acquire_table(sequence, table[el][1], timeout)
  118. else:
  119. return table
  120. def suppress_sequence(self, sequence, timeout):
  121. """
  122. Adds keys to the suppress_keys table
  123. :param sequence: List of scan codes
  124. :param timeout: Time allowed to elapse before resetting
  125. """
  126. # the suppress_keys table is organized
  127. # as a dict of dicts so that the critical
  128. # path is only checking whether the
  129. # scan code is 'in current_dict'
  130. flat = []
  131. for subsequence in sequence:
  132. flat.extend(subsequence)
  133. flat.append(self.SEQUENCE_END)
  134. last_index = flat[-1]
  135. self._write.acquire()
  136. table = self._acquire_table(flat, self._keys, timeout)
  137. table[last_index] = (table[last_index][0], table[last_index][1], True)
  138. self._refresh()
  139. self._write.release()
  140. def suppress_none(self):
  141. """
  142. Clears the suppress_keys table and disables
  143. key suppression
  144. :return:
  145. """
  146. self._write.acquire()
  147. self._keys = {}
  148. self._refresh()
  149. self._write.release()
  150. self._read.acquire()
  151. self._disable = True
  152. self._read.release()