[tor-commits] [nyx/master] Move raw curses usage behind raw_screen()

atagar at torproject.org atagar at torproject.org
Sun Mar 20 00:17:45 UTC 2016


commit 5d9c3dde6315b1ceb9ba9017823ea036501b909c
Author: Damian Johnson <atagar at torproject.org>
Date:   Thu Mar 17 09:53:18 2016 -0700

    Move raw curses usage behind raw_screen()
    
    Providing a function for raw access to the curses screen rather than passing a
    reference around. This is an intermediate step since we want to abstract this
    all away but for now better to have them go through nyx.curses to get this.
---
 nyx/controller.py       | 46 +++++++++++++++++-----------------------------
 nyx/curses.py           | 35 ++++++++++++++++++++++++++++++++---
 nyx/panel/__init__.py   | 14 ++++++++------
 nyx/panel/config.py     |  4 ++--
 nyx/panel/connection.py |  4 ++--
 nyx/panel/graph.py      |  4 ++--
 nyx/panel/header.py     |  4 ++--
 nyx/panel/log.py        |  4 ++--
 nyx/panel/torrc.py      |  4 ++--
 nyx/popups.py           |  2 +-
 10 files changed, 70 insertions(+), 51 deletions(-)

diff --git a/nyx/controller.py b/nyx/controller.py
index 602c85e..32f4db5 100644
--- a/nyx/controller.py
+++ b/nyx/controller.py
@@ -63,8 +63,8 @@ class LabelPanel(nyx.panel.Panel):
   Panel that just displays a single line of text.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'msg', 0, height=1)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'msg', 0, height=1)
     self.msg_text = ''
     self.msg_attr = NORMAL
 
@@ -92,41 +92,36 @@ class Controller:
   Tracks the global state of the interface
   """
 
-  def __init__(self, stdscr):
+  def __init__(self):
     """
     Creates a new controller instance. Panel lists are ordered as they appear,
     top to bottom on the page.
-
-    Arguments:
-      stdscr - curses window
     """
 
-    self._screen = stdscr
-
     self._sticky_panels = [
-      nyx.panel.header.HeaderPanel(stdscr),
-      LabelPanel(stdscr),
+      nyx.panel.header.HeaderPanel(),
+      LabelPanel(),
     ]
 
     self._page_panels, first_page_panels = [], []
 
     if CONFIG['features.panels.show.graph']:
-      first_page_panels.append(nyx.panel.graph.GraphPanel(stdscr))
+      first_page_panels.append(nyx.panel.graph.GraphPanel())
 
     if CONFIG['features.panels.show.log']:
-      first_page_panels.append(nyx.panel.log.LogPanel(stdscr))
+      first_page_panels.append(nyx.panel.log.LogPanel())
 
     if first_page_panels:
       self._page_panels.append(first_page_panels)
 
     if CONFIG['features.panels.show.connection']:
-      self._page_panels.append([nyx.panel.connection.ConnectionPanel(stdscr)])
+      self._page_panels.append([nyx.panel.connection.ConnectionPanel()])
 
     if CONFIG['features.panels.show.config']:
-      self._page_panels.append([nyx.panel.config.ConfigPanel(stdscr)])
+      self._page_panels.append([nyx.panel.config.ConfigPanel()])
 
     if CONFIG['features.panels.show.torrc']:
-      self._page_panels.append([nyx.panel.torrc.TorrcPanel(stdscr)])
+      self._page_panels.append([nyx.panel.torrc.TorrcPanel()])
 
     self.quit_signal = False
     self._page = 0
@@ -135,13 +130,6 @@ class Controller:
     self._last_drawn = 0
     self.set_msg()  # initializes our control message
 
-  def get_screen(self):
-    """
-    Provides our curses window.
-    """
-
-    return self._screen
-
   def get_page_count(self):
     """
     Provides the number of pages the interface has. This may be zero if all
@@ -306,7 +294,8 @@ class Controller:
     # https://trac.torproject.org/projects/tor/ticket/2830#comment:9
 
     if force:
-      self._screen.clear()
+      with nyx.curses.raw_screen() as stdscr:
+        stdscr.clear()
 
     for panel_impl in display_panels:
       panel_impl.redraw(force)
@@ -365,17 +354,14 @@ class Controller:
     return halt_thread
 
 
