Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

PATCH: subwindows, touching, better refreshing



These changes were inspired by trying to get the function below to
work.  I mistakenly thought that using a subwindow meant I could
have the main window automatically restored when the subwindow
is deleted, but it's actually the opposite: a subwindow shares the
same memory whereas a new window doesn't.  By the time I'd realised that
I'd added tracking of parents and children.

The key to getting windows removed is to touch the windows behind them.
There's no easy way to automate this, so I've simply added a "touch"
subcommand.  (I have, however, tried to help by adding some touchwin()s
where they seem inevitable.)  Also, I've optimized "refresh" so that you
can give it a list and the update only happens at the end.

Use the function keys to move round a rectangle.  You get a warning
and a second's delay if you hit the edge (input timeouts won't be hard
since curses does the hard work and they're on my list).  Any other key
exits.


curses_bang() {
  # Arrow keys to move, anything else to exit.
  zmodload zsh/curses
  
  local REPLY key
  integer h=$(( LINES - 10 )) w=$((COLUMNS - 20))
  integer x=1 y=1
  
  bang() {
    zcurses addwin bang 1 5 $(( y + 5 )) $(( x + 10 ))
    zcurses attr bang red/green bold
    zcurses string bang 'BANG!'
    zcurses refresh bang
    sleep 1
    zcurses delwin bang
    zcurses touch main
    zcurses refresh stdscr main
  }
  
  {
    zcurses init
  
    zcurses addwin main $(( LINES - 10 )) $(( COLUMNS - 20 )) 5 10
    zcurses border main
  
    zcurses move main $y $x
    zcurses refresh main
  
    while true; do
      zcurses input main REPLY key
      case $key in
        (UP)
        if (( y == 1 )); then
  	  bang
        else
  	  zcurses string main "^"
  	  (( y-- ))
  	  zcurses move main $y $x
  	  zcurses refresh main
        fi
        ;;
  
        (DOWN)
        if (( y == h - 2 )); then
  	  bang
        else
  	  zcurses string main "v"
  	  (( y++ ))
  	  zcurses move main $y $x
  	  zcurses refresh main
        fi
        ;;
  
        (LEFT)
        if (( x == 1 )); then
  	  bang
        else
  	  zcurses string main "<"
  	  (( x-- ))
  	  zcurses move main $y $x
  	  zcurses refresh main
        fi
        ;;
  
        (RIGHT)
        if (( x == w - 2 )); then
  	  bang
        else
  	  zcurses string main ">"
  	  (( x++ ))
  	  zcurses move main $y $x
  	  zcurses refresh main
        fi
        ;;
  
        ("")
        break
        ;;
      esac
    done
  
  } always {
    zcurses delwin main
    zcurses end
  }
}


Index: Doc/Zsh/mod_curses.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/mod_curses.yo,v
retrieving revision 1.15
diff -u -r1.15 mod_curses.yo
--- Doc/Zsh/mod_curses.yo	28 Oct 2007 00:21:54 -0000	1.15
+++ Doc/Zsh/mod_curses.yo	28 Oct 2007 19:28:48 -0000
@@ -11,12 +11,13 @@
 cindex(windows, curses)
 xitem(tt(zcurses) tt(init))
 xitem(tt(zcurses) tt(end))
-xitem(tt(zcurses) tt(addwin) var(targetwin) var(nlines) var(ncols) var(begin_y) var(begin_x) )
+xitem(tt(zcurses) tt(addwin) var(targetwin) var(nlines) var(ncols) var(begin_y) var(begin_x) [ var(parentwin) ] )
 xitem(tt(zcurses) tt(delwin) var(targetwin) )
-xitem(tt(zcurses) tt(refresh) [ var(targetwin) ] )
+xitem(tt(zcurses) tt(refresh) [ var(targetwin) ... ] )
+xitem(tt(zcurses) tt(touch) var(targetwin) ...)
 xitem(tt(zcurses) tt(move) var(targetwin) var(new_y) var(new_x) )
 xitem(tt(zcurses) tt(clear) var(targetwin) [ tt(redraw) | tt(eol) | tt(bot) ])
