sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 3478e400ae31b459b2875cc226796a6d4bba11f9
parent 3b05864b538d3d1c7aa77bc5cc2e4db58f0c9a6d
Author: William Morgan <wmorgan-sup@masanjin.net>
Date:   Thu,  6 Aug 2009 12:21:46 -0400

rewrap getch in select, handle sigwinch manually

This is necessary to operate with many versions of the ruby ncurses library,
all of which block threads when getch is being called. But this means we can't
use getch to determine a sigwich, since we won't see it until an actual key is
pressed. so we handle sigwinch ourselves.

Diffstat:
M bin/sup | 16 +++++++++++++++-
M lib/sup/buffer.rb | 24 ++++++++++++++++++------
2 files changed, 33 insertions(+), 7 deletions(-)
diff --git a/bin/sup b/bin/sup
@@ -161,6 +161,7 @@ begin
   Index.load
 
   trap("TERM") { |x| raise "so speaking as i think, i die, i die!" }
+  trap("WINCH") { |x| BufferManager.sigwinch_happened! }
 
   if(s = Redwood::SourceManager.source_for DraftManager.source_name)
     DraftManager.source = s
@@ -228,7 +229,20 @@ begin
       nil
     end
 
-    next unless c
+    if c.nil?
+      if BufferManager.sigwinch_happened?
+        Redwood::log "redrawing screen on sigwinch"
+        BufferManager.completely_redraw_screen
+      end
+      next
+    end
+
+    if c == 410
+      ## this is ncurses's way of telling us it's detected a refresh.
+      ## since we have our own sigwinch handler, we don't do anything.
+      next
+    end
+
     bm.erase_flash
 
     action = begin
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -26,12 +26,13 @@ module Ncurses
   def sync &b; mutex.synchronize(&b); end
 
   def nonblocking_getch
-    ## INSANITY
-    ## it is NECESSARY to call nodelay EVERY TIME otherwise a single ctrl-c
-    ## will turn a blocking call into a nonblocking one. hours of my life
-    ## wasted on this trivial bullshit: 3.
-    Ncurses.nodelay Ncurses.stdscr, false
-    Ncurses.getch
+    ## INSANTIY
+    ## it is NECESSARY to wrap Ncurses.getch in a select() otherwise all
+    ## background threads will be BLOCKED. (except in very modern versions
+    ## of libncurses-ruby. the current one on ubuntu seems to work well.)
+    if IO.select([$stdin], nil, nil, 0.5)
+      c = Ncurses.getch
+    end
   end
 
   module_function :rows, :cols, :curx, :nonblocking_getch, :mutex, :sync
@@ -195,10 +196,15 @@ EOS
     @flash = nil
     @shelled = @asking = false
     @in_x = ENV["TERM"] =~ /(xterm|rxvt|screen)/
+    @sigwinch_happened = false
+    @sigwinch_mutex = Mutex.new
 
     self.class.i_am_the_instance self
   end
 
+  def sigwinch_happened!; @sigwinch_mutex.synchronize { @sigwinch_happened = true } end
+  def sigwinch_happened?; @sigwinch_mutex.synchronize { @sigwinch_happened } end
+
   def buffers; @name_map.to_a; end
 
   def focus_on buf
@@ -260,6 +266,12 @@ EOS
   def completely_redraw_screen
     return if @shelled
 
+    ## this magic makes Ncurses get the new size of the screen
+    Ncurses.endwin
+    Ncurses.refresh
+    @sigwinch_mutex.synchronize { @sigwinch_happened = false }
+    Redwood::log "new screen size is #{Ncurses.rows} x #{Ncurses.cols}"
+
     status, title = get_status_and_title(@focus_buf) # must be called outside of the ncurses lock
 
     Ncurses.sync do