Uses curses to have a proper terminal UI
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
		@@ -1,6 +1,7 @@
 | 
				
			|||||||
# Copyright (C) 2020 by eichhornchen, ÿnérant
 | 
					# Copyright (C) 2020 by eichhornchen, ÿnérant
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import curses
 | 
				
			||||||
import socket
 | 
					import socket
 | 
				
			||||||
from argparse import ArgumentParser
 | 
					from argparse import ArgumentParser
 | 
				
			||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
@@ -8,6 +9,8 @@ from ipaddress import IPv6Address
 | 
				
			|||||||
from threading import Thread
 | 
					from threading import Thread
 | 
				
			||||||
from typing import Any, List, Optional, Tuple
 | 
					from typing import Any, List, Optional, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from squinnondation.term_manager import TermManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Squinnondation:
 | 
					class Squinnondation:
 | 
				
			||||||
    args: Any
 | 
					    args: Any
 | 
				
			||||||
@@ -43,28 +46,48 @@ class Squinnondation:
 | 
				
			|||||||
        instance = Squinnondation()
 | 
					        instance = Squinnondation()
 | 
				
			||||||
        instance.parse_arguments()
 | 
					        instance.parse_arguments()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        squirrel = Squirrel(input("Enter your nickname: "), instance.bind_address, instance.bind_port)
 | 
					        with TermManager() as term_manager:
 | 
				
			||||||
 | 
					            screen = term_manager.screen
 | 
				
			||||||
 | 
					            screen.addstr(0, 0, "Enter your nickname: ")
 | 
				
			||||||
 | 
					            nickname = screen.getstr().decode("UTF-8")
 | 
				
			||||||
 | 
					            squirrel = Squirrel(nickname, instance.bind_address, instance.bind_port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not instance.args.bind_only:
 | 
					            if not instance.args.bind_only:
 | 
				
			||||||
            hazelnut = Hazelnut(address=instance.client_address, port=instance.client_port)
 | 
					                hazelnut = Hazelnut(address=instance.client_address, port=instance.client_port)
 | 
				
			||||||
            squirrel.hazelnuts[(instance.client_address, instance.client_port)] = hazelnut
 | 
					                squirrel.hazelnuts[(instance.client_address, instance.client_port)] = hazelnut
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Worm(squirrel).start()
 | 
					            squirrel.history = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while True:
 | 
					            def refresh() -> None:
 | 
				
			||||||
            msg = f"<{squirrel.nickname}> {input(f'<{squirrel.nickname}> ')}"
 | 
					                screen.clear()
 | 
				
			||||||
            for hazelnut in list(squirrel.hazelnuts.values()):
 | 
					                screen.refresh()
 | 
				
			||||||
                pkt = Packet()
 | 
					                for i, msg in enumerate(squirrel.history[max(0, len(squirrel.history) - curses.LINES + 2):]):
 | 
				
			||||||
                pkt.magic = 95
 | 
					                    screen.addstr(i, 0, msg)
 | 
				
			||||||
                pkt.version = 0
 | 
					
 | 
				
			||||||
                tlv = DataTLV()
 | 
					                screen.addstr(curses.LINES - 1, 0, f"<{squirrel.nickname}> ")
 | 
				
			||||||
                tlv.data = msg.encode("UTF-8")
 | 
					                screen.refresh()
 | 
				
			||||||
                tlv.sender_id = 42
 | 
					            squirrel.refresh = refresh
 | 
				
			||||||
                tlv.nonce = 18
 | 
					
 | 
				
			||||||
                tlv.length = len(msg) + 1 + 1 + 8 + 4
 | 
					            Worm(squirrel).start()
 | 
				
			||||||
                pkt.body = [tlv]
 | 
					
 | 
				
			||||||
                pkt.body_length = tlv.length + 2
 | 
					            while True:
 | 
				
			||||||
                squirrel.send_packet(hazelnut, pkt)
 | 
					                refresh()
 | 
				
			||||||
 | 
					                msg = screen.getstr().decode("UTF-8")
 | 
				
			||||||
 | 
					                msg = f"<{squirrel.nickname}> {msg}"
 | 
				
			||||||
 | 
					                squirrel.history.append(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for hazelnut in list(squirrel.hazelnuts.values()):
 | 
				
			||||||
 | 
					                    pkt = Packet()
 | 
				
			||||||
 | 
					                    pkt.magic = 95
 | 
				
			||||||
 | 
					                    pkt.version = 0
 | 
				
			||||||
 | 
					                    tlv = DataTLV()
 | 
				
			||||||
 | 
					                    tlv.data = msg.encode("UTF-8")
 | 
				
			||||||
 | 
					                    tlv.sender_id = 42
 | 
				
			||||||
 | 
					                    tlv.nonce = 18
 | 
				
			||||||
 | 
					                    tlv.length = len(tlv.data) + 1 + 1 + 8 + 4
 | 
				
			||||||
 | 
					                    pkt.body = [tlv]
 | 
				
			||||||
 | 
					                    pkt.body_length = tlv.length + 2
 | 
				
			||||||
 | 
					                    squirrel.send_packet(hazelnut, pkt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TLV:
 | 
					class TLV:
 | 
				
			||||||
@@ -415,10 +438,12 @@ class Worm(Thread):
 | 
				
			|||||||
        self.squirrel = squirrel
 | 
					        self.squirrel = squirrel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self) -> None:
 | 
					    def run(self) -> None:
 | 
				
			||||||
 | 
					        self.squirrel.history = []
 | 
				
			||||||
        while True:
 | 
					        while True:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                pkt, hazelnut = self.squirrel.receive_packet()
 | 
					                pkt, hazelnut = self.squirrel.receive_packet()
 | 
				
			||||||
            except ValueError as error:
 | 
					            except ValueError as error:
 | 
				
			||||||
                print("An error occured while receiving a packet: ", error)
 | 
					                print("An error occured while receiving a packet: ", error)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                print(pkt.body[0].data.decode('UTF-8'))
 | 
					                self.squirrel.history.append(pkt.body[0].data.decode('UTF-8'))
 | 
				
			||||||
 | 
					                self.squirrel.refresh()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										38
									
								
								squinnondation/term_manager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								squinnondation/term_manager.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import curses
 | 
				
			||||||
 | 
					from types import TracebackType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TermManager:  # pragma: no cover
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    The TermManager object initializes the terminal, returns a screen object and
 | 
				
			||||||
 | 
					    de-initializes the terminal after use
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.screen = curses.initscr()
 | 
				
			||||||
 | 
					        # convert escapes sequences to curses abstraction
 | 
				
			||||||
 | 
					        self.screen.keypad(True)
 | 
				
			||||||
 | 
					        # stop printing typed keys to the terminal
 | 
				
			||||||
 | 
					        # curses.noecho()
 | 
				
			||||||
 | 
					        # send keys through without having to press <enter>
 | 
				
			||||||
 | 
					        # curses.cbreak()
 | 
				
			||||||
 | 
					        # make cursor invisible
 | 
				
			||||||
 | 
					        # curses.curs_set(False)
 | 
				
			||||||
 | 
					        # Catch mouse events
 | 
				
			||||||
 | 
					        curses.mousemask(True)
 | 
				
			||||||
 | 
					        # Enable colors
 | 
				
			||||||
 | 
					        curses.start_color()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __enter__(self):
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __exit__(self, exc_type: type, exc_value: Exception,
 | 
				
			||||||
 | 
					                 exc_traceback: TracebackType) -> None:
 | 
				
			||||||
 | 
					        # restore the terminal to its original state
 | 
				
			||||||
 | 
					        self.screen.keypad(False)
 | 
				
			||||||
 | 
					        # curses.echo()
 | 
				
			||||||
 | 
					        # curses.nocbreak()
 | 
				
			||||||
 | 
					        # curses.curs_set(True)
 | 
				
			||||||
 | 
					        curses.endwin()
 | 
				
			||||||
		Reference in New Issue
	
	Block a user