-xitem(tt(location) var(targetwin) var(array))
+xitem(tt(zcurses) tt(location) var(targetwin) var(array))
 xitem(tt(zcurses) tt(char) var(targetwin) var(character) )
 xitem(tt(zcurses) tt(string) var(targetwin) var(string) )
 xitem(tt(zcurses) tt(border) var(targetwin) var(border) )(
@@ -35,6 +36,13 @@
 in particular the curses convention that vertical values appear
 before horizontal values.
 
+If tt(addwin) is given an existing window as the final argument, the new
+window is created as a subwindow of var(parentwin).  This differs from an
+ordinary new window in that the memory of the window contents is shared
+with the parent's memory.  Subwindows must be deleted before their parent.
+Note that the coordinates of subwindows are relative to the screen, not
+the parent, as with other windows
+
 Use tt(delwin) to delete a window created with tt(addwin).  Note
 that tt(end) does em(not) implicitly delete windows, and that
 tt(delwin) does not erase the screen image of the window.
@@ -47,6 +55,11 @@
 necessary to make any pending changes (such as characters you have
 prepared for output with tt(char)) visible on the screen.  tt(refresh)
 without an argument causes the screen to be cleared and redrawn.
+If multiple windows are given, the screen is updated once at the end.
+
+The tt(touch) command marks the var(targetwin)s listed as changed.
+This is necessary before tt(refresh)ing windows if a window that
+was in front of another window (which may be tt(stdscr)) is deleted.
 
 tt(move) moves the cursor position in var(targetwin) to new coordinates
 var(new_y) and var(new_x).
Index: Src/Modules/curses.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/Modules/curses.c,v
retrieving revision 1.27
diff -u -r1.27 curses.c
--- Src/Modules/curses.c	28 Oct 2007 00:21:55 -0000	1.27
+++ Src/Modules/curses.c	28 Oct 2007 19:28:48 -0000
@@ -59,11 +59,15 @@
     ZCWF_SCROLL = 0x0002
 };
 
-typedef struct zc_win {
+typedef struct zc_win *ZCWin;
+
+struct zc_win {
     WINDOW *win;
     char *name;
     int flags;
-} *ZCWin;
+    LinkList children;
+    ZCWin parent;
+};
 
 struct zcurses_namenumberpair {
     char *name;
@@ -211,6 +215,9 @@
     if (w->name)
 	zsfree(w->name);
 
+    if (w->children)
+	freelinklist(w->children, (FreeFunc)NULL);
+
     zfree(w, sizeof(struct zc_win));
 
     return 0;
@@ -410,11 +417,37 @@
 	return 1;
 
     w->name = ztrdup(args[0]);
-    w->win = newwin(nlines, ncols, begin_y, begin_x);
+    if (args[5]) {
+	LinkNode node;
+	ZCWin worig;
+
+	node = zcurses_validate_window(args[5], ZCURSES_USED);
+	if (node == NULL) {
+	    zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0],
+		     0);
+	    zsfree(w->name);
+	    zfree(w, sizeof(struct zc_win));
+	    return 1;
+	}
+
+	worig = (ZCWin)getdata(node);
+
+	w->win = subwin(worig->win, nlines, ncols, begin_y, begin_x);
+	if (w->win) {
+	    w->parent = worig;
+	    if (!worig->children)
+		worig->children = znewlinklist();
+	    zinsertlinknode(worig->children, lastnode(worig->children),
+			    (void *)w);
+	}
+    } else {
+	w->win = newwin(nlines, ncols, begin_y, begin_x);
+    }
 
     if (w->win == NULL) {
+	zwarnnam(nam, "failed to create window `%s'", w->name);
 	zsfree(w->name);
-	free(w);
+	zfree(w, sizeof(struct zc_win));
 	return 1;
     }
 
