Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
PATCH: subwindows, touching, better refreshing
- X-seq: zsh-workers 24027
- From: Peter Stephenson <p.w.stephenson@xxxxxxxxxxxx>
- To: zsh-workers@xxxxxxxxxx (Zsh hackers list)
- Subject: PATCH: subwindows, touching, better refreshing
- Date: Sun, 28 Oct 2007 19:30:39 +0000
- Mailing-list: contact zsh-workers-help@xxxxxxxxxx; run by ezmlm
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