summaryrefslogtreecommitdiff
path: root/kvmd
diff options
context:
space:
mode:
Diffstat (limited to 'kvmd')
-rw-r--r--kvmd/kvmd/ps2.py75
-rw-r--r--kvmd/kvmd/server.py13
-rw-r--r--kvmd/web/index.html6
3 files changed, 68 insertions, 26 deletions
diff --git a/kvmd/kvmd/ps2.py b/kvmd/kvmd/ps2.py
index fe20e2c6..0820c13b 100644
--- a/kvmd/kvmd/ps2.py
+++ b/kvmd/kvmd/ps2.py
@@ -1,14 +1,29 @@
+import asyncio
import multiprocessing
import multiprocessing.queues
import queue
import time
+from typing import List
+from typing import Set
+from typing import NamedTuple
+
from .logging import get_logger
from . import gpio
# =====
+class _KeyEvent(NamedTuple):
+ key: str
+ state: bool
+
+
+def _key_event_to_ps2_codes(event: _KeyEvent) -> List[int]:
+ get_logger().info(str(event))
+ return [] # TODO
+
+
class Ps2Keyboard(multiprocessing.Process):
def __init__(self, clock: int, data: int, pulse: float) -> None:
super().__init__(daemon=True)
@@ -17,39 +32,65 @@ class Ps2Keyboard(multiprocessing.Process):
self.__data = gpio.set_output(data, initial=True)
self.__pulse = pulse
+ self.__pressed_keys: Set[str] = set()
+ self.__lock = asyncio.Lock()
self.__queue: multiprocessing.queues.Queue = multiprocessing.Queue()
- self.__event = multiprocessing.Event()
+
+ self.__stop_event = multiprocessing.Event()
def start(self) -> None:
get_logger().info("Starting keyboard daemon ...")
super().start()
- def stop(self) -> None:
- get_logger().info("Stopping keyboard daemon ...")
- self.__event.set()
- self.join()
+ async def send_event(self, key: str, state: bool) -> None:
+ if not self.__stop_event.is_set():
+ async with self.__lock:
+ if state and key not in self.__pressed_keys:
+ self.__pressed_keys.add(key)
+ self.__queue.put(_KeyEvent(key, state))
+ elif not state and key in self.__pressed_keys:
+ self.__pressed_keys.remove(key)
+ self.__queue.put(_KeyEvent(key, state))
+
+ async def clear_events(self) -> None:
+ if not self.__stop_event.is_set():
+ async with self.__lock:
+ self.__unsafe_clear_events()
+
+ async def cleanup(self) -> None:
+ async with self.__lock:
+ if self.is_alive():
+ self.__unsafe_clear_events()
+ get_logger().info("Stopping keyboard daemon ...")
+ self.__stop_event.set()
+ self.join()
+ else:
+ get_logger().warning("Emergency cleaning up keyboard events ...")
+ self.__emergency_clear_events()
- def send_event(self, code: str, state: bool) -> None:
- if state:
- get_logger().info("Key pressed: %s", code)
- else:
- get_logger().info("Key released: %s", code)
- # TODO: self.__queue.put(code)
+ def __unsafe_clear_events(self) -> None:
+ for key in self.__pressed_keys:
+ self.__queue.put(_KeyEvent(key, False))
+ self.__pressed_keys.clear()
- def cleanup(self) -> None:
- if self.is_alive():
- self.stop()
+ def __emergency_clear_events(self) -> None:
+ for key in self.__pressed_keys:
+ for code in _key_event_to_ps2_codes(_KeyEvent(key, False)):
+ self.__send_byte(code)
def run(self) -> None:
with gpio.bcm():
try:
- while not self.__event.is_set():
+ while True:
try:
- code = self.__queue.get(timeout=0.1)
+ event = self.__queue.get(timeout=0.1)
except queue.Empty:
pass
else:
- self.__send_byte(code)
+ for code in _key_event_to_ps2_codes(event):
+ self.__send_byte(code)
+ if self.__stop_event.is_set() and self.__queue.qsize() == 0:
+ break
except Exception:
get_logger().exception("Unhandled exception")
raise
diff --git a/kvmd/kvmd/server.py b/kvmd/kvmd/server.py
index 71a3a02c..2ec5ae41 100644
--- a/kvmd/kvmd/server.py
+++ b/kvmd/kvmd/server.py
@@ -135,11 +135,11 @@ class Server: # pylint: disable=too-many-instance-attributes
except Exception:
logger.exception("Can't parse JSON event from websocket")
else:
- if event.get("event_type") == "key_event":
- key_code = str(event.get("key_code", ""))[:64].strip()
- key_state = event.get("key_state")
- if key_code and key_state in [True, False]:
- self.__keyboard.send_event(key_code, key_state)
+ if event.get("event_type") == "key":
+ key = str(event.get("key", ""))[:64].strip()
+ state = event.get("state")
+ if key and state in [True, False]:
+ await self.__keyboard.send_event(key, state)
continue
else:
logger.error("Invalid websocket event: %r", event)
@@ -236,7 +236,7 @@ class Server: # pylint: disable=too-many-instance-attributes
await self.__remove_socket(ws)
async def __on_cleanup(self, _: aiohttp.web.Application) -> None:
- self.__keyboard.cleanup()
+ await self.__keyboard.cleanup()
await self.__streamer.cleanup()
await self.__msd.cleanup()
@@ -307,6 +307,7 @@ class Server: # pylint: disable=too-many-instance-attributes
async def __remove_socket(self, ws: aiohttp.web.WebSocketResponse) -> None:
async with self.__sockets_lock:
+ await self.__keyboard.clear_events()
try:
self.__sockets.remove(ws)
get_logger().info("Removed client socket: remote=%s; id=%d; active=%d",
diff --git a/kvmd/web/index.html b/kvmd/web/index.html
index cb61185a..3bfe8cc5 100644
--- a/kvmd/web/index.html
+++ b/kvmd/web/index.html
@@ -42,9 +42,9 @@ function onKeyEvent(event, state) {
// TODO: run this code under the lock
console.log("Key", (state ? "pressed:" : "released:"), event)
ws.send(JSON.stringify({
- event_type: "key_event",
- key_code: event.code,
- key_state: state,
+ event_type: "key",
+ key: event.code,
+ state: state,
}));
}