@@ -428,6 +461,7 @@
 {
     LinkNode node;
     ZCWin w;
+    int ret = 0;
 
     node = zcurses_validate_window(args[0], ZCURSES_USED);
     if (node == NULL) {
@@ -445,39 +479,84 @@
 	zwarnnam(nam, "window `%s' can't be deleted", args[0]);
 	return 1;
     }
-    if (delwin(w->win)!=OK)
+
+    if (w->children && firstnode(w->children)) {
+	zwarnnam(nam, "window `%s' has subwindows, delete those first",
+		 w->name);
 	return 1;
+    }
+
+    if (delwin(w->win)!=OK) {
+	/*
+	 * Not sure what to do here, but we are probably stuffed,
+	 * so delete the window locally anyway.
+	 */
+	ret = 1;
+    }
+
+    if (w->parent) {
+	/* Remove from parent's list of children */
+	LinkList wpc = w->parent->children;
+	LinkNode pcnode;
+	for (pcnode = firstnode(wpc); pcnode; incnode(pcnode)) {
+	    ZCWin child = (ZCWin)getdata(pcnode);
+	    if (child == w) {
+		remnode(wpc, pcnode);
+		break;
+	    }
+	}
+	DPUTS(pcnode == NULL, "BUG: child node not found in parent's children");
+	/*
+	 * We need to touch the parent to get the parent to refresh
+	 * properly.
+	 */
+	touchwin(w->parent->win);
+    }
+    else
+	touchwin(stdscr);
 
     if (w->name)
 	zsfree(w->name);
 
     zfree((ZCWin)remnode(zcurses_windows, node), sizeof(struct zc_win));
 
-    return 0;
+    return ret;
 }
 
 
 static int
 zccmd_refresh(const char *nam, char **args)
 {
-    if (args[0]) {
-	LinkNode node;
-	ZCWin w;
+    WINDOW *win;
+    int ret = 0;
 
-	node = zcurses_validate_window(args[0], ZCURSES_USED);
-	if (node == NULL) {
-	    zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0],
-		     0);
-	    return 1;
-	}
+    if (args[0]) {
+	for (; *args; args++) {
+	    LinkNode node;
+	    ZCWin w;
+
+	    node = zcurses_validate_window(args[0], ZCURSES_USED);
+	    if (node == NULL) {
+		zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0],
+			 0);
+		return 1;
+	    }
 
-	w = (ZCWin)getdata(node);
+	    w = (ZCWin)getdata(node);
 
-	return (wrefresh(w->win)!=OK) ? 1 : 0;
+	    if (w->parent) {
+		/* This is what the manual says you have to do. */
+		touchwin(w->parent->win);
+	    }
+	    win = w->win;
+	    if (wnoutrefresh(win) != OK)
+		ret = 1;
+	}
+	return (doupdate() != OK || ret);
     }
     else
     {
-	return (wrefresh(curscr) != OK) ? 1 : 0;
+	return (wrefresh(stdscr) != OK) ? 1 : 0;
     }
 }
 
@@ -890,6 +969,29 @@
 }
 
 
+static int
+zccmd_touch(const char *nam, char **args)
+{
+    LinkNode node;
+    ZCWin w;
+    int ret = 0;
+
+    for (; *args; args++) {
+	node = zcurses_validate_window(args[0], ZCURSES_USED);
+	if (node == NULL) {
+	    zwarnnam(nam, "%s: %s", zcurses_strerror(zc_errno), args[0]);
+	    return 1;
+	}
+
+	w = (ZCWin)getdata(node);
+	if (touchwin(w->win) != OK)
+	    ret = 1;
+    }
+
+    return ret;
+}
+
+
 /*********************
   Main builtin handler
  *********************/
@@ -904,9 +1006,9 @@
 
     struct zcurses_subcommand scs[] = {
 	{"init", zccmd_init, 0, 0},
-	{"addwin", zccmd_addwin, 5, 5},
+	{"addwin", zccmd_addwin, 5, 6},
 	{"delwin", zccmd_delwin, 1, 1},
-	{"refresh", zccmd_refresh, 0, 1},
+	{"refresh", zccmd_refresh, 0, -1},
 	{"move", zccmd_move, 3, 3},
 	{"clear", zccmd_clear, 1, 2},
 	{"position", zccmd_position, 2, 2},
@@ -917,6 +1019,7 @@
 	{"attr", zccmd_attr, 2, -1},
 	{"scroll", zccmd_scroll, 2, 2},
 	{"input", zccmd_input, 1, 3},
+	{"touch", zccmd_touch, 1, -1},
 	{NULL, (zccmd_t)0, 0, 0}
     };
 
@@ -954,7 +1057,7 @@
 
 
 static struct builtin bintab[] = {
-    BUILTIN("zcurses", 0, bin_zcurses, 1, 6, 0, "", NULL),
+    BUILTIN("zcurses", 0, bin_zcurses, 1, -1, 0, "", NULL),
 };
 
-- 
Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
Web page now at http://homepage.ntlworld.com/p.w.stephenson/



Messages sorted by: Reverse Date, Date, Thread, Author