|
1 | | -import os |
2 | 1 | import sys |
3 | 2 | import termios |
4 | | -import threading |
5 | 3 | import tty |
| 4 | +from contextlib import contextmanager |
| 5 | + |
| 6 | +from anyio import create_task_group |
| 7 | +from anyio.streams.file import FileReadStream, FileWriteStream |
6 | 8 |
|
7 | 9 | from jumpstarter.client import DriverClient |
8 | 10 |
|
9 | 11 |
|
| 12 | +class ConsoleExit(Exception): |
| 13 | + pass |
| 14 | + |
| 15 | + |
10 | 16 | class Console: |
11 | 17 | def __init__(self, serial_client: DriverClient): |
12 | 18 | self.serial_client = serial_client |
13 | 19 |
|
14 | 20 | def run(self): |
15 | | - with self.serial_client.stream() as stream: |
16 | | - self._run(stream) |
| 21 | + with self.setraw(): |
| 22 | + self.serial_client.portal.call(self.__run) |
17 | 23 |
|
18 | | - def _run(self, stream): |
19 | | - self._stream = stream |
20 | | - self._old_settings = termios.tcgetattr(0) |
| 24 | + @contextmanager |
| 25 | + def setraw(self): |
| 26 | + original = termios.tcgetattr(sys.stdin.fileno()) |
21 | 27 | try: |
22 | 28 | tty.setraw(sys.stdin.fileno()) |
23 | | - thread_serial_to_stdout = threading.Thread(target=self._copy_serial_to_stdout, daemon=True) |
24 | | - thread_serial_to_stdout.start() |
25 | | - ctrl_b_count = 0 |
26 | | - while True: |
27 | | - data = sys.stdin.buffer.read(1) |
28 | | - if not data: |
29 | | - continue |
30 | | - if data == b"\x02": # Ctrl-B |
31 | | - ctrl_b_count += 1 |
32 | | - if ctrl_b_count == 3: |
33 | | - return |
34 | | - else: |
35 | | - ctrl_b_count = 0 |
36 | | - stream.send(data) |
| 29 | + yield |
37 | 30 | finally: |
38 | | - self._reset_terminal() |
| 31 | + termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, original) |
| 32 | + # Clear screen and move cursor to top-left (like \033c\033[2J\033[H). |
| 33 | + print("\033c\033[2J\033[H", end="") |
39 | 34 |
|
40 | | - def _reset_terminal(self): |
41 | | - termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, self._old_settings) |
42 | | - # Clear screen and move cursor to top-left (like \033c\033[2J\033[H). |
43 | | - print("\033c\033[2J\033[H", end="") |
| 35 | + async def __run(self): |
| 36 | + async with self.serial_client.stream_async(method="connect") as stream: |
| 37 | + try: |
| 38 | + async with create_task_group() as tg: |
| 39 | + tg.start_soon(self.__serial_to_stdout, stream) |
| 40 | + tg.start_soon(self.__stdin_to_serial, stream) |
| 41 | + except* ConsoleExit: |
| 42 | + pass |
44 | 43 |
|
45 | | - def _copy_serial_to_stdout(self): |
46 | | - try: |
47 | | - while True: |
48 | | - data = self._stream.receive() |
49 | | - os.write(sys.stdout.fileno(), data) |
50 | | - sys.stdout.flush() |
51 | | - finally: |
52 | | - self._reset_terminal() |
| 44 | + async def __serial_to_stdout(self, stream): |
| 45 | + stdout = FileWriteStream(sys.stdout.buffer) |
| 46 | + while True: |
| 47 | + data = await stream.receive() |
| 48 | + await stdout.send(data) |
| 49 | + sys.stdout.flush() |
| 50 | + |
| 51 | + async def __stdin_to_serial(self, stream): |
| 52 | + stdin = FileReadStream(sys.stdin.buffer) |
| 53 | + ctrl_b_count = 0 |
| 54 | + while True: |
| 55 | + data = await stdin.receive(max_bytes=1) |
| 56 | + if not data: |
| 57 | + continue |
| 58 | + if data == b"\x02": # Ctrl-B |
| 59 | + ctrl_b_count += 1 |
| 60 | + if ctrl_b_count == 3: |
| 61 | + raise ConsoleExit |
| 62 | + else: |
| 63 | + ctrl_b_count = 0 |
| 64 | + await stream.send(data) |
0 commit comments