-def start_nyx(stdscr):
+def start_nyx():
   """
   Main draw loop context.
-
-  Arguments:
-    stdscr - curses window
   """
 
   global NYX_CONTROLLER
 
-  NYX_CONTROLLER = Controller(stdscr)
+  NYX_CONTROLLER = Controller()
   control = get_controller()
 
   if not CONFIG['features.acsSupport']:
@@ -411,7 +397,9 @@ def start_nyx(stdscr):
     # redraws the interface if it's needed
 
     control.redraw(False)
-    stdscr.refresh()
+
+    with nyx.curses.raw_screen() as stdscr:
+      stdscr.refresh()
 
     # wait for user keyboard input until timeout, unless an override was set
 
diff --git a/nyx/curses.py b/nyx/curses.py
index 97ad02a..4d9571b 100644
--- a/nyx/curses.py
+++ b/nyx/curses.py
@@ -9,6 +9,7 @@ if we want Windows support in the future too.
 ::
 
   start - initializes curses with the given function
+  raw_screen - provides direct access to the curses screen
   key_input - get keypress by user
   curses_attr - curses encoded text attribute
 
@@ -139,8 +140,7 @@ CONFIG = stem.util.conf.config_dict('nyx', {
 
 def start(function, transparent_background = False, cursor = True):
   """
-  Starts a curses interface, delegating to the given function. The function
-  should accept a single argument for the curses screen.
+  Starts a curses interface, delegating to the given function.
 
   :param funtion: function to invoke when curses starts
   :param bool transparent_background: allows background transparency
@@ -164,11 +164,40 @@ def start(function, transparent_background = False, cursor = True):
       except curses.error:
         pass
 
-    function(stdscr)
+    function()
 
   curses.wrapper(_wrapper)
 
 
+def raw_screen():
+  """
+  Provides the curses screen. This can only be called after
+  :func:`~nyx.curses.start`, and is used as follows...
+
+  ::
+
+    with nyx.curses.raw_screen() as stdscr:
+      ... work with curses...
+
+  In the future this will never be called directly. This is just an
+  intermediate function as we migrate.
+  """
+
+  class _Wrapper(object):
+    def __enter__(self):
+      # TODO: We should be wrapping this with CURSES_LOCK.acquire/release(),
+      # but doing so seems to be causing frequent terminal gliches when
+      # shutting down. Strange since this should be strictly safer. Oh well -
+      # something to dig into later.
+
+      return CURSES_SCREEN
+
+    def __exit__(self, exit_type, value, traceback):
+      pass
+
+  return _Wrapper()
+
+
 def key_input(input_timeout = None):
   """
   Gets a key press from the user.
diff --git a/nyx/panel/__init__.py b/nyx/panel/__init__.py
index 176eea9..bab0cc7 100644
--- a/nyx/panel/__init__.py
+++ b/nyx/panel/__init__.py
@@ -122,12 +122,11 @@ class Panel(object):
   redraw().
   """
 
-  def __init__(self, parent, name, top, left = 0, height = -1, width = -1):
+  def __init__(self, name, top, left = 0, height = -1, width = -1):
     """
     Creates a durable wrapper for a curses subwindow in the given parent.
 
     Arguments:
-      parent - parent curses window
       name   - identifier for the panel
       top    - positioning of top within parent
       left   - positioning of the left edge within the parent
@@ -140,7 +139,6 @@ class Panel(object):
     # might chose their height based on its parent's current width).
 
     self.panel_name = name
-    self.parent = parent
     self.visible = False
     self.title_visible = True
 
@@ -358,7 +356,9 @@ class Panel(object):
     returns a tuple of (height, width).
     """
 
-    new_height, new_width = self.parent.getmaxyx()
+    with nyx.curses.raw_screen() as stdscr:
+      new_height, new_width = stdscr.getmaxyx()
+
     set_height, set_width = self.get_height(), self.get_width()
     new_height = max(0, new_height - self.top)
     new_width = max(0, new_width - self.left)
@@ -601,7 +601,8 @@ class Panel(object):
 
     display_width = self.get_preferred_size()[1]
 
-    input_subwindow = self.parent.subwin(1, display_width - x, self.top + y, self.left + x)
+    with nyx.curses.raw_screen() as stdscr:
+      input_subwindow = stdscr.subwin(1, display_width - x, self.top + y, self.left + x)
 
     # blanks the field's area, filling it with the font in case it's hilighting
 
@@ -744,7 +745,8 @@ class Panel(object):
     # would mean far more complicated code and no more selective refreshing)
 
     if recreate:
-      self.win = self.parent.subwin(new_height, new_width, self.top, self.left)
+      with nyx.curses.raw_screen() as stdscr:
+        self.win = stdscr.subwin(new_height, new_width, self.top, self.left)
 
       # note: doing this log before setting win produces an infinite loop
       stem.util.log.debug("recreating panel '%s' with the dimensions of %i/%i" % (self.get_name(), new_height, new_width))
diff --git a/nyx/panel/config.py b/nyx/panel/config.py
index 5b8a636..fc6dad1 100644
--- a/nyx/panel/config.py
+++ b/nyx/panel/config.py
@@ -118,8 +118,8 @@ class ConfigPanel(nyx.panel.Panel):
   Editor for tor's configuration.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'configuration', 0)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'configuration', 0)
 
     self._contents = []
     self._scroller = nyx.curses.CursorScroller()
diff --git a/nyx/panel/connection.py b/nyx/panel/connection.py
index e3124d9..09efafb 100644
--- a/nyx/panel/connection.py
+++ b/nyx/panel/connection.py
@@ -259,8 +259,8 @@ class ConnectionPanel(nyx.panel.Panel, threading.Thread):
   the current consensus and other data sources.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'connections', 0)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'connections', 0)
     threading.Thread.__init__(self)
     self.setDaemon(True)
 
diff --git a/nyx/panel/graph.py b/nyx/panel/graph.py
index 20a64c0..e4a0ce7 100644
--- a/nyx/panel/graph.py
+++ b/nyx/panel/graph.py
@@ -381,8 +381,8 @@ class GraphPanel(nyx.panel.Panel):
   Panel displaying graphical information of GraphCategory instances.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'graph', 0)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'graph', 0)
 
     self._displayed_stat = None if CONFIG['features.graph.type'] == 'none' else CONFIG['features.graph.type']
     self._update_interval = CONFIG['features.graph.interval']
diff --git a/nyx/panel/header.py b/nyx/panel/header.py
index 51245ee..c3bba36 100644
--- a/nyx/panel/header.py
+++ b/nyx/panel/header.py
@@ -37,8 +37,8 @@ class HeaderPanel(nyx.panel.Panel, threading.Thread):
   Top area containing tor settings and system information.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'header', 0)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'header', 0)
     threading.Thread.__init__(self)
     self.setDaemon(True)
 
diff --git a/nyx/panel/log.py b/nyx/panel/log.py
index 3ab53f3..d6e00de 100644
--- a/nyx/panel/log.py
+++ b/nyx/panel/log.py
@@ -64,8 +64,8 @@ class LogPanel(nyx.panel.Panel, threading.Thread):
   from tor's log file if it exists.
   """
 
-  def __init__(self, stdscr):
-    nyx.panel.Panel.__init__(self, stdscr, 'log', 0)
+  def __init__(self):
+    nyx.panel.Panel.__init__(self, 'log', 0)
     threading.Thread.__init__(self)
     self.setDaemon(True)
 
diff --git a/nyx/panel/torrc.py b/nyx/panel/torrc.py
index 033fd94..f52b6fb 100644
--- a/nyx/panel/torrc.py
+++ b/nyx/panel/torrc.py
@@ -20,8 +20,8 @@ class TorrcPanel(panel.Panel):
   area.
   """
 
-  def __init__(self, stdscr):
-    panel.Panel.__init__(self, stdscr, 'torrc', 0)
+  def __init__(self):
+    panel.Panel.__init__(self, 'torrc', 0)
 
     self._scroller = nyx.curses.Scroller()
     self._show_line_numbers = True  # shows left aligned line numbers
diff --git a/nyx/popups.py b/nyx/popups.py
index f29c9c8..6190a09 100644
--- a/nyx/popups.py
+++ b/nyx/popups.py
@@ -54,7 +54,7 @@ def popup_window(height = -1, width = -1, top = 0, left = 0, below_static = True
       else:
         sticky_height = 0
 
-      popup = nyx.panel.Panel(control.get_screen(), 'popup', top + sticky_height, left, height, width)
+      popup = nyx.panel.Panel('popup', top + sticky_height, left, height, width)
       popup.set_visible(True)
 
       # Redraws the popup to prepare a subwindow instance. If none is spawned then





More information about the tor-commits mailing list