Cut the main loop into smaller functions to have more modularity
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
parent
e8fa0ece22
commit
00e24d74ee
|
@ -73,102 +73,7 @@ class Squinnondation:
|
||||||
squirrel.hazelnuts[(instance.args.client_address, instance.args.client_port)] = hazelnut
|
squirrel.hazelnuts[(instance.args.client_address, instance.args.client_port)] = hazelnut
|
||||||
|
|
||||||
Worm(squirrel).start()
|
Worm(squirrel).start()
|
||||||
|
squirrel.wait_for_key()
|
||||||
while True:
|
|
||||||
squirrel.refresh_history()
|
|
||||||
squirrel.refresh_input()
|
|
||||||
if not instance.no_emoji:
|
|
||||||
squirrel.refresh_emoji_pad()
|
|
||||||
key = screen.getkey(curses.LINES - 1, 3 + len(squirrel.nickname) + squirrel.input_index)
|
|
||||||
if key == "\x7f": # backspace
|
|
||||||
if squirrel.input_index:
|
|
||||||
squirrel.input_index -= 1
|
|
||||||
squirrel.input_buffer = squirrel.input_buffer[:squirrel.input_index] \
|
|
||||||
+ squirrel.input_buffer[squirrel.input_index + 1:]
|
|
||||||
continue
|
|
||||||
elif key == "KEY_LEFT":
|
|
||||||
squirrel.input_index = max(0, squirrel.input_index - 1)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_RIGHT":
|
|
||||||
squirrel.input_index = min(len(squirrel.input_buffer), squirrel.input_index + 1)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_UP":
|
|
||||||
squirrel.last_line = min(max(curses.LINES - 3, squirrel.last_line - 1), len(squirrel.history) - 1)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_DOWN":
|
|
||||||
squirrel.last_line = min(len(squirrel.history) - 1, squirrel.last_line + 1)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_PPAGE":
|
|
||||||
squirrel.last_line = min(max(curses.LINES - 3, squirrel.last_line - (curses.LINES - 3)),
|
|
||||||
len(squirrel.history) - 1)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_NPAGE":
|
|
||||||
squirrel.last_line = min(len(squirrel.history) - 1, squirrel.last_line + (curses.LINES - 3))
|
|
||||||
continue
|
|
||||||
elif key == "KEY_HOME":
|
|
||||||
squirrel.input_index = 0
|
|
||||||
continue
|
|
||||||
elif key == "KEY_END":
|
|
||||||
squirrel.input_index = len(squirrel.input_buffer)
|
|
||||||
continue
|
|
||||||
elif key == "KEY_RESIZE":
|
|
||||||
continue
|
|
||||||
elif key == "KEY_MOUSE":
|
|
||||||
try:
|
|
||||||
_, x, y, _, attr = curses.getmouse()
|
|
||||||
except curses.error:
|
|
||||||
# This is not a valid click
|
|
||||||
continue
|
|
||||||
if not instance.no_emoji:
|
|
||||||
if y == curses.LINES - 1 and x >= curses.COLS - 3:
|
|
||||||
# Click on the emoji
|
|
||||||
squirrel.emoji_panel_page *= -1
|
|
||||||
elif squirrel.emoji_panel_page > 0 and y == curses.LINES - 4 and x >= curses.COLS - 5:
|
|
||||||
squirrel.emoji_panel_page += 1
|
|
||||||
elif squirrel.emoji_panel_page > 1 and y == curses.LINES - curses.LINES // 2 - 1 \
|
|
||||||
and x >= curses.COLS - 5:
|
|
||||||
squirrel.emoji_panel_page -= 1
|
|
||||||
elif squirrel.emoji_panel_page > 0 and y >= curses.LINES // 2 - 1 and x >= curses.COLS // 2 - 1:
|
|
||||||
pad_y, pad_x = y - (curses.LINES - curses.LINES // 2) + 1,\
|
|
||||||
(x - (curses.COLS - curses.COLS // 3)) // 2
|
|
||||||
height, width = squirrel.emoji_pad.getmaxyx()
|
|
||||||
height -= 1
|
|
||||||
width -= 1
|
|
||||||
|
|
||||||
emojis = list(unicode_codes.UNICODE_EMOJI)
|
|
||||||
emojis = [c for c in emojis if len(c) == 1]
|
|
||||||
size = (height - 2) * (width - 4) // 2
|
|
||||||
page = emojis[(squirrel.emoji_panel_page - 1) * size:squirrel.emoji_panel_page * size]
|
|
||||||
index = pad_y * (width - 4) // 2 + pad_x
|
|
||||||
char = page[index]
|
|
||||||
if char:
|
|
||||||
demojized = emoji.demojize(char)
|
|
||||||
if char != demojized:
|
|
||||||
for c in reversed(demojized):
|
|
||||||
curses.ungetch(c)
|
|
||||||
continue
|
|
||||||
elif len(key) > 1:
|
|
||||||
squirrel.add_system_message(f"unmanaged key press: {key}")
|
|
||||||
continue
|
|
||||||
elif key != "\n":
|
|
||||||
squirrel.input_buffer = squirrel.input_buffer[:squirrel.input_index] + key \
|
|
||||||
+ squirrel.input_buffer[squirrel.input_index:]
|
|
||||||
squirrel.input_index += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
msg = squirrel.input_buffer
|
|
||||||
squirrel.input_buffer = ""
|
|
||||||
squirrel.input_index = 0
|
|
||||||
|
|
||||||
if not msg:
|
|
||||||
continue
|
|
||||||
|
|
||||||
msg = f"<{squirrel.nickname}> {msg}"
|
|
||||||
squirrel.add_message(msg)
|
|
||||||
|
|
||||||
for hazelnut in list(squirrel.hazelnuts.values()):
|
|
||||||
pkt = Packet.construct(DataTLV.construct(msg))
|
|
||||||
squirrel.send_packet(hazelnut, pkt)
|
|
||||||
|
|
||||||
|
|
||||||
class TLV:
|
class TLV:
|
||||||
|
@ -238,8 +143,7 @@ class Pad1TLV(TLV):
|
||||||
|
|
||||||
def handle(self, squirrel: "Squirrel", sender: "Hazelnut") -> None:
|
def handle(self, squirrel: "Squirrel", sender: "Hazelnut") -> None:
|
||||||
# TODO Add some easter eggs
|
# TODO Add some easter eggs
|
||||||
squirrel.add_system_message(f"For each byte in the packet that I received, you will die today. "
|
squirrel.add_system_message("For each byte in the packet that I received, you will die today. And eat cookies.")
|
||||||
"And eat cookies.")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tlv_length(self) -> int:
|
def tlv_length(self) -> int:
|
||||||
|
@ -312,7 +216,7 @@ class HelloTLV(TLV):
|
||||||
+ (":3_hearts:" if self.is_long else "smiling_eyes:"))
|
+ (":3_hearts:" if self.is_long else "smiling_eyes:"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_long(self):
|
def is_long(self) -> bool:
|
||||||
return self.length == 16
|
return self.length == 16
|
||||||
|
|
||||||
|
|
||||||
|
@ -608,6 +512,140 @@ class Squirrel(Hazelnut):
|
||||||
"""
|
"""
|
||||||
return self.socket.recvfrom(1024)
|
return self.socket.recvfrom(1024)
|
||||||
|
|
||||||
|
def wait_for_key(self) -> None:
|
||||||
|
"""
|
||||||
|
Infinite loop where we are waiting for a key of the user.
|
||||||
|
"""
|
||||||
|
while True:
|
||||||
|
self.refresh_history()
|
||||||
|
self.refresh_input()
|
||||||
|
if not self.squinnondation.no_emoji:
|
||||||
|
self.refresh_emoji_pad()
|
||||||
|
key = self.squinnondation.screen.getkey(curses.LINES - 1, 3 + len(self.nickname) + self.input_index)
|
||||||
|
|
||||||
|
if key == "KEY_MOUSE":
|
||||||
|
try:
|
||||||
|
_, x, y, _, attr = curses.getmouse()
|
||||||
|
except curses.error:
|
||||||
|
# This is not a valid click
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.handle_mouse_click(y, x, attr)
|
||||||
|
finally:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.handle_key_pressed(key)
|
||||||
|
|
||||||
|
def handle_key_pressed(self, key: str) -> None:
|
||||||
|
"""
|
||||||
|
Process the key press from the user.
|
||||||
|
"""
|
||||||
|
if key == "\x7f": # backspace
|
||||||
|
# delete character at the good position
|
||||||
|
if self.input_index:
|
||||||
|
self.input_index -= 1
|
||||||
|
self.input_buffer = self.input_buffer[:self.input_index] + self.input_buffer[self.input_index + 1:]
|
||||||
|
return
|
||||||
|
elif key == "KEY_LEFT":
|
||||||
|
# Navigate in the message to the left
|
||||||
|
self.input_index = max(0, self.input_index - 1)
|
||||||
|
return
|
||||||
|
elif key == "KEY_RIGHT":
|
||||||
|
# Navigate in the message to the right
|
||||||
|
self.input_index = min(len(self.input_buffer), self.input_index + 1)
|
||||||
|
return
|
||||||
|
elif key == "KEY_UP":
|
||||||
|
# Scroll up in the history
|
||||||
|
self.last_line = min(max(curses.LINES - 3, self.last_line - 1), len(self.history) - 1)
|
||||||
|
return
|
||||||
|
elif key == "KEY_DOWN":
|
||||||
|
# Scroll down in the history
|
||||||
|
self.last_line = min(len(self.history) - 1, self.last_line + 1)
|
||||||
|
return
|
||||||
|
elif key == "KEY_PPAGE":
|
||||||
|
# Page up in the history
|
||||||
|
self.last_line = min(max(curses.LINES - 3, self.last_line - (curses.LINES - 3)), len(self.history) - 1)
|
||||||
|
return
|
||||||
|
elif key == "KEY_NPAGE":
|
||||||
|
# Page down in the history
|
||||||
|
self.last_line = min(len(self.history) - 1, self.last_line + (curses.LINES - 3))
|
||||||
|
return
|
||||||
|
elif key == "KEY_HOME":
|
||||||
|
# Place the cursor at the beginning of the typing word
|
||||||
|
self.input_index = 0
|
||||||
|
return
|
||||||
|
elif key == "KEY_END":
|
||||||
|
# Place the cursor at the end of the typing word
|
||||||
|
self.input_index = len(self.input_buffer)
|
||||||
|
return
|
||||||
|
elif len(key) > 1:
|
||||||
|
# Unmanaged complex key
|
||||||
|
return
|
||||||
|
elif key != "\n":
|
||||||
|
# Insert the pressed key in the current message
|
||||||
|
self.input_buffer = self.input_buffer[:self.input_index] + key + self.input_buffer[self.input_index:]
|
||||||
|
self.input_index += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
# Send message to neighbours
|
||||||
|
msg = self.input_buffer
|
||||||
|
self.input_buffer = ""
|
||||||
|
self.input_index = 0
|
||||||
|
|
||||||
|
if not msg:
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = f"<{self.nickname}> {msg}"
|
||||||
|
self.add_message(msg)
|
||||||
|
|
||||||
|
pkt = Packet.construct(DataTLV.construct(msg))
|
||||||
|
for hazelnut in list(self.hazelnuts.values()):
|
||||||
|
self.send_packet(hazelnut, pkt)
|
||||||
|
|
||||||
|
def handle_mouse_click(self, y: int, x: int, attr: int) -> None:
|
||||||
|
"""
|
||||||
|
The user clicks on the screen, at coordinates (y, x).
|
||||||
|
According to the position, we can indicate what can be done.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.squinnondation.no_emoji:
|
||||||
|
if y == curses.LINES - 1 and x >= curses.COLS - 3:
|
||||||
|
# Click on the emoji, open or close the emoji pad
|
||||||
|
self.emoji_panel_page *= -1
|
||||||
|
elif self.emoji_panel_page > 0 and y == curses.LINES - 4 and x >= curses.COLS - 5:
|
||||||
|
# Open next emoji page
|
||||||
|
self.emoji_panel_page += 1
|
||||||
|
elif self.emoji_panel_page > 1 and y == curses.LINES - curses.LINES // 2 - 1 \
|
||||||
|
and x >= curses.COLS - 5:
|
||||||
|
# Open previous emoji page
|
||||||
|
self.emoji_panel_page -= 1
|
||||||
|
elif self.emoji_panel_page > 0 and y >= curses.LINES // 2 - 1 and x >= curses.COLS // 2 - 1:
|
||||||
|
pad_y, pad_x = y - (curses.LINES - curses.LINES // 2) + 1, \
|
||||||
|
(x - (curses.COLS - curses.COLS // 3) + 1) // 2
|
||||||
|
# Click on an emoji on the pad to autocomplete an emoji
|
||||||
|
self.click_on_emoji_pad(pad_y, pad_x)
|
||||||
|
|
||||||
|
def click_on_emoji_pad(self, pad_y: int, pad_x: int) -> None:
|
||||||
|
"""
|
||||||
|
The emoji pad contains the list of all available emojis.
|
||||||
|
Clicking on a emoji auto-complete the emoji in the input pad.
|
||||||
|
"""
|
||||||
|
height, width = self.emoji_pad.getmaxyx()
|
||||||
|
height -= 1
|
||||||
|
width -= 1
|
||||||
|
|
||||||
|
emojis = list(unicode_codes.UNICODE_EMOJI)
|
||||||
|
emojis = [c for c in emojis if len(c) == 1]
|
||||||
|
size = (height - 2) * (width - 4) // 2
|
||||||
|
page = emojis[(self.emoji_panel_page - 1) * size:self.emoji_panel_page * size]
|
||||||
|
index = pad_y * (width - 4) // 2 + pad_x
|
||||||
|
char = page[index]
|
||||||
|
if char:
|
||||||
|
demojized = emoji.demojize(char)
|
||||||
|
if char != demojized:
|
||||||
|
for c in reversed(demojized):
|
||||||
|
curses.ungetch(c)
|
||||||
|
|
||||||
def add_message(self, msg: str) -> None:
|
def add_message(self, msg: str) -> None:
|
||||||
"""
|
"""
|
||||||
Store a new message into the history.
|
Store a new message into the history.
|
||||||
|
|
Loading…
Reference in New Issue