mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
moar keyboard
This commit is contained in:
parent
dec9aedb19
commit
670be54348
@ -1,14 +1,29 @@
|
|||||||
|
import asyncio
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import multiprocessing.queues
|
import multiprocessing.queues
|
||||||
import queue
|
import queue
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from typing import Set
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
|
||||||
from . import gpio
|
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):
|
class Ps2Keyboard(multiprocessing.Process):
|
||||||
def __init__(self, clock: int, data: int, pulse: float) -> None:
|
def __init__(self, clock: int, data: int, pulse: float) -> None:
|
||||||
super().__init__(daemon=True)
|
super().__init__(daemon=True)
|
||||||
@ -17,39 +32,65 @@ class Ps2Keyboard(multiprocessing.Process):
|
|||||||
self.__data = gpio.set_output(data, initial=True)
|
self.__data = gpio.set_output(data, initial=True)
|
||||||
self.__pulse = pulse
|
self.__pulse = pulse
|
||||||
|
|
||||||
|
self.__pressed_keys: Set[str] = set()
|
||||||
|
self.__lock = asyncio.Lock()
|
||||||
self.__queue: multiprocessing.queues.Queue = multiprocessing.Queue()
|
self.__queue: multiprocessing.queues.Queue = multiprocessing.Queue()
|
||||||
self.__event = multiprocessing.Event()
|
|
||||||
|
self.__stop_event = multiprocessing.Event()
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
get_logger().info("Starting keyboard daemon ...")
|
get_logger().info("Starting keyboard daemon ...")
|
||||||
super().start()
|
super().start()
|
||||||
|
|
||||||
def stop(self) -> None:
|
async def send_event(self, key: str, state: bool) -> None:
|
||||||
get_logger().info("Stopping keyboard daemon ...")
|
if not self.__stop_event.is_set():
|
||||||
self.__event.set()
|
async with self.__lock:
|
||||||
self.join()
|
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))
|
||||||
|
|
||||||
def send_event(self, code: str, state: bool) -> None:
|
async def clear_events(self) -> None:
|
||||||
if state:
|
if not self.__stop_event.is_set():
|
||||||
get_logger().info("Key pressed: %s", code)
|
async with self.__lock:
|
||||||
else:
|
self.__unsafe_clear_events()
|
||||||
get_logger().info("Key released: %s", code)
|
|
||||||
# TODO: self.__queue.put(code)
|
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
async def cleanup(self) -> None:
|
||||||
|
async with self.__lock:
|
||||||
if self.is_alive():
|
if self.is_alive():
|
||||||
self.stop()
|
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 __unsafe_clear_events(self) -> None:
|
||||||
|
for key in self.__pressed_keys:
|
||||||
|
self.__queue.put(_KeyEvent(key, False))
|
||||||
|
self.__pressed_keys.clear()
|
||||||
|
|
||||||
|
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:
|
def run(self) -> None:
|
||||||
with gpio.bcm():
|
with gpio.bcm():
|
||||||
try:
|
try:
|
||||||
while not self.__event.is_set():
|
while True:
|
||||||
try:
|
try:
|
||||||
code = self.__queue.get(timeout=0.1)
|
event = self.__queue.get(timeout=0.1)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
for code in _key_event_to_ps2_codes(event):
|
||||||
self.__send_byte(code)
|
self.__send_byte(code)
|
||||||
|
if self.__stop_event.is_set() and self.__queue.qsize() == 0:
|
||||||
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
get_logger().exception("Unhandled exception")
|
get_logger().exception("Unhandled exception")
|
||||||
raise
|
raise
|
||||||
|
|||||||
@ -135,11 +135,11 @@ class Server: # pylint: disable=too-many-instance-attributes
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Can't parse JSON event from websocket")
|
logger.exception("Can't parse JSON event from websocket")
|
||||||
else:
|
else:
|
||||||
if event.get("event_type") == "key_event":
|
if event.get("event_type") == "key":
|
||||||
key_code = str(event.get("key_code", ""))[:64].strip()
|
key = str(event.get("key", ""))[:64].strip()
|
||||||
key_state = event.get("key_state")
|
state = event.get("state")
|
||||||
if key_code and key_state in [True, False]:
|
if key and state in [True, False]:
|
||||||
self.__keyboard.send_event(key_code, key_state)
|
await self.__keyboard.send_event(key, state)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
logger.error("Invalid websocket event: %r", event)
|
logger.error("Invalid websocket event: %r", event)
|
||||||
@ -236,7 +236,7 @@ class Server: # pylint: disable=too-many-instance-attributes
|
|||||||
await self.__remove_socket(ws)
|
await self.__remove_socket(ws)
|
||||||
|
|
||||||
async def __on_cleanup(self, _: aiohttp.web.Application) -> None:
|
async def __on_cleanup(self, _: aiohttp.web.Application) -> None:
|
||||||
self.__keyboard.cleanup()
|
await self.__keyboard.cleanup()
|
||||||
await self.__streamer.cleanup()
|
await self.__streamer.cleanup()
|
||||||
await self.__msd.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 def __remove_socket(self, ws: aiohttp.web.WebSocketResponse) -> None:
|
||||||
async with self.__sockets_lock:
|
async with self.__sockets_lock:
|
||||||
|
await self.__keyboard.clear_events()
|
||||||
try:
|
try:
|
||||||
self.__sockets.remove(ws)
|
self.__sockets.remove(ws)
|
||||||
get_logger().info("Removed client socket: remote=%s; id=%d; active=%d",
|
get_logger().info("Removed client socket: remote=%s; id=%d; active=%d",
|
||||||
|
|||||||
@ -42,9 +42,9 @@ function onKeyEvent(event, state) {
|
|||||||
// TODO: run this code under the lock
|
// TODO: run this code under the lock
|
||||||
console.log("Key", (state ? "pressed:" : "released:"), event)
|
console.log("Key", (state ? "pressed:" : "released:"), event)
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
event_type: "key_event",
|
event_type: "key",
|
||||||
key_code: event.code,
|
key: event.code,
|
||||||
key_state: state,
|
state: state,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user