From 9c9236eb49caf55f86bd5b7a39ab364f028bc1bf Mon Sep 17 00:00:00 2001 From: "louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13" Date: Sun, 17 Oct 2010 05:14:22 +0000 Subject: refresh optimization by limiting the .refresh() calls at the STRICT minimum --- src/gui.py | 41 +++++++++-------- src/tab.py | 31 ++++++++++++- src/window.py | 145 ++++++++++++++++++++++++++++++---------------------------- 3 files changed, 128 insertions(+), 89 deletions(-) diff --git a/src/gui.py b/src/gui.py index f56828b2..9fc649d9 100644 --- a/src/gui.py +++ b/src/gui.py @@ -69,9 +69,6 @@ SHOW_NAME = { resize_lock = threading.Lock() -def doupdate(): - curses.doupdate() - class Gui(object): """ User interface using ncurses @@ -82,6 +79,7 @@ class Gui(object): self.xmpp = xmpp default_tab = InfoTab(self.stdscr, "Info") if self.xmpp.anon\ else RosterInfoTab(self.stdscr) + default_tab.on_gain_focus() self.tabs = [default_tab] self.roster = Roster() # a unique buffer used to store global informations @@ -281,7 +279,7 @@ class Gui(object): else: self.on_user_change_status(room, user, from_nick, from_room, affiliation, role, show, status) self.refresh_window() - doupdate() + self.doupdate() def on_user_join(self, room, from_nick, affiliation, show, status, role, jid): """ @@ -426,7 +424,7 @@ class Gui(object): We received a Private Message (from someone in a Muc) """ jid = message['from'] - nick_from = jid.boundjid.resource + nick_from = jid.resource room_from = jid.bare room = self.get_room_by_name(jid.full) # get the tab with the private conversation if not room: # It's the first message we receive: create the tab @@ -436,7 +434,7 @@ class Gui(object): body = message['body'] self.add_message_to_text_buffer(room, body, None, nick_from) self.refresh_window() - doupdate() + self.doupdate() def focus_tab_named(self, tab_name): for tab in self.tabs: @@ -504,14 +502,15 @@ class Gui(object): This is to avoid multiple unnecessary software resizes (this can be heavy on resource on slow computers or networks) """ - with resize_lock: - if self.resize_timer: - # a recent terminal resize occured. - # Cancel the programmed software resize - self.resize_timer.cancel() - # add the new timer - self.resize_timer = threading.Timer(0.15, self.resize_window) - self.resize_timer.start() + # with resize_lock: + # if self.resize_timer: + # # a recent terminal resize occured. + # # Cancel the programmed software resize + # self.resize_timer.cancel() + # # add the new timer + # self.resize_timer = threading.Timer(0.15, self.resize_window) + # self.resize_timer.start() + self.resize_window() def resize_window(self): """ @@ -529,7 +528,7 @@ class Gui(object): """ self.refresh_window() while True: - doupdate() + self.doupdate() char=read_char(self.stdscr) # search for keyboard shortcut if char in list(self.key_func.keys()): @@ -567,8 +566,10 @@ class Gui(object): """ ncurses initialization """ - theme.init_colors() + curses.curs_set(1) curses.noecho() + # curses.raw() + theme.init_colors() stdscr.keypad(True) def reset_curses(self): @@ -586,7 +587,6 @@ class Gui(object): """ self.current_tab().set_color_state(theme.COLOR_TAB_CURRENT) self.current_tab().refresh(self.tabs, self.information_buffer, self.roster) - doupdate() def open_new_room(self, room, nick, focus=True): """ @@ -778,7 +778,7 @@ class Gui(object): date = date if delayed == True else None self.add_message_to_text_buffer(room, body, date, nick_from) self.refresh_window() - doupdate() + self.doupdate() def add_message_to_text_buffer(self, room, txt, time=None, nickname=None, colorized=False): """ @@ -1334,4 +1334,7 @@ class Gui(object): self.add_message_to_text_buffer(self.current_tab().get_room(), line, None, self.current_tab().get_room().own_nick) elif isinstance(self.current_tab(), MucTab): muc.send_groupchat_message(self.xmpp, self.current_tab().get_name(), line) - doupdate() + self.doupdate() + + def doupdate(self): + curses.doupdate() diff --git a/src/tab.py b/src/tab.py index 8cb3ed0c..4acfab08 100644 --- a/src/tab.py +++ b/src/tab.py @@ -27,6 +27,7 @@ MIN_HEIGHT = 16 import window import theme +import curses from roster import RosterGroup from common import debug @@ -118,6 +119,14 @@ class Tab(object): """ raise NotImplementedError + def just_before_refresh(self): + """ + Method called just before the screen refresh. + Particularly useful to move the cursor at the + correct position. + """ + raise NotImplementedError + class InfoTab(Tab): """ The information tab, used to display global informations @@ -159,6 +168,7 @@ class InfoTab(Tab): def on_gain_focus(self): self.color_state = theme.COLOR_TAB_CURRENT + curses.curs_set(0) def on_scroll_up(self): pass @@ -169,6 +179,9 @@ class InfoTab(Tab): def on_info_win_size_changed(self, size, stdscr): return + def just_before_refresh(self): + return + class MucTab(Tab): """ The tab containing a multi-user-chat room. @@ -276,6 +289,7 @@ class MucTab(Tab): def on_gain_focus(self): self._room.set_color_state(theme.COLOR_TAB_CURRENT) + curses.curs_set(1) def on_scroll_up(self): self._room.scroll_up(self.text_win.height-1) @@ -290,6 +304,9 @@ class MucTab(Tab): self.info_header.resize(1, (self.width//10)*9, self.height-3-self.info_win_size, 0, stdscr, self.visible) self.info_win.resize(self.info_win_size, (self.width//10)*9, self.height-2-self.info_win_size, 0, stdscr, self.visible) + def just_before_refresh(self): + self.input.move_cursor_to_pos() + class PrivateTab(Tab): """ The tab containg a private conversation (someone from a MUC) @@ -341,6 +358,7 @@ class PrivateTab(Tab): def on_gain_focus(self): self._room.set_color_state(theme.COLOR_TAB_CURRENT) + curses.curs_set(1) def on_scroll_up(self): self._room.scroll_up(self.text_win.height-1) @@ -357,6 +375,9 @@ class PrivateTab(Tab): def get_room(self): return self._room + def just_before_refresh(self): + return + class RosterInfoTab(Tab): """ A tab, splitted in two, containing the roster and infos @@ -386,9 +407,9 @@ class RosterInfoTab(Tab): self.input.resize(1, self.width, self.height-1, 0, stdscr, self.visible) def refresh(self, tabs, informations, roster): + self.v_separator.refresh() self.roster_win.refresh(roster) self.contact_info_win.refresh(self.roster_win.get_selected_row()) - self.v_separator.refresh() self.info_win.refresh(informations) self.tab_win.refresh(tabs, tabs[0]) self.input.refresh() @@ -412,6 +433,7 @@ class RosterInfoTab(Tab): def on_gain_focus(self): self._color_state = theme.COLOR_TAB_CURRENT + curses.curs_set(0) def add_message(self): return False @@ -429,6 +451,9 @@ class RosterInfoTab(Tab): selected_row = self.roster_win.get_selected_row() return selected_row + def just_before_refresh(self): + return + class ConversationTab(Tab): """ The tab containg a normal conversation (someone from our roster) @@ -457,6 +482,7 @@ class ConversationTab(Tab): self.info_win.refresh(informations) self.tab_win.refresh(tabs, tabs[0]) self.input.refresh() + curses.curs_set(1) def get_color_state(self): if self._room.color_state == theme.COLOR_TAB_NORMAL or\ @@ -495,3 +521,6 @@ class ConversationTab(Tab): def get_room(self): return self._room + + def just_before_refresh(self): + return diff --git a/src/window.py b/src/window.py index 19912ed1..64db72ae 100644 --- a/src/window.py +++ b/src/window.py @@ -47,28 +47,27 @@ class Win(object): if not visible: return self.height, self.width, self.x, self.y = height, width, x, y - try: - self.win = curses.newwin(height, width, y, x) - except: - # When resizing in a too little height (less than 3 lines) - # We don't need to resize the window, since this size - # just makes no sense - # Just don't crash when this happens. - # (°> also, a penguin - # //\ - # V_/_ - return - self.win.leaveok(1) - - def refresh(self): - self.win.noutrefresh() + # try: + self._win = curses.newwin(height, width, y, x) + # except: + # # When resizing in a too little height (less than 3 lines) + # # We don't need to resize the window, since this size + # # just makes no sense + # # Just don't crash when this happens. + # # (°> also, a penguin + # # //\ + # # V_/_ + # return + + def _refresh(self): + self._win.noutrefresh() def addnstr(self, *args): """ Safe call to addnstr """ try: - self.win.addnstr(*args) + self._win.addnstr(*args) except: pass @@ -77,7 +76,7 @@ class Win(object): Safe call to addstr """ try: - self.win.addstr(*args) + self._win.addstr(*args) except: pass @@ -85,7 +84,7 @@ class Win(object): """ Write colored spaces until the end of line """ - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() size = self.width-x self.addnstr(' '*size, size, curses.color_pair(color)) @@ -111,7 +110,7 @@ class UserList(Win): if not self.visible: return with g_lock: - self.win.erase() + self._win.erase() y = 0 for user in sorted(users): if not user.role in self.color_role: @@ -127,16 +126,16 @@ class UserList(Win): y += 1 if y == self.height: break - self.win.refresh() + self._refresh() def resize(self, height, width, y, x, stdscr, visible): self.visible = visible if not visible: return self._resize(height, width, y, x, stdscr, visible) - self.win.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - self.win.vline(0, 0, curses.ACS_VLINE, self.height) - self.win.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._win.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._win.vline(0, 0, curses.ACS_VLINE, self.height) + self._win.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) class Topic(Win): def __init__(self, height, width, y, x, parent_win, visible): @@ -150,14 +149,14 @@ class Topic(Win): if not self.visible: return with g_lock: - self.win.erase() + self._win.erase() self.addstr(0, 0, topic[:self.width-1], curses.color_pair(theme.COLOR_TOPIC_BAR)) - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() remaining_size = self.width - x if remaining_size: self.addnstr(' '*remaining_size, remaining_size, curses.color_pair(theme.COLOR_INFORMATION_BAR)) - self.win.refresh() + self._refresh() class GlobalInfoBar(Win): def __init__(self, height, width, y, x, parent_win, visible): @@ -175,7 +174,7 @@ class GlobalInfoBar(Win): return a.nb comp = lambda x: x.nb with g_lock: - self.win.erase() + self._win.erase() self.addstr(0, 0, "[", curses.color_pair(theme.COLOR_INFORMATION_BAR)) sorted_tabs = sorted(tabs, key=comp) for tab in sorted_tabs: @@ -185,13 +184,13 @@ class GlobalInfoBar(Win): self.addstr("|", curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: # end of line break - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() self.addstr(y, x-1, '] ', curses.color_pair(theme.COLOR_INFORMATION_BAR)) - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() remaining_size = self.width - x self.addnstr(' '*remaining_size, remaining_size, curses.color_pair(theme.COLOR_INFORMATION_BAR)) - self.win.refresh() + self._refresh() class InfoWin(Win): """ @@ -227,11 +226,11 @@ class PrivateInfoWin(InfoWin): if not self.visible: return with g_lock: - self.win.erase() + self._win.erase() self.write_room_name(room) self.print_scroll_position(room) self.finish_line(theme.COLOR_INFORMATION_BAR) - self.win.refresh() + self._refresh() def write_room_name(self, room): (room_name, nick) = room.name.split('/', 1) @@ -257,11 +256,11 @@ class ConversationInfoWin(InfoWin): # from someone not in our roster. In this case, we display # only the maximum information from the message we can get. with g_lock: - self.win.erase() + self._win.erase() self.write_room_name(contact, room) self.print_scroll_position(room) self.finish_line(theme.COLOR_INFORMATION_BAR) - self.win.refresh() + self._refresh() def write_room_name(self, contact, room): if not contact: @@ -285,14 +284,14 @@ class MucInfoWin(InfoWin): if not self.visible: return with g_lock: - self.win.erase() + self._win.erase() self.write_room_name(room) self.write_own_nick(room) self.write_disconnected(room) self.write_role(room) self.print_scroll_position(room) self.finish_line(theme.COLOR_INFORMATION_BAR) - self.win.refresh() + self._refresh() def write_room_name(self, room): """ @@ -414,7 +413,7 @@ class TextWin(Win): if self.height <= 0: return with g_lock: - self.win.erase() + self._win.erase() lines = self.build_lines_from_messages(room.messages) if room.pos + self.height > len(lines): room.pos = len(lines) - self.height @@ -426,7 +425,7 @@ class TextWin(Win): lines = lines[-self.height:] y = 0 for line in lines: - self.win.move(y, 0) + self._win.move(y, 0) if line == None: self.write_line_separator() y += 1 @@ -437,14 +436,14 @@ class TextWin(Win): self.write_nickname(line.nickname, line.nickname_color) self.write_text(y, line.text_offset, line.text, line.text_color, line.colorized) y += 1 - self.win.refresh() + self._refresh() def write_line_separator(self): """ """ - self.win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) + self._win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) self.addnstr('- '*(self.width//2), self.width) - self.win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) + self._win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) def write_text(self, y, x, txt, color, colorized): """ @@ -453,10 +452,10 @@ class TextWin(Win): txt = txt if not colorized: if color: - self.win.attron(curses.color_pair(color)) + self._win.attron(curses.color_pair(color)) self.addstr(y, x, txt) if color: - self.win.attroff(curses.color_pair(color)) + self._win.attroff(curses.color_pair(color)) else: # Special messages like join or quit special_words = { @@ -485,7 +484,7 @@ class TextWin(Win): self.addstr(word[1:-1], curses.color_pair(theme.COLOR_BRACKETED_WORD)) else: self.addstr(word, curses.color_pair(color)) - self.win.addch(' ') + self._win.addch(' ') def write_nickname(self, nickname, color): """ @@ -493,10 +492,10 @@ class TextWin(Win): and return the number of written characters """ if color: - self.win.attron(curses.color_pair(color)) + self._win.attron(curses.color_pair(color)) self.addstr(nickname) if color: - self.win.attroff(curses.color_pair(color)) + self._win.attroff(curses.color_pair(color)) self.addstr("> ") def write_time(self, time): @@ -569,7 +568,7 @@ class Input(Win): if not visible: return self._resize(height, width, y, x, stdscr, visible) - self.win.clear() + self._win.erase() self.addnstr(0, 0, self.text, self.width-1) def jump_word_left(self): @@ -657,7 +656,7 @@ class Input(Win): """ if not len(self.history): return - self.win.erase() + self._win.erase() if self.histo_pos >= 0: self.histo_pos -= 1 self.text = self.history[self.histo_pos+1] @@ -709,7 +708,7 @@ class Input(Win): Move the cursor one char to the left """ self.reset_completion() - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if self.pos == self.width-1 and self.line_pos > 0: self.line_pos -= 1 elif self.pos >= 1: @@ -721,7 +720,7 @@ class Input(Win): Move the cursor one char to the right """ self.reset_completion() - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if self.pos == self.width-1: if self.line_pos + self.width-1 < len(self.text): self.line_pos += 1 @@ -734,7 +733,7 @@ class Input(Win): Delete the char just before the cursor """ self.reset_completion() - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if self.pos == 0: return self.text = self.text[:self.pos+self.line_pos-1]+self.text[self.pos+self.line_pos:] @@ -771,7 +770,7 @@ class Input(Win): #if " " in self.text.strip() and (not self.last_completion or ' ' in self.last_completion): else: after = " " # don't put the "," if it's not the begining of the sentence - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if not self.last_completion: # begin is the begining of the nick we want to complete if self.text.strip() != '': @@ -804,7 +803,7 @@ class Input(Win): after = " " # don't put the "," if it's not the begining of the sentence else: after = config.get('after_completion', ',')+" " - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if self.text != '': begin = self.text.split()[-1].lower() else: @@ -848,7 +847,7 @@ class Input(Win): # return # ignore non-handled keyboard shortcuts self.reset_completion() self.text = self.text[:self.pos+self.line_pos]+key+self.text[self.pos+self.line_pos:] - (y, x) = self.win.getyx() + (y, x) = self._win.getyx() if x == self.width-1: self.line_pos += 1 else: @@ -878,7 +877,7 @@ class Input(Win): self.clear_text() self.addstr(self.text[self.line_pos:self.line_pos+self.width-1]) # self.win.chgat(0, self.pos, 1, curses.A_REVERSE) - self.win.refresh() + self._refresh() def refresh(self): if not self.visible: @@ -886,7 +885,15 @@ class Input(Win): self.rewrite_text() def clear_text(self): - self.win.erase() + self._win.erase() + + def move_cursor_to_pos(self): + """ + move the cursor at the current pos + """ + from common import debug + debug('ALLO') + self._win.move(0, self.pos) class VerticalSeparator(Win): """ @@ -899,8 +906,8 @@ class VerticalSeparator(Win): def rewrite_line(self): with g_lock: - self.win.vline(0, 0, curses.ACS_VLINE, self.height, curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) - self.win.refresh() + self._win.vline(0, 0, curses.ACS_VLINE, self.height, curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._refresh() def resize(self, height, width, y, x, stdscr, visible): self.visible = visible @@ -962,7 +969,7 @@ class RosterWin(Win): return with g_lock: self.roster_len = len(roster) - self.win.erase() + self._win.erase() self.draw_roster_information(roster) y = 1 for group in roster.get_groups(): @@ -987,19 +994,19 @@ class RosterWin(Win): self.draw_plus(1) if self.start_pos + self.height-2 < self.roster_len: self.draw_plus(self.height-1) - self.win.refresh() + self._refresh() def draw_plus(self, y): """ Draw the indicator that shows that the list is longer that what is displayed """ - self.win.addstr(y, self.width-5, '++++', curses.color_pair(42)) + self.addstr(y, self.width-5, '++++', curses.color_pair(42)) def draw_roster_information(self, roster): """ """ - self.win.addstr('%s contacts' % roster.get_contact_len(), curses.color_pair(12)) + self.addstr('%s contacts' % roster.get_contact_len(), curses.color_pair(12)) self.finish_line(12) def draw_group(self, y, group, colored): @@ -1007,14 +1014,14 @@ class RosterWin(Win): Draw a groupname on a line """ if colored: - self.win.attron(curses.color_pair(14)) + self._win.attron(curses.color_pair(14)) if group.folded: self.addstr(y, 0, '[+] ') else: self.addstr(y, 0, '[-] ') self.addstr(y, 4, group.name) if colored: - self.win.attroff(curses.color_pair(14)) + self._win.attroff(curses.color_pair(14)) def draw_contact_line(self, y, contact, colored): """ @@ -1028,11 +1035,11 @@ class RosterWin(Win): contact.get_jid().bare) else: display_name = '%s' % (contact.get_jid().bare,) - self.win.addstr(y, 1, " ", curses.color_pair(color)) + self.addstr(y, 1, " ", curses.color_pair(color)) if colored: - self.win.addstr(y, 4, display_name, curses.color_pair(14)) + self.addstr(y, 4, display_name, curses.color_pair(14)) else: - self.win.addstr(y, 4, display_name) + self.addstr(y, 4, display_name) def get_selected_row(self): return self.selected_row @@ -1065,9 +1072,9 @@ class ContactInfoWin(Win): if not self.visible: return with g_lock: - self.win.erase() + self._win.erase() if isinstance(selected_row, RosterGroup): self.draw_group_info(selected_row) elif isinstance(selected_row, Contact): self.draw_contact_info(selected_row) - self.win.refresh() + self._refresh() -- cgit v1.2.3