From 3ef9228211df46ada9bb5b851682a9c7d833de56 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 7 Aug 2014 23:20:21 +0200 Subject: Continuation keys (like after M-j or Ctrl-c) are handled without blocking --- src/core/core.py | 61 ++++++++++++++++++++++++++------------- src/keyboard.py | 9 ++++++ src/windows/input_placeholders.py | 13 +++++---- src/windows/inputs.py | 12 ++++---- 4 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/core/core.py b/src/core/core.py index fe9efdd5..f4da11d0 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -39,13 +39,13 @@ from config import config, firstrun from contact import Contact, Resource from daemon import Executor from fifo import Fifo -from keyboard import Keyboard from logger import logger from plugin_manager import PluginManager from roster import roster from size_manager import SizeManager from text_buffer import TextBuffer from theming import get_theme +import keyboard from . import completions from . import commands @@ -72,7 +72,7 @@ class Core(object): self.running = True self.xmpp = singleton.Singleton(connection.Connection) self.xmpp.core = self - self.keyboard = Keyboard() + self.keyboard = keyboard.Keyboard() roster.set_node(self.xmpp.client_roster) decorators.refresh_wrapper.core = self self.paused = False @@ -131,6 +131,11 @@ class Core(object): del self.commands['status'] del self.commands['show'] + # A list of integers. For example if the user presses Alt+j, 2, 1, + # we will insert 2, then 1 in that list, and we will finally build + # the number 21 and use it with command_win, before clearing the + # list. + self.room_number_jump = [] self.key_func = KeyDict() # Key bindings associated with handlers # and pseudo-keys used to map actions below. @@ -742,10 +747,21 @@ class Core(object): def do_command(self, key, raw): """ Execute the action associated with a key + + Or if keyboard.continuation_keys_callback is set, call it instead. See + the comment of this variable. """ if not key: return - return self.current_tab().on_input(key, raw) + if keyboard.continuation_keys_callback is not None: + # Reset the callback to None BEFORE calling it, because this + # callback MAY set a new callback itself, and we don’t want to + # erase it in that case + cb = keyboard.continuation_keys_callback + keyboard.continuation_keys_callback = None + cb(key) + else: + self.current_tab().on_input(key, raw) def try_execute(self, line): @@ -1060,17 +1076,24 @@ class Core(object): Read 2 more chars and go to the tab with the given number """ - char = self.read_keyboard()[0] - try: - nb1 = int(char) - except ValueError: - return - char = self.read_keyboard()[0] - try: - nb2 = int(char) - except ValueError: - return - self.command_win('%s%s' % (nb1, nb2)) + def read_next_digit(digit): + try: + nb = int(digit) + except ValueError: + # If it is not a number, we do nothing. If it was the first + # one, we do not wait for a second one by re-setting the + # callback + pass + else: + self.room_number_jump.append(digit) + if len(self.room_number_jump) == 2: + arg = "".join(self.room_number_jump) + self.room_number_jump.clear() + self.command_win(arg) + else: + # We need to read more digits + keyboard.continuation_keys_callback = read_next_digit + keyboard.continuation_keys_callback = read_next_digit def go_to_roster(self): "Select the roster as the current tab" @@ -1623,13 +1646,11 @@ class Core(object): def read_keyboard(self): """ - Get the next keyboard key pressed and returns it. - get_user_input() has a timeout: it returns None when the timeout - occurs. In that case we do not return (we loop until we get - a non-None value), but we check for timed events instead. + Get the next keyboard key pressed and returns it. It blocks until + something can be read on stdin, this function must be called only if + there is something to read. No timeout ever occurs. """ - res = self.keyboard.get_user_input(self.stdscr) - return res + return self.keyboard.get_user_input(self.stdscr) def escape_next_key(self): """ diff --git a/src/keyboard.py b/src/keyboard.py index 0a1391ea..f88a030c 100755 --- a/src/keyboard.py +++ b/src/keyboard.py @@ -18,6 +18,15 @@ import curses.ascii import logging log = logging.getLogger(__name__) +# A callback that will handle the next key entered by the user. For +# example if the user presses Ctrl+j, we set a callbacks, and the +# next key pressed by the user will be passed to this callback +# instead of the normal process of executing global keybard +# shortcuts or inserting text in the current output. The callback +# is always reset to None afterwards (to resume the normal +# processing of keys) +continuation_keys_callback = None + def get_next_byte(s): """ Read the next byte of the utf-8 char diff --git a/src/windows/input_placeholders.py b/src/windows/input_placeholders.py index 6fd7975a..0887cfc5 100644 --- a/src/windows/input_placeholders.py +++ b/src/windows/input_placeholders.py @@ -68,9 +68,12 @@ class YesNoInput(Win): def prompt(self): """Monopolizes the input while waiting for a recognized keypress""" - cl = [] - while self.value is None: - if len(cl) == 1 and cl[0] in self.key_func: - self.key_func[cl[0]]() - cl = self.core.read_keyboard() + def cb(key): + if key in self.key_func: + self.key_func[key]() + if self.value is None: + # We didn’t finish with this prompt, continue monopolizing + # it again until value is set + keyboard.continuation_keys_callback = cb + keyboard.continuation_keys_callback = cb diff --git a/src/windows/inputs.py b/src/windows/inputs.py index ff13a562..afce3dd8 100644 --- a/src/windows/inputs.py +++ b/src/windows/inputs.py @@ -8,6 +8,7 @@ log = logging.getLogger(__name__) import curses import string +import keyboard import common import poopt from . import Win @@ -655,11 +656,12 @@ class MessageInput(HistoryInput): """ Read one more char (c), add the corresponding char from formats_char to the text string """ - attr_char = self.core.read_keyboard()[0] - if attr_char in self.text_attributes: - char = format_chars[self.text_attributes.index(attr_char)] - self.do_command(char, False) - self.rewrite_text() + def cb(attr_char): + if attr_char in self.text_attributes: + char = format_chars[self.text_attributes.index(attr_char)] + self.do_command(char, False) + self.rewrite_text() + keyboard.continuation_keys_callback = cb def key_enter(self): if self.history_enter(): -- cgit v1.2.3