commit 85e41b3581371d9119dc678ce5e7f939453f45ba Author: Damian Johnson atagar@torproject.org Date: Thu Sep 15 23:06:38 2016 -0700
Finish merging controller module
Moving the draw loop, making a handful of minor improvements in the process. This lets us finally drop the controller module. --- nyx/__init__.py | 85 ++++++++++++++++++++++++++++++++--- nyx/controller.py | 114 ----------------------------------------------- nyx/menu.py | 2 +- nyx/popups.py | 2 +- nyx/settings/strings.cfg | 4 ++ nyx/starter.py | 15 ++++++- test/__init__.py | 2 +- 7 files changed, 99 insertions(+), 125 deletions(-)
diff --git a/nyx/__init__.py b/nyx/__init__.py index 33da167..69ede9b 100644 --- a/nyx/__init__.py +++ b/nyx/__init__.py @@ -36,7 +36,9 @@ import distutils.spawn import os import sys import threading +import time
+import stem import stem.connection import stem.control import stem.util.conf @@ -62,14 +64,23 @@ __all__ = [ 'tracker', ]
+ +def conf_handler(key, value): + if key == 'features.redrawRate': + return max(1, value) + + CONFIG = stem.util.conf.config_dict('nyx', { + 'features.confirmQuit': True, 'features.panels.show.graph': True, 'features.panels.show.log': True, 'features.panels.show.connection': True, 'features.panels.show.config': True, 'features.panels.show.torrc': True, 'features.panels.show.interpreter': True, -}) + 'features.redrawRate': 5, + 'start_time': 0, +}, conf_handler)
NYX_INTERFACE = None TOR_CONTROLLER = None @@ -94,7 +105,6 @@ except IOError as exc:
def main(): try: - import nyx.starter nyx.starter.main() except ImportError as exc: if exc.message == 'No module named stem': @@ -119,6 +129,55 @@ def main(): sys.exit(1)
+def draw_loop(): + interface = nyx_interface() + next_key = None # use this as the next user input + + stem.util.log.info(msg('startup_time', start_time = time.time() - CONFIG['start_time'])) + + while not interface._quit: + interface.redraw() + + with nyx.curses.raw_screen() as stdscr: + stdscr.refresh() + + if next_key: + key, next_key = next_key, None + else: + key = nyx.curses.key_input(CONFIG['features.redrawRate']) + + if key.match('right'): + interface.set_page((interface.get_page() + 1) % interface.page_count()) + elif key.match('left'): + interface.set_page((interface.get_page() - 1) % interface.page_count()) + elif key.match('p'): + interface.set_paused(not interface.is_paused()) + elif key.match('m'): + nyx.menu.show_menu() + elif key.match('q'): + if CONFIG['features.confirmQuit']: + confirmation_key = show_message(msg('confirm_quit'), nyx.curses.BOLD, max_wait = 30) + + if not confirmation_key.match('q'): + continue + + break + elif key.match('x'): + confirmation_key = show_message(msg('confirm_reload'), nyx.curses.BOLD, max_wait = 30) + + if confirmation_key.match('x'): + try: + tor_controller().signal(stem.Signal.RELOAD) + except stem.ControllerError as exc: + stem.util.log.error('Error detected when reloading tor: %s' % exc.strerror) + elif key.match('h'): + next_key = nyx.popups.show_help() + else: + for panel in interface.page_panels(): + for keybinding in panel.key_handlers(): + keybinding.handle(key) + + def nyx_interface(): """ Singleton controller for our interface. @@ -308,7 +367,11 @@ class Interface(object): if CONFIG['features.panels.show.interpreter']: self._page_panels.append([nyx.panel.interpreter.InterpreterPanel()])
+ visible_panels = self.page_panels() + for panel in self: + panel.set_visible(panel in visible_panels) + if isinstance(panel, nyx.panel.DaemonPanel): panel.start()
@@ -337,6 +400,11 @@ class Interface(object): self._page = page_number self.header_panel().redraw()
+ visible_panels = self.page_panels() + + for panel in self: + panel.set_visible(panel in visible_panels) + def page_count(self): """ Provides the number of pages the interface has. @@ -365,7 +433,8 @@ class Interface(object): :returns: **list** of panels on that page """
- return list(self._page_panels[self._page if page_number is None else page_number]) + page_number = self._page if page_number is None else page_number + return [self._header_panel] + self._page_panels[page_number]
def is_paused(self): """ @@ -409,7 +478,7 @@ class Interface(object):
occupied = 0
- for panel in [self.header_panel()] + self.page_panels(): + for panel in self.page_panels(): panel.redraw(force = force, top = occupied) occupied += panel.get_height()
@@ -430,10 +499,10 @@ class Interface(object): def halt_panels(): daemons = [panel for panel in self if isinstance(panel, nyx.panel.DaemonPanel)]
- for panel in daemons(): + for panel in daemons: panel.stop()
- for panel in daemons(): + for panel in daemons: panel.join()
halt_thread = threading.Thread(target = halt_panels) @@ -448,6 +517,8 @@ class Interface(object): yield panel
+import nyx.curses +import nyx.menu import nyx.panel import nyx.panel.config import nyx.panel.connection @@ -456,3 +527,5 @@ import nyx.panel.header import nyx.panel.interpreter import nyx.panel.log import nyx.panel.torrc +import nyx.popups +import nyx.starter diff --git a/nyx/controller.py b/nyx/controller.py deleted file mode 100644 index 76fa549..0000000 --- a/nyx/controller.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2009-2016, Damian Johnson and The Tor Project -# See LICENSE for licensing information - -""" -Main interface loop for nyx, periodically redrawing the screen and issuing -user input to the proper panels. -""" - -import time - -import nyx.curses -import nyx.menu -import nyx.popups - -import nyx.panel - -import stem - -from stem.util import conf, log - -from nyx.curses import BOLD -from nyx import nyx_interface, tor_controller, show_message - - -def conf_handler(key, value): - if key == 'features.redrawRate': - return max(1, value) - - -CONFIG = conf.config_dict('nyx', { - 'features.redrawRate': 5, - 'features.confirmQuit': True, - 'start_time': 0, -}, conf_handler) - - -def start_nyx(): - """ - Main draw loop context. - """ - - # provides notice about any unused config keys - - for key in sorted(conf.get_config('nyx').unused_keys()): - if not key.startswith('msg.') and not key.startswith('dedup.'): - log.notice('Unused configuration entry: %s' % key) - - interface = nyx_interface() - - # logs the initialization time - - log.info('nyx started (initialization took %0.3f seconds)' % (time.time() - CONFIG['start_time'])) - - # main draw loop - - override_key = None # uses this rather than waiting on user input - - while not interface._quit: - display_panels = [interface.header_panel()] + interface.page_panels() - - # sets panel visability - - for panel in interface: - panel.set_visible(panel in display_panels) - - interface.redraw() - - with nyx.curses.raw_screen() as stdscr: - stdscr.refresh() - - # wait for user keyboard input until timeout, unless an override was set - - if override_key: - key, override_key = override_key, None - else: - key = nyx.curses.key_input(CONFIG['features.redrawRate']) - - if key.match('right'): - interface.set_page((interface.get_page() + 1) % interface.page_count()) - elif key.match('left'): - interface.set_page((interface.get_page() - 1) % interface.page_count()) - elif key.match('p'): - interface.set_paused(not interface.is_paused()) - elif key.match('m'): - nyx.menu.show_menu() - elif key.match('q'): - # provides prompt to confirm that nyx should exit - - if CONFIG['features.confirmQuit']: - msg = 'Are you sure (q again to confirm)?' - confirmation_key = show_message(msg, BOLD, max_wait = 30) - quit_confirmed = confirmation_key.match('q') - else: - quit_confirmed = True - - if quit_confirmed: - break - elif key.match('x'): - # provides prompt to confirm that nyx should issue a sighup - - msg = "This will reset Tor's internal state. Are you sure (x again to confirm)?" - confirmation_key = show_message(msg, BOLD, max_wait = 30) - - if confirmation_key.match('x'): - try: - tor_controller().signal(stem.Signal.RELOAD) - except stem.ControllerError as exc: - log.error('Error detected when reloading tor: %s' % exc.strerror) - elif key.match('h'): - override_key = nyx.popups.show_help() - else: - for panel in display_panels: - for keybinding in panel.key_handlers(): - keybinding.handle(key) diff --git a/nyx/menu.py b/nyx/menu.py index 83b8479..9aa5797 100644 --- a/nyx/menu.py +++ b/nyx/menu.py @@ -269,7 +269,7 @@ def _view_menu(): page_group = RadioGroup(interface.set_page, interface.get_page())
for i in range(interface.page_count()): - page_panels = interface.page_panels(page_number = i) + page_panels = interface.page_panels(page_number = i)[1:] label = ' / '.join([type(panel).__name__.replace('Panel', '') for panel in page_panels]) view_menu.add(RadioMenuItem(label, page_group, i))
diff --git a/nyx/popups.py b/nyx/popups.py index e2f0205..0a05a2f 100644 --- a/nyx/popups.py +++ b/nyx/popups.py @@ -59,7 +59,7 @@ def show_help(): interface = nyx_interface() handlers = []
- for panel in reversed(interface.page_panels()): + for panel in reversed(interface.page_panels()[1:]): handlers += [handler for handler in panel.key_handlers() if handler.description]
def _render(subwindow): diff --git a/nyx/settings/strings.cfg b/nyx/settings/strings.cfg index 9179519..0f24529 100644 --- a/nyx/settings/strings.cfg +++ b/nyx/settings/strings.cfg @@ -14,6 +14,10 @@
msg.wrap {text}
+msg.startup_time nyx started (initialization took {start_time} seconds) +msg.confirm_quit Are you sure (q again to confirm)? +msg.confirm_reload This will reset Tor's internal state. Are you sure (x again to confirm)? + msg.config.unable_to_read_file Failed to load configuration (using defaults): "{error}" msg.config.nothing_loaded No nyxrc loaded, using defaults. You can customize nyx by placing a configuration file at {path} (see the nyxrc.sample for its options).
diff --git a/nyx/starter.py b/nyx/starter.py index 7ff37b2..dbb6d8c 100644 --- a/nyx/starter.py +++ b/nyx/starter.py @@ -16,7 +16,6 @@ import time
import nyx import nyx.arguments -import nyx.controller import nyx.curses import nyx.tracker
@@ -78,6 +77,7 @@ def main(config):
_warn_if_root(controller) _warn_if_unable_to_get_pid(controller) + _warn_about_unused_config_keys() _setup_freebsd_chroot(controller) _use_english_subcommands() _use_no_esc_delay() @@ -85,7 +85,7 @@ def main(config): _set_process_name()
try: - nyx.curses.start(nyx.controller.start_nyx, acs_support = config.get('features.acsSupport', True), transparent_background = True, cursor = False) + nyx.curses.start(nyx.draw_loop, acs_support = config.get('features.acsSupport', True), transparent_background = True, cursor = False) except UnboundLocalError as exc: if os.environ['TERM'] != 'xterm': print(msg('setup.unknown_term', term = os.environ['TERM'])) @@ -190,6 +190,17 @@ def _warn_if_unable_to_get_pid(controller):
@uses_settings +def _warn_about_unused_config_keys(config): + """ + Provides a notice if the user's nyxrc has any entries that are unused. + """ + + for key in sorted(config.unused_keys()): + if not key.startswith('msg.') and not key.startswith('dedup.'): + log.notice('Unused configuration entry: %s' % key) + + +@uses_settings def _setup_freebsd_chroot(controller, config): """ If we're running under FreeBSD then check the system for a chroot path. diff --git a/test/__init__.py b/test/__init__.py index ebb0793..6e534e1 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -83,7 +83,7 @@ def render(func, *args, **kwargs): attr = {}
def draw_func(): - nyx.curses.disable_acs() + nyx.curses._disable_acs() nyx.curses.CURSES_SCREEN.erase() start_time = time.time()