From ca81118f48c625b24c6385cf2629f2f84c3e824d Mon Sep 17 00:00:00 2001 From: Paul Nameless Date: Wed, 3 Jun 2020 09:35:59 +0800 Subject: [PATCH 1/2] Do curses draw in one thread --- tg/controllers.py | 59 +++++++++++++++++++++++++++++-------------- tg/main.py | 8 +++--- tg/update_handlers.py | 55 ++++++++++++++++++++++------------------ 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/tg/controllers.py b/tg/controllers.py index b350ac2..a705846 100644 --- a/tg/controllers.py +++ b/tg/controllers.py @@ -1,6 +1,7 @@ import curses import logging import os +import queue import threading from datetime import datetime from functools import partial @@ -45,8 +46,8 @@ class Controller: def __init__(self, model: Model, view: View, tg: Tdlib) -> None: self.model = model self.view = view - self.render_lock = threading.RLock() - self.render_status_lock = threading.RLock() + self.queue = queue.Queue() + self.is_running = True self.tg = tg self.chat_size = 0.5 @@ -363,8 +364,10 @@ class Controller: return self.update_status("Info", msg) def update_status(self, level: str, msg: str): - with self.render_status_lock: - self.view.status.draw(f"{level}: {msg}") + self.queue.put(partial(self._update_status, level, msg)) + + def _update_status(self, level: str, msg: str): + self.view.status.draw(f"{level}: {msg}") def edit_msg(self): msg = MsgProxy(self.model.current_msg) @@ -400,9 +403,13 @@ class Controller: def run(self) -> None: try: self.handle(self.chat_bindings, 0.5) + self.queue.put(self.close) except Exception: log.exception("Error happened in main loop") + def close(self): + self.is_running = False + def handle_msgs(self, _: int): rc = self.handle(self.msg_bindings, 0.2) if rc == "QUIT": @@ -431,6 +438,9 @@ class Controller: self.resize() def resize(self): + self.queue.put(self._resize) + + def _resize(self): rows, cols = self.view.stdscr.getmaxyx() # If we didn't clear the screen before doing this, # the original window contents would remain on the screen @@ -443,24 +453,35 @@ class Controller: self.view.status.resize(rows, cols) self.render() - def render(self) -> None: - with self.render_lock: - # using lock here, because render is used from another - # thread by tdlib python wrapper - page_size = self.view.chats.h - chats = self.model.get_chats( - self.model.current_chat, page_size, MSGS_LEFT_SCROLL_THRESHOLD - ) - selected_chat = min( - self.model.current_chat, page_size - MSGS_LEFT_SCROLL_THRESHOLD - ) + def draw(self): + while self.is_running: + try: + log.info("Queue size: %d", self.queue.qsize()) + fun = self.queue.get() + fun() + except Exception: + log.exception("Error happened in draw loop") - self.view.chats.draw(selected_chat, chats) - self.render_msgs() - with self.render_status_lock: - self.view.status.draw() + def render(self) -> None: + self.queue.put(self._render) + + def _render(self) -> None: + page_size = self.view.chats.h + chats = self.model.get_chats( + self.model.current_chat, page_size, MSGS_LEFT_SCROLL_THRESHOLD + ) + selected_chat = min( + self.model.current_chat, page_size - MSGS_LEFT_SCROLL_THRESHOLD + ) + + self.view.chats.draw(selected_chat, chats) + self.render_msgs() + self.view.status.draw() def render_msgs(self) -> None: + self.queue.put(self._render_msgs) + + def _render_msgs(self) -> None: current_msg_idx = self.model.get_current_chat_msg_idx() if current_msg_idx is None: return diff --git a/tg/main.py b/tg/main.py index 2c69351..c281e40 100644 --- a/tg/main.py +++ b/tg/main.py @@ -36,9 +36,11 @@ def run(tg: Tdlib, stdscr: window) -> None: for msg_type, handler in update_handlers.handlers.items(): tg.add_update_handler(msg_type, partial(handler, controller)) - t = threading.Thread(target=controller.run,) - t.start() - t.join() + thread = threading.Thread(target=controller.run,) + thread.daemon = True + thread.start() + + controller.draw() def main(): diff --git a/tg/update_handlers.py b/tg/update_handlers.py index bf6acfd..7f11a2d 100644 --- a/tg/update_handlers.py +++ b/tg/update_handlers.py @@ -1,4 +1,5 @@ import logging +from datetime import datetime from functools import wraps from typing import Any, Callable, Dict @@ -69,8 +70,8 @@ def update_chat_order(controller: Controller, update: Dict[str, Any]): chat_id = update["chat_id"] order = update["order"] - controller.model.chats.update_chat(chat_id, order=order) - controller._refresh_current_chat(current_chat_id) + if controller.model.chats.update_chat(chat_id, order=order): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatTitle") @@ -80,8 +81,8 @@ def update_chat_title(controller: Controller, update: Dict[str, Any]): title = update["title"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat(chat_id, title=title) - controller._refresh_current_chat(current_chat_id) + if controller.model.chats.update_chat(chat_id, title=title): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatIsMarkedAsUnread") @@ -93,10 +94,10 @@ def update_chat_marked_as_unread( is_marked_as_unread = update["is_marked_as_unread"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, is_marked_as_unread=is_marked_as_unread - ) - controller._refresh_current_chat(current_chat_id) + ): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatIsPinned") @@ -107,10 +108,10 @@ def update_chat_is_pinned(controller: Controller, update: Dict[str, Any]): order = update["order"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, is_pinned=is_pinned, order=order - ) - controller._refresh_current_chat(current_chat_id) + ): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatReadOutbox") @@ -120,10 +121,10 @@ def update_chat_read_outbox(controller: Controller, update: Dict[str, Any]): last_read_outbox_message_id = update["last_read_outbox_message_id"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, last_read_outbox_message_id=last_read_outbox_message_id, - ) - controller._refresh_current_chat(current_chat_id) + ): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatReadInbox") @@ -134,12 +135,12 @@ def update_chat_read_inbox(controller: Controller, update: Dict[str, Any]): unread_count = update["unread_count"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, last_read_inbox_message_id=last_read_inbox_message_id, unread_count=unread_count, - ) - controller._refresh_current_chat(current_chat_id) + ): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatDraftMessage") @@ -151,22 +152,26 @@ def update_chat_draft_msg(controller: Controller, update: Dict[str, Any]): order = update["order"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat(chat_id, order=order) - controller._refresh_current_chat(current_chat_id) + if controller.model.chats.update_chat(chat_id, order=order): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatLastMessage") def update_chat_last_msg(controller: Controller, update: Dict[str, Any]): log.info("Proccessing updateChatLastMessage") chat_id = update["chat_id"] - message = update["last_message"] + last_message = update.get("last_message") + if not last_message: + # according to documentation it can be null + log.warning("last_message is null: %s", update) + return order = update["order"] current_chat_id = controller.model.current_chat_id - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, last_message=message, order=order - ) - controller._refresh_current_chat(current_chat_id) + ): + controller._refresh_current_chat(current_chat_id) @update_handler("updateChatNotificationSettings") @@ -174,10 +179,10 @@ def update_chat_notification_settings(controller: Controller, update): log.info("Proccessing update_chat_notification_settings") chat_id = update["chat_id"] notification_settings = update["notification_settings"] - controller.model.chats.update_chat( + if controller.model.chats.update_chat( chat_id, notification_settings=notification_settings - ) - controller.render() + ): + controller.render() @update_handler("updateMessageSendSucceeded") From 30d0d525af981f142a384921ee7680dd5b90c189 Mon Sep 17 00:00:00 2001 From: Paul Nameless Date: Wed, 3 Jun 2020 09:44:06 +0800 Subject: [PATCH 2/2] Fix mypy errors --- tg/controllers.py | 4 ++-- tg/update_handlers.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tg/controllers.py b/tg/controllers.py index a705846..b1efaaa 100644 --- a/tg/controllers.py +++ b/tg/controllers.py @@ -1,10 +1,10 @@ import curses import logging import os -import queue import threading from datetime import datetime from functools import partial +from queue import Queue from tempfile import NamedTemporaryFile from typing import Any, Callable, Dict, Optional @@ -46,7 +46,7 @@ class Controller: def __init__(self, model: Model, view: View, tg: Tdlib) -> None: self.model = model self.view = view - self.queue = queue.Queue() + self.queue: Queue = Queue() self.is_running = True self.tg = tg self.chat_size = 0.5 diff --git a/tg/update_handlers.py b/tg/update_handlers.py index 7f11a2d..a600677 100644 --- a/tg/update_handlers.py +++ b/tg/update_handlers.py @@ -169,7 +169,7 @@ def update_chat_last_msg(controller: Controller, update: Dict[str, Any]): current_chat_id = controller.model.current_chat_id if controller.model.chats.update_chat( - chat_id, last_message=message, order=order + chat_id, last_message=last_message, order=order ): controller._refresh_current_chat(current_chat_id)