Index: Makefile.am
===================================================================
--- Makefile.am	(revision 9d27ee8678625706f5eead430bbe2f956fc1eb41)
+++ Makefile.am	(revision 9d27ee8678625706f5eead430bbe2f956fc1eb41)
@@ -39,5 +39,5 @@
            $(GIT_FLAGS)
 
-BASE_SRCS=list.c message.c mainwin.c popwin.c zephyr.c messagelist.c \
+CODELIST_SRCS=list.c message.c mainwin.c popwin.c zephyr.c messagelist.c \
      commands.c global.c text.c fmtext.c editwin.c util.c logging.c \
      perlconfig.c keys.c functions.c zwrite.c viewwin.c help.c filter.c \
@@ -46,5 +46,9 @@
      aim.c buddy.c buddylist.c style.c errqueue.c \
      zbuddylist.c popexec.c select.c wcwidth.c \
-     glib_compat.c filterproc.c
+     glib_compat.c mainpanel.c msgwin.c sepbar.c
+
+NORMAL_SRCS = filterproc.c window.c windowcb.c
+
+BASE_SRCS = $(CODELIST_SRCS) $(NORMAL_SRCS)
 
 GEN_C = varstubs.c perlglue.c
@@ -68,5 +72,5 @@
 	$(AM_V_GEN)perl $< $(sort $(filter-out $<,$+)) > $@
 
-owl_prototypes.h.new: codelist.pl varstubs.c $(BASE_SRCS)
+owl_prototypes.h.new: codelist.pl varstubs.c $(CODELIST_SRCS)
 	$(AM_V_GEN)perl $< $(sort $(filter-out $<,$+)) > $@
 
Index: cmd.c
===================================================================
--- cmd.c	(revision 1cc9b61526eafe04cf53d24712f11ab9ac0fbb8b)
+++ cmd.c	(revision 1cc9b61526eafe04cf53d24712f11ab9ac0fbb8b)
@@ -74,4 +74,6 @@
   } else if (NULL != (cmd = owl_dict_find_element(cd, argv[0]))) {
     retval = owl_cmd_execute(cmd, cd, ctx, argc, argv, buff);
+    /* redraw the sepbar; TODO: don't violate layering */
+    owl_global_sepbar_dirty(&g);
   } else {
     owl_function_makemsg("Unknown command '%s'.", buff);
@@ -90,5 +92,4 @@
   if (argc < 0) {
     owl_free(tmpbuff);
-    sepbar(NULL);
     owl_function_makemsg("Unbalanced quotes");
     return NULL;
@@ -105,5 +106,4 @@
   owl_parse_delete(argv, argc);
   owl_free(tmpbuff);
-  sepbar(NULL);
   return retval;
 }
Index: commands.c
===================================================================
--- commands.c	(revision 987cf3f48057443b84ecf140854129cf1a6cbbdc)
+++ commands.c	(revision 987cf3f48057443b84ecf140854129cf1a6cbbdc)
@@ -1371,5 +1371,4 @@
 {
   owl_function_full_redisplay();
-  owl_global_set_needrefresh(&g);
 }
 
@@ -1387,6 +1386,4 @@
 {
   owl_global_set_rightshift(&g, shift);
-  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-  owl_global_set_needrefresh(&g);
 }
 
@@ -2693,5 +2690,4 @@
   }
 
-  owl_global_set_needrefresh(&g);
   owl_global_pop_context(&g);
 
@@ -2714,6 +2710,4 @@
     owl_editwin_clear(e);
     owl_editwin_insert_string(e, ptr);
-    owl_editwin_redisplay(e);
-    owl_global_set_needrefresh(&g);
   } else {
     owl_function_beep();
@@ -2731,6 +2725,4 @@
     owl_editwin_clear(e);
     owl_editwin_insert_string(e, ptr);
-    owl_editwin_redisplay(e);
-    owl_global_set_needrefresh(&g);
   } else {
     owl_function_beep();
@@ -2742,6 +2734,4 @@
   buff = skiptokens(buff, 1);
   owl_editwin_insert_string(e, buff);
-  owl_editwin_redisplay(e);
-  owl_global_set_needrefresh(&g);  
   return NULL;
 }
@@ -2758,5 +2748,4 @@
   owl_global_set_typwin_inactive(&g);
   owl_global_pop_context(&g);
-  owl_global_set_needrefresh(&g);
 
   owl_editwin_do_callback(e);
@@ -2780,7 +2769,6 @@
 void owl_command_popless_quit(owl_viewwin *vw)
 {
+  owl_viewwin_cleanup(vw);
   owl_popwin_close(owl_global_get_popwin(&g));
   owl_global_pop_context(&g);
-  owl_viewwin_cleanup(vw);
-  owl_global_set_needrefresh(&g);
-}
+}
Index: configure.ac
===================================================================
--- configure.ac	(revision 04af908ca44749f771ae778a21315327b6d24135)
+++ configure.ac	(revision 04af908ca44749f771ae778a21315327b6d24135)
@@ -115,5 +115,5 @@
 
 dnl Add CFLAGS and LDFLAGS for glib-2.0
-PKG_CHECK_MODULES(GLIB,glib-2.0)
+PKG_CHECK_MODULES(GLIB,[glib-2.0 gobject-2.0])
 
 AC_MSG_NOTICE([Adding glib-2.0 CFLAGS ${GLIB_CFLAGS}])
Index: context.c
===================================================================
--- context.c	(revision 2a17b635c6895121650446d2529f46d7e291a486)
+++ context.c	(revision 07b59ea301a55bf3db1724aa9571e7048fe89857)
@@ -9,4 +9,5 @@
   ctx->mode = OWL_CTX_STARTUP;
   ctx->data = NULL;
+  ctx->cursor = NULL;
   return 0;
 }
Index: doc/owl-window.txt
===================================================================
--- doc/owl-window.txt	(revision 95d54e6fd988cf92c13614431dc8504cd5264e95)
+++ doc/owl-window.txt	(revision 95d54e6fd988cf92c13614431dc8504cd5264e95)
@@ -0,0 +1,191 @@
+				==========
+				owl_window
+				==========
+
+owl_window is intended to be a wrapper over ncurses WINDOW and PANEL
+objects to abstract away much of the nastiness that is ncurses. It
+provides redraw scheduling, and sane window moving and resizing.
+
+-------------------
+Hierarchy and types
+-------------------
+
+owl_window creates a hierarchy of window objects. The hierarchy
+currently uses ncurses' built-in window hierarchy system with
+subwins. This was easier to implement but has some caveats, detailed
+later.
+
+At the top level, we have the screen window which represents the
+actual screen. The screen window is not usually interacted with
+directly, but rather it is a handle to interface with the terminal as
+if it were a normal window.
+
+Underneath the screen, we have top-level windows, or panels. Panels
+are normal WINDOWs (newwin) connected to a PANEL (from
+libpanel). Panels may overlap freely and maintain a stacking
+order. (For now, this stacking order is not exposed except for a
+mechanism to move a panel to the top. This is not difficult to
+otherwise correct otherwise.)
+
+Under each panel is a tree of subwins. These are backed by ncurses
+subwins. Because ncurses subwins simply share their parent windows'
+buffer, we cannot provide as many nice guarantees about
+ordering. Sibling subwins may not overlap, and parents must
+(sometimes) take children position into account. More on this
+later. This model is sufficient for BarnOwl's current purposes. Should
+we need to, this may later be reworked. (Specifically, we'd want to
+back everything by pads and build a compositing window manager.)
+
+Each of these three types is temporarily stuffed into one type. We can
+later do subtyping of sorts, but that will require more heavy use of
+GObject.
+
+As an example, here is the graph used by BarnOwl's current interface:
+
+      	       	       	+==========+
+     	      	      	|| screen ||
+      	       	       	+==========+
+                          /      \
+                +-----------+   +--------+
+                | mainpanel |   | popwin |
+                +-----------+   +--------+
+               /   /    |    \          \
+         recwin sepwin msgwin typwin   viewwin
+
+
+A window may be unlinked from its parent with a call to
+owl_window_unlink. From then on, the object is still accessible, but
+it will not do anything. If desired, we may add the ability to relink
+a window in future. This behavior allows us to safely hold references
+to windows, even after they have been "destroyed".
+
+----------
+Visibility
+----------
+
+Each window maintains state for whether or not the user has requested
+it be visible. A user calls owl_window_show to flip the 'shown' bit on
+a window and owl_window_show_all to do so recursively. Likewise,
+owl_window_hide disables this bit. If a window and all its parents are
+shown, then the user has requested this window be visible.
+
+We say a window is realized if it has a corresponding on-screen
+window. A window will only be realized if it is requested to be
+shown. Furthermore, the window's parent must also be realized (unless
+it is the screen). If a window is failed to be created for any reason
+(most notably if its dimensions are zero), owl_window will cope and
+consider it unrealized.
+
+This realized/unrealized state fixes two nuisances with the old code:
+
+     First, owl_window can safely manage all NULL windows. Interacting
+     code needn't check to avoid segfaults; the owl_window is never
+     NULL, and code requesting the WINDOW will never be called when
+     NULL.
+
+     Second, we have a consistent handle to a logical window, despite
+     changes in the physical window. This is important for resizing
+     windows. It is difficult to safely resize windows in ncurses. It
+     is usually far easier to destroy everything and recreate it.
+
+Note that this means owl_window will intentionally never expose the
+underlying WINDOW except during appropriate events.
+
+--------
+Resizing
+--------
+
+Windows may be moved and resized with owl_window_move,
+owl_window_resize, and owl_window_set_position. Internally, resizing
+is very simple. We unrealize the window, set new position, and then
+realize it at the new location. When a window changes size, it emits a
+"resized" signal which windows may react to. It may actually possible
+to optimize moves, but this has a slight nuisance with incorrect
+begy/begx values which cursors currently rely on.
+
+It is intended that top-level windows connect to this signal to change
+themselves, while windows containing subwins connect to their own
+signals to relayout their subwins. This is because top-level windows
+may be sized independently, while sibling subwins should not
+overlap. The signals are implemented currently with GObject signals.
+
+---------
+Redrawing
+---------
+
+Currently, users of widgets in BarnOwl must remember to call
+owl_foo_redisplay when a widget needs to be redrawn. This is quite
+annoying and an implementation detail that functions like
+owl_editwin_insert_string should take care of. To allow widget
+implementations to redraw themselves without calling an expensive
+redisplay many times, owl_window_dirty flags a window as "dirty". The
+framework will promise to redraw that window before the next doupdate,
+while repeated owl_window_dirty calls remain cheap.
+
+Windows currently redraw with a "redraw" GObject signal. This and the
+resize signal, is somewhat awkward for the editwin which attaches to
+and releases windows, as we must remember signal ids. However, if we
+make widget objects (owl_editwin, owl_mainwin, etc.) also GObjects,
+these signals will automatically detach. We may also consider a
+different mechanism than continuously attaching/detaching
+things. That's kinda weird anyway.
+
+(We may want to replace it with a normal callback at some
+point. Mostly using the GObject signals to play with them, and because
+we'd likely get some form of automatic binding generation.)
+
+----------------------
+Known issues and notes
+----------------------
+
+- owl_window does not export the underlying WINDOW*. This is
+  intentional. To safely resize windows, owl_window reserves the right
+  to destroy/recreate the window whenever it feels like. We can add
+  "window-realized"/"window-unrealized" signals if people really want
+  though.
+
+- This is currently using GObject. This buys us a lot of niceness, but
+  it is adding a new library with full-blown (and somewhat
+  overengineered) object system. It's very useful for prototyping, but
+  if people want, it can be replaced. I think it's actually
+  worthwhile, as most of the overengineering is explicitly designed
+  for language bindings, something we could be better at.
+
+- There is this really sketchy "three types in one" thing going on
+  with the screen, panels, and subwins. I'd like to refactor that into
+  some sort of subtyping, but that would ideally involve even more use
+  of GObject.
+
+- owl_mainwin is not very well ported and the windows do not have a
+  very consistent implementation. This is a known problem that will be
+  addressed in the next iteration. The current ports were done partly
+  as experiments for conventions and mostly to get something working
+  as soon as possible. Among things that should change is for the
+  widgets to all use new/delete/pointers instead of
+  init/cleanup/embed-in-struct.
+
+- The editwin and a few others are strange and keep track of signal
+  ids. This is somewhat a side effect of us not using GObject
+  everywhere; the signals can automatically disconnect in the right
+  contexts if we do.
+
+- The sepbar depends on a value computed while the mainwin is drawn,
+  so we currently ensure the windows are created in the right order
+  for the repaints to occur correctly. This is rather poor and should
+  be refactored later.
+
+- The code fairly routinely does casts to add extra throw-away
+  parameters to functions. Most notably, casting functions of type
+
+      void do_something(void *obj)
+
+  to something of type
+
+      void do_something(void *obj, void *user_data)
+
+  (the latter is a GFunc) with the expectation that the user_data
+  argument is discarded. While calling with this cast is undefined by
+  the standard and depends on calling convention, glib uses it
+  internally /everywhere/ and much of glib and gobject API heavily
+  depends on it. As BarnOwl already depends on glib, we implicitly
+  assume this cast works.
Index: editwin.c
===================================================================
--- editwin.c	(revision 9d7a7205d6c9cbea0e8999e4c350440f8ef5fef8)
+++ editwin.c	(revision 3f11c00e8b62c8bf4c5a6b6f51f8f3e12cfd8fcf)
@@ -28,5 +28,7 @@
   int cursorx;
   int winlines, wincols, fillcol, wrapcol;
-  WINDOW *curswin;
+  owl_window *win;
+  gulong repaint_id;
+  gulong resized_id;
   int style;
   int lock;
@@ -40,4 +42,6 @@
 };
 
+static void oe_set_window(owl_editwin *e, owl_window *w, int winlines, int wincols);
+static void oe_redraw(owl_window *win, WINDOW *curswin, void *user_data);
 static void oe_reframe(owl_editwin *e);
 static void oe_save_excursion(owl_editwin *e, oe_excursion *x);
@@ -58,4 +62,6 @@
 static char *oe_chunk(owl_editwin *e, int start, int end);
 static void oe_destroy_cbdata(owl_editwin *e);
+static void oe_dirty(owl_editwin *e);
+static void oe_window_resized(owl_window *w, owl_editwin *e);
 
 #define INCR 4096
@@ -73,4 +79,8 @@
 void owl_editwin_delete(owl_editwin *e)
 {
+  if (e->win) {
+    g_signal_handler_disconnect(e->win, e->repaint_id);
+    g_signal_handler_disconnect(e->win, e->resized_id);
+  }
   owl_free(e->buff);
   owl_free(e->killbuf);
@@ -91,4 +101,5 @@
   }
   e->index = index;
+  oe_dirty(e);
 }
 
@@ -105,5 +116,4 @@
 
 static void _owl_editwin_init(owl_editwin *e,
-                              WINDOW *win,
                               int winlines,
                               int wincols,
@@ -130,23 +140,27 @@
     e->style=OWL_EDITWIN_STYLE_MULTILINE;
   }
-  owl_editwin_set_curswin(e, win, winlines, wincols);
   e->lock=0;
   e->dotsend=0;
   e->echochar='\0';
-
-  if (win) werase(win);
-}
-
-owl_editwin *owl_editwin_new(WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
+}
+
+owl_editwin *owl_editwin_new(owl_window *win, int winlines, int wincols, int style, owl_history *hist)
 {
   owl_editwin *e = owl_editwin_allocate();
 
-  _owl_editwin_init(e, win, winlines, wincols, style, hist);
+  _owl_editwin_init(e, winlines, wincols, style, hist);
+  oe_set_window(e, win, winlines, wincols);
   return e;
 }
 
-void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols)
-{
-  e->curswin=w;
+static void oe_window_resized(owl_window *w, owl_editwin *e)
+{
+  /* update the sizes */
+  owl_window_get_position(w, &e->winlines, &e->wincols, NULL, NULL);
+}
+
+static void oe_set_window(owl_editwin *e, owl_window *w, int winlines, int wincols)
+{
+  e->win=w;
   e->winlines=winlines;
   e->wincols=wincols;
@@ -156,4 +170,9 @@
   else
     e->wrapcol = 0;
+  if (e->win) {
+    e->repaint_id = g_signal_connect(w, "redraw", G_CALLBACK(oe_redraw), e);
+    e->resized_id = g_signal_connect(w, "resized", G_CALLBACK(oe_window_resized), e);
+    owl_window_dirty(e->win);
+  }
 }
 
@@ -165,9 +184,5 @@
 {
   e->echochar=ch;
-}
-
-WINDOW *owl_editwin_get_curswin(owl_editwin *e)
-{
-  return(e->curswin);
+  oe_dirty(e);
 }
 
@@ -239,5 +254,5 @@
   e->lock=e->bufflen;
   oe_set_index(e, e->lock);
-  owl_editwin_redisplay(e);
+  oe_dirty(e);
 }
 
@@ -265,5 +280,5 @@
 
   owl_free(e->buff);
-  _owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
+  _owl_editwin_init(e, e->winlines, e->wincols, e->style, e->hist);
 
   if (lock > 0) {
@@ -286,4 +301,5 @@
 {
   e->topindex = -1;
+  oe_dirty(e);
 }
 
@@ -467,28 +483,30 @@
 
   oe_restore_excursion(e, &x);
-}
-
-static void oe_addnec(owl_editwin *e, int count)
+  oe_dirty(e);
+}
+
+static void oe_addnec(owl_editwin *e, WINDOW *curswin, int count)
 {
   int i;
 
   for (i = 0; i < count; i++)
-    waddch(e->curswin, e->echochar);
-}
-
-static void oe_mvaddnec(owl_editwin *e, int y, int x, int count)
-{
-  wmove(e->curswin, y, x);
-  oe_addnec(e, count);
+    waddch(curswin, e->echochar);
+}
+
+static void oe_mvaddnec(owl_editwin *e, WINDOW *curswin, int y, int x, int count)
+{
+  wmove(curswin, y, x);
+  oe_addnec(e, curswin, count);
 }
 
 /* regenerate the text on the curses window */
-void owl_editwin_redisplay(owl_editwin *e)
+static void oe_redraw(owl_window *win, WINDOW *curswin, void *user_data)
 {
   int x = -1, y = -1, t, hard;
   int line, index, lineindex, times = 0;
+  owl_editwin *e = user_data;
 
   do {
-    werase(e->curswin);
+    werase(curswin);
 
     if (e->topindex == -1 || e->index < e->topindex)
@@ -505,22 +523,22 @@
       if (index - lineindex) {
 	if (!e->echochar)
-	  mvwaddnstr(e->curswin, line, 0,
+	  mvwaddnstr(curswin, line, 0,
 		     e->buff + lineindex,
 		     index - lineindex);
 	else {
 	  if(lineindex < e->lock) {
-	    mvwaddnstr(e->curswin, line, 0,
+	    mvwaddnstr(curswin, line, 0,
 		       e->buff + lineindex,
 		       MIN(index - lineindex,
 			   e->lock - lineindex));
 	    if (e->lock < index)
-	      oe_addnec(e,
+	      oe_addnec(e, curswin,
 			oe_region_width(e, e->lock, index,
 					oe_region_width(e, lineindex, e->lock, 0)));
 	  } else
-	    oe_mvaddnec(e, line, 0, oe_region_width(e, lineindex, index, 0));
+	    oe_mvaddnec(e, curswin, line, 0, oe_region_width(e, lineindex, index, 0));
 	}
         if (!hard)
-          waddch(e->curswin, '\\');
+          waddch(curswin, '\\');
       }
       line++;
@@ -531,5 +549,5 @@
   } while(x == -1 && times < 3);
 
-  wmove(e->curswin, y, x);
+  wmove(curswin, y, x);
   e->cursorx = x;
 }
@@ -624,4 +642,6 @@
   if (start <= e->topindex)
     owl_editwin_recenter(e);
+
+  oe_dirty(e);
 
   return change;
@@ -857,4 +877,5 @@
 
   e->goal_column = goal_column;
+  oe_dirty(e);
 
   return distance;
@@ -1211,5 +1232,4 @@
     return;
   }
-  owl_editwin_redisplay(e);
 }
 
@@ -1346,4 +1366,9 @@
 }
 
+static void oe_dirty(owl_editwin *e)
+{
+  if (e->win) owl_window_dirty(e->win);
+}
+
 
 
Index: functions.c
===================================================================
--- functions.c	(revision 7bfc61380289737bd52f9cae9175a60839766b6a)
+++ functions.c	(revision 8bba1aed9c99b38e86fab76c25d3b415be31afdb)
@@ -216,5 +216,4 @@
   /* redisplay etc. */
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-  owl_global_set_needrefresh(&g);
 }
 
@@ -287,5 +286,5 @@
   owl_editwin_set_cbdata(e, data, cleanup);
   owl_editwin_set_callback(e, callback);
-  owl_global_push_context(&g, OWL_CTX_EDITMULTI, e, "editmulti");
+  owl_global_push_context(&g, OWL_CTX_EDITMULTI, e, "editmulti", owl_global_get_typwin_window(&g));
 }
 
@@ -786,6 +785,4 @@
 {
   owl_global_set_rightshift(&g, owl_global_get_rightshift(&g)+10);
-  owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-  owl_global_set_needrefresh(&g);
 }
 
@@ -797,6 +794,4 @@
   if (shift > 0) {
     owl_global_set_rightshift(&g, MAX(shift - 10, 0));
-    owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-    owl_global_set_needrefresh(&g);
   } else {
     owl_function_beep();
@@ -1201,5 +1196,4 @@
   if (owl_global_is_bell(&g)) {
     beep();
-    owl_global_set_needrefresh(&g); /* do we really need this? */
   }
 }
@@ -1230,25 +1224,14 @@
 }
 
-void owl_function_set_cursor(WINDOW *win)
-{
-  /* Be careful that this window is actually empty, otherwise panels get confused */
-  if (is_wintouched(win)) {
-    owl_function_debugmsg("Warning: owl_function_set_cursor called on dirty window");
-    update_panels();
-  }
-  wnoutrefresh(win);
+static void _dirty_everything(owl_window *w) {
+  if (!owl_window_is_realized(w))
+    return;
+  owl_window_dirty(w);
+  owl_window_children_foreach(w, (GFunc)_dirty_everything, NULL);
 }
 
 void owl_function_full_redisplay(void)
 {
-  touchwin(owl_global_get_curs_recwin(&g));
-  touchwin(owl_global_get_curs_sepwin(&g));
-  touchwin(owl_global_get_curs_typwin(&g));
-  touchwin(owl_global_get_curs_msgwin(&g));
-
-  sepbar("");
-  owl_function_makemsg("");
-
-  owl_global_set_needrefresh(&g);
+  _dirty_everything(owl_window_get_screen());
 }
 
@@ -1262,10 +1245,6 @@
 
   owl_popwin_up(pw);
-  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
-  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
-			owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
-			text);
-  owl_viewwin_redisplay(v);
-  owl_global_set_needrefresh(&g);
+  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
+  owl_viewwin_init_text(v, owl_popwin_get_content(pw), text);
 }
 
@@ -1279,10 +1258,6 @@
 
   owl_popwin_up(pw);
-  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
-  owl_viewwin_init_fmtext(v, owl_popwin_get_curswin(pw),
-		   owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
-		   fm);
-  owl_viewwin_redisplay(v);
-  owl_global_set_needrefresh(&g);
+  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
+  owl_viewwin_init_fmtext(v, owl_popwin_get_content(pw), fm);
 }
 
@@ -1544,5 +1519,4 @@
   /* redisplay */
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-  owl_global_set_needrefresh(&g);
 }
 
@@ -1550,5 +1524,5 @@
 {
   owl_global_set_typwin_lines(&g, newsize);
-  owl_global_set_relayout_pending(&g);
+  owl_mainpanel_layout_contents(&g.mainpanel);
 }
 
@@ -1664,5 +1638,4 @@
     }
     owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-    owl_global_set_needrefresh(&g);
   } else {
     owl_function_error("No message with id %d: unable to mark for (un)delete",id);
@@ -1700,5 +1673,4 @@
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
   owl_function_makemsg("%i messages marked for deletion", count);
-  owl_global_set_needrefresh(&g);
 }
 
@@ -1905,10 +1877,8 @@
 
   owl_editwin_set_locktext(tw, "command: ");
-  owl_global_set_needrefresh(&g);
 
   owl_editwin_insert_string(tw, line);
-  owl_editwin_redisplay(tw);
-
-  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline");
+
+  owl_global_push_context(&g, OWL_CTX_EDITLINE, tw, "editline", owl_global_get_typwin_window(&g));
   owl_editwin_set_callback(tw, owl_callback_command);
 }
@@ -1921,9 +1891,6 @@
 
   owl_editwin_set_locktext(tw, line);
-  owl_global_set_needrefresh(&g);
-
-  owl_editwin_redisplay(tw);
-
-  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
+
+  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
   return tw;
 }
@@ -1938,9 +1905,6 @@
 
   owl_editwin_set_locktext(tw, line);
-  owl_global_set_needrefresh(&g);
-
-  owl_editwin_redisplay(tw);
-
-  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse");
+
+  owl_global_push_context(&g, OWL_CTX_EDITRESPONSE, tw, "editresponse", owl_global_get_typwin_window(&g));
   return tw;
 }
@@ -2105,5 +2069,4 @@
     }
     owl_filter_set_fgcolor(f, owl_util_string_to_color(argv[3]));
-    owl_global_set_needrefresh(&g);
     owl_mainwin_redisplay(owl_global_get_mainwin(&g));
     return;
@@ -2120,5 +2083,4 @@
     }
     owl_filter_set_bgcolor(f, owl_util_string_to_color(argv[3]));
-    owl_global_set_needrefresh(&g);
     owl_mainwin_redisplay(owl_global_get_mainwin(&g));
     return;
@@ -2149,5 +2111,4 @@
     owl_function_change_currentview_filter(argv[1]);
   }
-  owl_global_set_needrefresh(&g);
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
 }
@@ -2693,5 +2654,4 @@
   owl_filter_set_fgcolor(f, owl_util_string_to_color(fgcolor));
   
-  owl_global_set_needrefresh(&g);
   owl_mainwin_redisplay(owl_global_get_mainwin(&g));
   return(0);
@@ -3407,16 +3367,12 @@
 {
   va_list ap;
-  char buff[2048];
-
-  if (!owl_global_get_curs_msgwin(&g)) return;
+  char *str;
 
   va_start(ap, fmt);
-  werase(owl_global_get_curs_msgwin(&g));
-  
-  vsnprintf(buff, 2048, fmt, ap);
-  owl_function_debugmsg("makemsg: %s", buff);
-  waddstr(owl_global_get_curs_msgwin(&g), buff);  
-  owl_global_set_needrefresh(&g);
+  str = g_strdup_vprintf(fmt, ap);
   va_end(ap);
+
+  owl_function_debugmsg("makemsg: %s", str);
+  owl_msgwin_set_text(&g.msgwin, str);
 }
 
Index: global.c
===================================================================
--- global.c	(revision efc460e15619cd45b33b4c8b4cb7297f515ff1db)
+++ global.c	(revision 8bba1aed9c99b38e86fab76c25d3b415be31afdb)
@@ -13,8 +13,12 @@
 #endif
 
+static void _owl_global_init_windows(owl_global *g);
+
 void owl_global_init(owl_global *g) {
   struct hostent *hent;
   char hostname[MAXHOSTNAMELEN];
   char *cd;
+
+  g_type_init();
 
   gethostname(hostname, MAXHOSTNAMELEN);
@@ -26,17 +30,19 @@
   }
 
+  g->lines=LINES;
+  g->cols=COLS;
+  /* We shouldn't need this if we initialize lines and cols before the first
+   * owl_window_get_screen, but to be safe, we synchronize. */
+  owl_window_resize(owl_window_get_screen(), g->lines, g->cols);
+
   g->context_stack = NULL;
-  owl_global_push_context(g, OWL_CTX_STARTUP, NULL, NULL);
+  owl_global_push_context(g, OWL_CTX_STARTUP, NULL, NULL, NULL);
 
   g->curmsg=0;
   g->topmsg=0;
   g->markedmsgid=-1;
-  g->needrefresh=1;
   g->startupargs=NULL;
 
   owl_variable_dict_setup(&(g->vars));
-
-  g->lines=LINES;
-  g->cols=COLS;
 
   g->rightshift=0;
@@ -54,5 +60,4 @@
   g->curmsg_vert_offset=0;
   g->resizepending=0;
-  g->relayoutpending = 0;
   g->direction=OWL_DIRECTION_DOWNWARDS;
   g->zaway=0;
@@ -76,6 +81,4 @@
   g->nextmsgid=0;
 
-  _owl_global_setup_windows(g);
-
   /* Fill in some variables which don't have constant defaults */
   /* TODO: come back later and check passwd file first */
@@ -89,6 +92,6 @@
 
   owl_messagelist_create(&(g->msglist));
-  owl_mainwin_init(&(g->mw));
-  owl_popwin_init(&(g->pw));
+
+  _owl_global_init_windows(g);
 
   g->aim_screenname=NULL;
@@ -115,4 +118,18 @@
   g->timerlist = NULL;
   g->interrupted = FALSE;
+}
+
+static void _owl_global_init_windows(owl_global *g)
+{
+  /* Create the main window */
+  owl_mainpanel_init(&(g->mainpanel));
+
+  /* Create the widgets */
+  owl_mainwin_init(&(g->mw), g->mainpanel.recwin);
+  owl_popwin_init(&(g->pw));
+  owl_msgwin_init(&(g->msgwin), g->mainpanel.msgwin);
+  owl_sepbar_init(g->mainpanel.sepwin);
+
+  owl_window_set_default_cursor(g->mainpanel.sepwin);
 
   /* set up a pad for input */
@@ -121,4 +138,9 @@
   keypad(g->input_pad, 1);
   meta(g->input_pad, 1);
+}
+
+void owl_global_sepbar_dirty(owl_global *g)
+{
+  owl_window_dirty(g->mainpanel.sepwin);
 }
 
@@ -153,34 +175,4 @@
 }
 
-void _owl_global_setup_windows(owl_global *g) {
-  int cols, typwin_lines;
-
-  cols=g->cols;
-  typwin_lines=owl_global_get_typwin_lines(g);
-
-  /* set the new window sizes */
-  g->recwinlines=g->lines-(typwin_lines+2);
-  if (g->recwinlines<0) {
-    /* gotta deal with this */
-    g->recwinlines=0;
-  }
-
-  /* create the new windows */
-  _owl_panel_set_window(&g->recpan, newwin(g->recwinlines, cols, 0, 0));
-  _owl_panel_set_window(&g->seppan, newwin(1, cols, g->recwinlines, 0));
-  _owl_panel_set_window(&g->msgpan, newwin(1, cols, g->recwinlines+1, 0));
-  _owl_panel_set_window(&g->typpan, newwin(typwin_lines, cols, g->recwinlines+2, 0));
-
-  if (g->tw)
-      owl_editwin_set_curswin(g->tw, owl_global_get_curs_typwin(g), typwin_lines, g->cols);
-
-  idlok(owl_global_get_curs_typwin(g), FALSE);
-  idlok(owl_global_get_curs_recwin(g), FALSE);
-  idlok(owl_global_get_curs_sepwin(g), FALSE);
-  idlok(owl_global_get_curs_msgwin(g), FALSE);
-
-  wmove(owl_global_get_curs_typwin(g), 0, 0);
-}
-
 owl_context *owl_global_get_context(owl_global *g) {
   if (!g->context_stack)
@@ -189,15 +181,17 @@
 }
 
-static void owl_global_lookup_keymap(owl_global *g) {
-  owl_context *c = owl_global_get_context(g);
-  if (!c || !c->keymap)
+static void owl_global_activate_context(owl_global *g, owl_context *c) {
+  if (!c)
     return;
 
-  if (!owl_keyhandler_activate(owl_global_get_keyhandler(g), c->keymap)) {
-    owl_function_error("Unable to activate keymap '%s'", c->keymap);
-  }
-}
-
-void owl_global_push_context(owl_global *g, int mode, void *data, const char *keymap) {
+  if (c->keymap) {
+    if (!owl_keyhandler_activate(owl_global_get_keyhandler(g), c->keymap)) {
+      owl_function_error("Unable to activate keymap '%s'", c->keymap);
+    }
+  }
+  owl_window_set_cursor(c->cursor);
+}
+
+void owl_global_push_context(owl_global *g, int mode, void *data, const char *keymap, owl_window *cursor) {
   owl_context *c;
   if (!(mode & OWL_CTX_MODE_BITS))
@@ -206,7 +200,8 @@
   c->mode = mode;
   c->data = data;
+  c->cursor = cursor ? g_object_ref(cursor) : NULL;
   c->keymap = owl_strdup(keymap);
   g->context_stack = g_list_prepend(g->context_stack, c);
-  owl_global_lookup_keymap(g);
+  owl_global_activate_context(g, owl_global_get_context(g));
 }
 
@@ -218,7 +213,9 @@
   g->context_stack = g_list_delete_link(g->context_stack,
                                         g->context_stack);
+  if (c->cursor)
+    g_object_unref(c->cursor);
   owl_free(c->keymap);
   owl_free(c);
-  owl_global_lookup_keymap(g);
+  owl_global_activate_context(g, owl_global_get_context(g));
 }
 
@@ -232,5 +229,5 @@
 
 int owl_global_get_recwin_lines(const owl_global *g) {
-  return(g->recwinlines);
+  return g->mainpanel.recwinlines;
 }
 
@@ -298,20 +295,8 @@
 }
 
-/* curses windows */
-
-WINDOW *owl_global_get_curs_recwin(const owl_global *g) {
-  return panel_window(g->recpan);
-}
-
-WINDOW *owl_global_get_curs_sepwin(const owl_global *g) {
-  return panel_window(g->seppan);
-}
-
-WINDOW *owl_global_get_curs_msgwin(const owl_global *g) {
-  return panel_window(g->msgpan);
-}
-
-WINDOW *owl_global_get_curs_typwin(const owl_global *g) {
-  return panel_window(g->typpan);
+/* underlying owl_windows */
+
+owl_window *owl_global_get_typwin_window(const owl_global *g) {
+  return g->mainpanel.typwin;
 }
 
@@ -322,19 +307,4 @@
 }
 
-/* refresh */
-
-int owl_global_is_needrefresh(const owl_global *g) {
-  if (g->needrefresh==1) return(1);
-  return(0);
-}
-
-void owl_global_set_needrefresh(owl_global *g) {
-  g->needrefresh=1;
-}
-
-void owl_global_set_noneedrefresh(owl_global *g) {
-  g->needrefresh=0;
-}
-
 /* variable dictionary */
 
@@ -352,5 +322,6 @@
 
 void owl_global_set_rightshift(owl_global *g, int i) {
-  g->rightshift=i;
+  g->rightshift = i;
+  owl_mainwin_redisplay(owl_global_get_mainwin(g));
 }
 
@@ -367,5 +338,10 @@
       owl_function_resize_typwin(owl_global_get_typwin_lines(g) + d);
 
-  g->tw = owl_editwin_new(owl_global_get_curs_typwin(g),
+  if (g->typwin_erase_id) {
+    g_signal_handler_disconnect(owl_global_get_typwin_window(g), g->typwin_erase_id);
+    g->typwin_erase_id = 0;
+  }
+
+  g->tw = owl_editwin_new(owl_global_get_typwin_window(g),
                           owl_global_get_typwin_lines(g),
                           g->cols,
@@ -380,5 +356,10 @@
       owl_function_resize_typwin(owl_global_get_typwin_lines(g) - d);
 
-  werase(owl_global_get_curs_typwin(g));
+  if (!g->typwin_erase_id) {
+    g->typwin_erase_id =
+      g_signal_connect(owl_global_get_typwin_window(g), "redraw", G_CALLBACK(owl_window_erase_cb), NULL);
+  }
+  owl_window_dirty(owl_global_get_typwin_window(g));
+
   g->tw = NULL;
 }
@@ -388,8 +369,4 @@
 void owl_global_set_resize_pending(owl_global *g) {
   g->resizepending=1;
-}
-
-void owl_global_set_relayout_pending(owl_global *g) {
-  g->relayoutpending = 1;
 }
 
@@ -471,5 +448,5 @@
  * fail.
  */
-static void _owl_global_get_size(int *lines, int *cols) {
+void owl_global_get_terminal_size(int *lines, int *cols) {
   struct winsize size;
   /* get the new size */
@@ -488,70 +465,13 @@
 }
 
-void owl_global_resize(owl_global *g, int x, int y) {
-  /* resize the screen.  If x or y is 0 use the terminal size */
+void owl_global_check_resize(owl_global *g) {
+  /* resize the screen.  If lines or cols is 0 use the terminal size */
   if (!g->resizepending) return;
   g->resizepending = 0;
 
-  _owl_global_get_size(&g->lines, &g->cols);
-  if (x != 0) {
-    g->lines = x;
-  }
-  if (y != 0) {
-    g->cols = y;
-  }
-
-  resizeterm(g->lines, g->cols);
+  owl_global_get_terminal_size(&g->lines, &g->cols);
+  owl_window_resize(owl_window_get_screen(), g->lines, g->cols);
 
   owl_function_debugmsg("New size is %i lines, %i cols.", g->lines, g->cols);
-  owl_global_set_relayout_pending(g);
-}
-
-void owl_global_relayout(owl_global *g) {
-  owl_popwin *pw;
-  owl_viewwin *vw;
-
-  if (!g->relayoutpending) return;
-  g->relayoutpending = 0;
-
-  owl_function_debugmsg("Relayouting...");
-
-  /* re-initialize the windows */
-  _owl_global_setup_windows(g);
-
-  /* in case any styles rely on the current width */
-  owl_messagelist_invalidate_formats(owl_global_get_msglist(g));
-
-  /* recalculate the topmsg to make sure the current message is on
-   * screen */
-  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
-
-  /* recreate the popwin */
-  pw = owl_global_get_popwin(g);
-  if (owl_popwin_is_active(pw)) {
-    /*
-     * This is somewhat hacky; we probably want a proper windowing layer. We
-     * destroy the popwin and recreate it. Then the viewwin is redirected to
-     * the new window.
-     */
-    vw = owl_global_get_viewwin(g);
-    owl_popwin_close(pw);
-    owl_popwin_up(pw);
-    owl_viewwin_set_curswin(vw, owl_popwin_get_curswin(pw),
-	owl_popwin_get_lines(pw), owl_popwin_get_cols(pw));
-    owl_viewwin_redisplay(vw);
-  }
-
-  /* refresh stuff */
-  g->needrefresh=1;
-  owl_mainwin_redisplay(&(g->mw));
-  sepbar(NULL);
-  if (g->tw)
-      owl_editwin_redisplay(g->tw);
-  else
-    werase(owl_global_get_curs_typwin(g));
-
-  owl_function_full_redisplay();
-
-  owl_function_makemsg("");
 }
 
@@ -623,5 +543,5 @@
 
 void owl_global_set_curmsg_vert_offset(owl_global *g, int i) {
-  g->curmsg_vert_offset=i;
+  g->curmsg_vert_offset = i;
 }
 
Index: keys.c
===================================================================
--- keys.c	(revision 44cc9abbc2c15abaf4cd5fe0d1fc62b0e0936913)
+++ keys.c	(revision 5cc7e5e45702da552c2ba2d3eb236eaeca359eb4)
@@ -338,15 +338,7 @@
     owl_editwin_post_process_char(e, j);
   }
-  owl_global_set_needrefresh(&g);
 }
 
 void owl_keys_popless_postalways(owl_input j) {
-  owl_viewwin *v = owl_global_get_viewwin(&g);
-  const owl_popwin *pw = owl_global_get_popwin(&g);
-
-  if (pw && owl_popwin_is_active(pw) && v) {
-    owl_viewwin_redisplay(v);
-    owl_global_set_needrefresh(&g);
-  }  
 }
 
Index: mainpanel.c
===================================================================
--- mainpanel.c	(revision 4eee9482aeaa92aa1199b5d6ca4141afdf5f0791)
+++ mainpanel.c	(revision 4eee9482aeaa92aa1199b5d6ca4141afdf5f0791)
@@ -0,0 +1,57 @@
+#include "owl.h"
+
+void owl_mainpanel_init(owl_mainpanel *mp)
+{
+  /* Create windows */
+  mp->panel = owl_window_new(NULL);
+  /* HACK for now: the sepwin must be drawn /after/ the recwin for
+   * lastdisplayed to work */
+  mp->sepwin = owl_window_new(mp->panel);
+  mp->recwin = owl_window_new(mp->panel);
+  mp->msgwin = owl_window_new(mp->panel);
+  mp->typwin = owl_window_new(mp->panel);
+
+  /* Set up sizing hooks */
+  owl_signal_connect_object(owl_window_get_screen(), "resized", G_CALLBACK(owl_window_fill_parent_cb), mp->panel, 0);
+  g_signal_connect_swapped(mp->panel, "resized", G_CALLBACK(owl_mainpanel_layout_contents), mp);
+
+  /* Bootstrap the sizes and go */
+  owl_window_fill_parent_cb(owl_window_get_screen(), mp->panel);
+  owl_window_show_all(mp->panel);
+}
+
+void owl_mainpanel_layout_contents(owl_mainpanel *mp)
+{
+  int lines, cols, typwin_lines;
+
+  /* skip if we haven't been initialized */
+  if (!mp->panel) return;
+
+  owl_window_get_position(mp->panel, &lines, &cols, NULL, NULL);
+  typwin_lines = owl_global_get_typwin_lines(&g);
+
+  /* set the new window sizes */
+  mp->recwinlines=lines-(typwin_lines+2);
+  if (mp->recwinlines<0) {
+    /* gotta deal with this */
+    mp->recwinlines=0;
+    typwin_lines = lines - 2;
+  }
+
+  /* resize all the windows */
+  owl_window_set_position(mp->recwin, mp->recwinlines, cols, 0, 0);
+  owl_window_set_position(mp->sepwin, 1, cols, mp->recwinlines, 0);
+  owl_window_set_position(mp->msgwin, 1, cols, mp->recwinlines+1, 0);
+  owl_window_set_position(mp->typwin, typwin_lines, cols, mp->recwinlines+2, 0);
+}
+
+void owl_mainpanel_cleanup(owl_mainpanel *mp)
+{
+  owl_window_unlink(mp->panel);
+  g_object_unref(mp->panel);
+  g_object_unref(mp->recwin);
+  g_object_unref(mp->sepwin);
+  g_object_unref(mp->msgwin);
+  g_object_unref(mp->typwin);
+  mp->panel = mp->recwin = mp->sepwin = mp->msgwin = mp->typwin = NULL;
+}
Index: mainwin.c
===================================================================
--- mainwin.c	(revision f449096479541df1434d1d796890d0bd2939f072)
+++ mainwin.c	(revision 5cc7e5e45702da552c2ba2d3eb236eaeca359eb4)
@@ -1,11 +1,42 @@
 #include "owl.h"
 
-void owl_mainwin_init(owl_mainwin *mw)
+static void owl_mainwin_redraw(owl_window *w, WINDOW *recwin, void *user_data);
+static void owl_mainwin_resized(owl_window *w, void *user_data);
+
+void owl_mainwin_init(owl_mainwin *mw, owl_window *window)
 {
   mw->curtruncated=0;
   mw->lastdisplayed=-1;
+  mw->window = g_object_ref(window);
+  /* for now, just assume this object lasts forever */
+  g_signal_connect(window, "redraw", G_CALLBACK(owl_mainwin_redraw), mw);
+  g_signal_connect(window, "resized", G_CALLBACK(owl_mainwin_resized), mw);
+  owl_window_dirty(window);
+
+  /* For now, we do not bother with connecting up dependencies; that'll be a
+   * future refactor of the mainwin */
+}
+
+static void owl_mainwin_resized(owl_window *w, void *user_data)
+{
+  owl_mainwin *mw = user_data;
+
+  /* in case any styles rely on the current width */
+  owl_messagelist_invalidate_formats(owl_global_get_msglist(&g));
+
+  /* recalculate the topmsg to make sure the current message is on
+   * screen */
+  owl_function_calculate_topmsg(OWL_DIRECTION_NONE);
+
+  /* Schedule a redraw */
+  owl_window_dirty(mw->window);
 }
 
 void owl_mainwin_redisplay(owl_mainwin *mw)
+{
+  owl_window_dirty(mw->window);
+}
+
+static void owl_mainwin_redraw(owl_window *w, WINDOW *recwin, void *user_data)
 {
   owl_message *m;
@@ -13,10 +44,9 @@
   int x, y, savey, recwinlines, start;
   int topmsg, curmsg, markedmsgid, fgcolor, bgcolor;
-  WINDOW *recwin;
   const owl_view *v;
   GList *fl;
   const owl_filter *f;
+  owl_mainwin *mw = user_data;
 
-  recwin = owl_global_get_curs_recwin(&g);
   topmsg = owl_global_get_topmsg(&g);
   curmsg = owl_global_get_curmsg(&g);
@@ -43,5 +73,4 @@
     mw->curtruncated=0;
     mw->lastdisplayed=-1;
-    owl_global_set_needrefresh(&g);
     return;
   }
@@ -88,5 +117,5 @@
     if (y+lines > recwinlines-1) {
       isfull=1;
-      owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g),
+      owl_message_curs_waddstr(m, recwin,
 			       start,
 			       start+recwinlines-y,
@@ -96,5 +125,5 @@
     } else {
       /* otherwise print the whole thing */
-      owl_message_curs_waddstr(m, owl_global_get_curs_recwin(&g),
+      owl_message_curs_waddstr(m, recwin,
 			       start,
 			       start+lines,
@@ -139,6 +168,4 @@
   }
   mw->lastdisplayed=i-1;
-
-  owl_global_set_needrefresh(&g);
 }
 
Index: msgwin.c
===================================================================
--- msgwin.c	(revision 23b6c118279eabdaa1e3d8d2d600a97bce616017)
+++ msgwin.c	(revision 23b6c118279eabdaa1e3d8d2d600a97bce616017)
@@ -0,0 +1,46 @@
+#include "owl.h"
+
+static void owl_msgwin_redraw(owl_window *w, WINDOW *curswin, void *msgwin_);
+
+/* Maintains a small status window of text */
+
+void owl_msgwin_init(owl_msgwin *msgwin, owl_window *window)
+{
+  msgwin->msg = NULL;
+  msgwin->window = g_object_ref(window);
+  msgwin->redraw_id = g_signal_connect(window, "redraw", G_CALLBACK(owl_msgwin_redraw), msgwin);
+  owl_window_dirty(window);
+}
+
+static void owl_msgwin_redraw(owl_window *w, WINDOW *curswin, void *msgwin_)
+{
+  owl_msgwin *msgwin = msgwin_;
+
+  werase(curswin);
+  if (msgwin->msg)
+    waddstr(curswin, msgwin->msg);  
+}
+
+void owl_msgwin_set_text(owl_msgwin *msgwin, const char *msg)
+{
+  owl_msgwin_set_text_nocopy(msgwin, owl_strdup(msg));
+}
+
+void owl_msgwin_set_text_nocopy(owl_msgwin *msgwin, char *msg)
+{
+  owl_free(msgwin->msg);
+  msgwin->msg = msg;
+  owl_window_dirty(msgwin->window);
+}
+
+void owl_msgwin_cleanup(owl_msgwin *msgwin)
+{
+  owl_free(msgwin->msg);
+  msgwin->msg = NULL;
+  if (msgwin->window) {
+    g_signal_handler_disconnect(msgwin->window, msgwin->redraw_id);
+    g_object_unref(msgwin->window);
+    msgwin->window = NULL;
+    msgwin->redraw_id = 0;
+  }
+}
Index: owl.c
===================================================================
--- owl.c	(revision 04b16f8ed734b58f776e5aa382d59f78696a3771)
+++ owl.c	(revision 0be3efd03a33482174f56f64de8d3412b9ecb783)
@@ -240,4 +240,6 @@
   /* log the message if we need to */
   owl_log_message(m);
+  /* redraw the sepbar; TODO: don't violate layering */
+  owl_global_sepbar_dirty(&g);
 
   return 1;
@@ -272,6 +274,4 @@
     /* this should be optimized to not run if the new messages won't be displayed */
     owl_mainwin_redisplay(owl_global_get_mainwin(&g));
-    sepbar(NULL);
-    owl_global_set_needrefresh(&g);
   }
   return newmsgs;
@@ -428,26 +428,7 @@
 {
   /* if a resize has been scheduled, deal with it */
-  owl_global_resize(&g, 0, 0);
-  /* also handle relayouts */
-  owl_global_relayout(&g);
-
+  owl_global_check_resize(&g);
   /* update the terminal if we need to */
-  if (owl_global_is_needrefresh(&g)) {
-    /* these are here in case a relayout changes the windows */
-    WINDOW *sepwin = owl_global_get_curs_sepwin(&g);
-    WINDOW *typwin = owl_global_get_curs_typwin(&g);
-
-    /* push all changed windows to screen */
-    update_panels();
-    /* leave the cursor in the appropriate window */
-    if (!owl_popwin_is_active(owl_global_get_popwin(&g))
-	&& owl_global_get_typwin(&g)) {
-      owl_function_set_cursor(typwin);
-    } else {
-      owl_function_set_cursor(sepwin);
-    }
-    doupdate();
-    owl_global_set_noneedrefresh(&g);
-  }
+  owl_window_redraw_scheduled();
   return 0;
 }
@@ -519,5 +500,5 @@
 
   owl_global_pop_context(&g);
-  owl_global_push_context(&g, OWL_CTX_READCONFIG, NULL, NULL);
+  owl_global_push_context(&g, OWL_CTX_READCONFIG, NULL, NULL, NULL);
 
   perlerr=owl_perlconfig_initperl(opts.configfile, &argc, &argv, &env);
@@ -563,5 +544,4 @@
     "-----------------------------------------------------------------m-m---\n"
   );
-  sepbar(NULL);
 
   /* process the startup file */
@@ -587,5 +567,5 @@
 
   owl_global_pop_context(&g);
-  owl_global_push_context(&g, OWL_CTX_READCONFIG|OWL_CTX_RECV, NULL, "recv");
+  owl_global_push_context(&g, OWL_CTX_READCONFIG|OWL_CTX_RECV, NULL, "recv", NULL);
 
   owl_select_add_pre_select_action(owl_refresh_pre_select_action, NULL, NULL);
Index: owl.h
===================================================================
--- owl.h	(revision 987cf3f48057443b84ecf140854129cf1a6cbbdc)
+++ owl.h	(revision 987cf3f48057443b84ecf140854129cf1a6cbbdc)
@@ -60,4 +60,6 @@
 typedef void HV;
 #endif
+
+#include "window.h"
 
 #ifdef  GIT_VERSION
@@ -308,4 +310,5 @@
   void *data;		/* determined by mode */
   char *keymap;
+  owl_window *cursor;
 } owl_context;
 
@@ -395,4 +398,5 @@
   int lasttruncated;
   int lastdisplayed;
+  owl_window *window;
 } owl_mainwin;
 
@@ -402,6 +406,6 @@
   int topline;
   int rightshift;
-  int winlines, wincols;
-  WINDOW *curswin;
+  owl_window *window;
+  gulong sig_redraw_id;
   void (*onclose_hook) (struct _owl_viewwin *vwin, void *data);
   void *onclose_hook_data;
@@ -409,10 +413,14 @@
   
 typedef struct _owl_popwin {
-  PANEL *borderpanel;
-  PANEL *poppanel;
-  int lines;
-  int cols;
+  owl_window *border;
+  owl_window *content;
   int active;
 } owl_popwin;
+  
+typedef struct _owl_msgwin {
+  char *msg;
+  owl_window *window;
+  gulong redraw_id;
+} owl_msgwin;
 
 typedef struct _owl_messagelist {
@@ -464,4 +472,13 @@
 typedef struct _owl_editwin_excursion owl_editwin_excursion;
 
+typedef struct _owl_mainpanel {
+  owl_window *panel;
+  owl_window *typwin;
+  owl_window *sepwin;
+  owl_window *msgwin;
+  owl_window *recwin;
+  int recwinlines;
+} owl_mainpanel;
+
 typedef struct _owl_keybinding {
   int  *keys;			/* keypress stack */
@@ -547,7 +564,10 @@
 } owl_popexec;
 
+typedef struct _OwlGlobalNotifier OwlGlobalNotifier;
+
 typedef struct _owl_global {
   owl_mainwin mw;
   owl_popwin pw;
+  owl_msgwin msgwin;
   owl_history cmdhist;		/* command history */
   owl_history msghist;		/* outgoing message history */
@@ -567,10 +587,8 @@
   owl_messagelist msglist;
   WINDOW *input_pad;
-  PANEL *recpan, *seppan, *msgpan, *typpan;
-  int needrefresh;
+  owl_mainpanel mainpanel;
+  gulong typwin_erase_id;
   int rightshift;
   volatile sig_atomic_t resizepending;
-  int relayoutpending;
-  int recwinlines;
   char *thishost;
   char *homedir;
Index: popexec.c
===================================================================
--- popexec.c	(revision 125fd21c2d564a4a7d0a027552b7156a4d694243)
+++ popexec.c	(revision 07b59ea301a55bf3db1724aa9571e7048fe89857)
@@ -27,10 +27,6 @@
 
   owl_popwin_up(pw);
-  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless");
-  owl_viewwin_init_text(v, owl_popwin_get_curswin(pw),
-			owl_popwin_get_lines(pw), owl_popwin_get_cols(pw),
-			"");
-  owl_viewwin_redisplay(v);
-  owl_global_set_needrefresh(&g);
+  owl_global_push_context(&g, OWL_CTX_POPLESS, v, "popless", NULL);
+  owl_viewwin_init_text(v, owl_popwin_get_content(pw), "");
   owl_viewwin_set_onclose_hook(v, owl_popexec_viewwin_onclose, pe);
   pe->refcount++;
@@ -111,6 +107,4 @@
     if (pe->winactive) { 
       owl_viewwin_append_text(pe->vwin, "\n");
-      owl_viewwin_redisplay(pe->vwin);
-      owl_global_set_needrefresh(&g);
     }
     owl_select_remove_io_dispatch(d);
@@ -139,6 +133,4 @@
   if (pe->winactive) {
     owl_viewwin_append_text(pe->vwin, buf);
-    owl_viewwin_redisplay(pe->vwin);
-    owl_global_set_needrefresh(&g);
   }
   owl_free(buf);
Index: popwin.c
===================================================================
--- popwin.c	(revision 0881cdda52baa4677179208b39a3392f06182cf5)
+++ popwin.c	(revision 05ca0d8685143d49b9e1c7d2949a55f6976c27ff)
@@ -4,6 +4,4 @@
 {
   pw->active=0;
-  pw->lines=0;
-  pw->cols=0;
   return(0);
 }
@@ -11,24 +9,47 @@
 int owl_popwin_up(owl_popwin *pw)
 {
-  int glines, gcols, startcol, startline;
-  WINDOW *popwin, *borderwin;
+  pw->border = owl_window_new(NULL);
+  pw->content = owl_window_new(pw->border);
+  g_signal_connect(pw->border, "redraw", G_CALLBACK(owl_popwin_draw_border), 0);
+  owl_signal_connect_object(owl_window_get_screen(), "resized", G_CALLBACK(owl_popwin_size_border), pw->border, 0);
+  owl_signal_connect_object(pw->border, "resized", G_CALLBACK(owl_popwin_size_content), pw->content, 0);
 
-  /* calculate the size of the popwin */
-  glines=owl_global_get_lines(&g);
-  gcols=owl_global_get_cols(&g);
+  /* bootstrap sizing */
+  owl_popwin_size_border(owl_window_get_screen(), pw->border);
 
-  pw->lines = owl_util_min(glines,24)*3/4 + owl_util_max(glines-24,0)/2;
-  startline = (glines-pw->lines)/2;
+  owl_window_show_all(pw->border);
 
-  pw->cols = owl_util_min(gcols,90)*15/16 + owl_util_max(gcols-90,0)/2;
-  startcol = (gcols-pw->cols)/2;
+  pw->active=1;
+  return(0);
+}
 
-  borderwin = newwin(pw->lines, pw->cols, startline, startcol);
-  pw->borderpanel = new_panel(borderwin);
-  popwin = newwin(pw->lines-2, pw->cols-2, startline+1, startcol+1);
-  pw->poppanel = new_panel(popwin);
+void owl_popwin_size_border(owl_window *parent, void *user_data)
+{
+  int lines, cols, startline, startcol;
+  int glines, gcols;
+  owl_window *border = user_data;
 
-  werase(popwin);
-  werase(borderwin);
+  owl_window_get_position(parent, &glines, &gcols, 0, 0);
+
+  lines = owl_util_min(glines,24)*3/4 + owl_util_max(glines-24,0)/2;
+  startline = (glines-lines)/2;
+  cols = owl_util_min(gcols,90)*15/16 + owl_util_max(gcols-90,0)/2;
+  startcol = (gcols-cols)/2;
+
+  owl_window_set_position(border, lines, cols, startline, startcol);
+}
+
+void owl_popwin_size_content(owl_window *parent, void *user_data)
+{
+  int lines, cols;
+  owl_window *content = user_data;
+  owl_window_get_position(parent, &lines, &cols, 0, 0);
+  owl_window_set_position(content, lines-2, cols-2, 1, 1);
+}
+
+void owl_popwin_draw_border(owl_window *w, WINDOW *borderwin, void *user_data)
+{
+  int lines, cols;
+  owl_window_get_position(w, &lines, &cols, 0, 0);
   if (owl_global_is_fancylines(&g)) {
     box(borderwin, 0, 0);
@@ -37,31 +58,22 @@
     wmove(borderwin, 0, 0);
     waddch(borderwin, '+');
-    wmove(borderwin, pw->lines-1, 0);
+    wmove(borderwin, lines-1, 0);
     waddch(borderwin, '+');
-    wmove(borderwin, pw->lines-1, pw->cols-1);
+    wmove(borderwin, lines-1, cols-1);
     waddch(borderwin, '+');
-    wmove(borderwin, 0, pw->cols-1);
+    wmove(borderwin, 0, cols-1);
     waddch(borderwin, '+');
   }
-    
-  owl_global_set_needrefresh(&g);
-  pw->active=1;
-  return(0);
 }
 
 int owl_popwin_close(owl_popwin *pw)
 {
-  WINDOW *popwin, *borderwin;
+  owl_window_unlink(pw->border);
+  g_object_unref(pw->border);
+  g_object_unref(pw->content);
 
-  popwin = panel_window(pw->poppanel);
-  borderwin = panel_window(pw->borderpanel);
-
-  del_panel(pw->poppanel);
-  del_panel(pw->borderpanel);
-  delwin(popwin);
-  delwin(borderwin);
-
+  pw->border = 0;
+  pw->content = 0;
   pw->active=0;
-  owl_global_set_needrefresh(&g);
   return(0);
 }
@@ -72,16 +84,6 @@
 }
 
-WINDOW *owl_popwin_get_curswin(const owl_popwin *pw)
+owl_window *owl_popwin_get_content(const owl_popwin *pw)
 {
-  return panel_window(pw->poppanel);
+  return pw->content;
 }
-
-int owl_popwin_get_lines(const owl_popwin *pw)
-{
-  return(pw->lines-2);
-}
-
-int owl_popwin_get_cols(const owl_popwin *pw)
-{
-  return(pw->cols-2);
-}
Index: sepbar.c
===================================================================
--- sepbar.c	(revision 044f19f7c86e3f63b353e7fff2865f139319a45a)
+++ sepbar.c	(revision 044f19f7c86e3f63b353e7fff2865f139319a45a)
@@ -0,0 +1,116 @@
+#include "owl.h"
+
+static void sepbar_redraw(owl_window *w, WINDOW *sepwin, void *user_data);
+
+void owl_sepbar_init(owl_window *w)
+{
+  g_signal_connect(w, "redraw", G_CALLBACK(sepbar_redraw), NULL);
+  /* TODO: handle dirtiness in the sepbar */
+  owl_window_dirty(w);
+}
+
+static void sepbar_redraw(owl_window *w, WINDOW *sepwin, void *user_data)
+{
+  const owl_messagelist *ml;
+  const owl_view *v;
+  int x, y, i;
+  const char *foo, *appendtosepbar;
+
+  ml=owl_global_get_msglist(&g);
+  v=owl_global_get_current_view(&g);
+
+  werase(sepwin);
+  wattron(sepwin, A_REVERSE);
+  if (owl_global_is_fancylines(&g)) {
+    whline(sepwin, ACS_HLINE, owl_global_get_cols(&g));
+  } else {
+    whline(sepwin, '-', owl_global_get_cols(&g));
+  }
+
+  if (owl_global_is_sepbar_disable(&g)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, owl_global_get_cols(&g)-1);
+    return;
+  }
+
+  wmove(sepwin, 0, 2);
+
+  if (owl_messagelist_get_size(ml) == 0)
+    waddstr(sepwin, " (-/-) ");
+  else
+    wprintw(sepwin, " (%i/%i/%i) ", owl_global_get_curmsg(&g) + 1,
+            owl_view_get_size(v),
+            owl_messagelist_get_size(ml));
+
+  foo=owl_view_get_filtname(v);
+  if (strcmp(foo, owl_global_get_view_home(&g)))
+      wattroff(sepwin, A_REVERSE);
+  wprintw(sepwin, " %s ", owl_view_get_filtname(v));
+  if (strcmp(foo, owl_global_get_view_home(&g)))
+      wattron(sepwin, A_REVERSE);
+
+  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    waddstr(sepwin, " <truncated> ");
+    wattroff(sepwin, A_BOLD);
+  }
+
+  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
+  if ((i != -1) &&
+      (i < owl_view_get_size(v)-1)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    waddstr(sepwin, " <more> ");
+    wattroff(sepwin, A_BOLD);
+  }
+
+  if (owl_global_get_rightshift(&g)>0) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wprintw(sepwin, " right: %i ", owl_global_get_rightshift(&g));
+  }
+
+  if (owl_global_is_zaway(&g) || owl_global_is_aaway(&g)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    wattroff(sepwin, A_REVERSE);
+    if (owl_global_is_zaway(&g) && owl_global_is_aaway(&g)) {
+      waddstr(sepwin, " AWAY ");
+    } else if (owl_global_is_zaway(&g)) {
+      waddstr(sepwin, " Z-AWAY ");
+    } else if (owl_global_is_aaway(&g)) {
+      waddstr(sepwin, " A-AWAY ");
+    }
+    wattron(sepwin, A_REVERSE);
+    wattroff(sepwin, A_BOLD);
+  }
+
+  if (owl_global_get_curmsg_vert_offset(&g)) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    wattron(sepwin, A_BOLD);
+    wattroff(sepwin, A_REVERSE);
+    waddstr(sepwin, " SCROLL ");
+    wattron(sepwin, A_REVERSE);
+    wattroff(sepwin, A_BOLD);
+  }
+
+  appendtosepbar = owl_global_get_appendtosepbar(&g);
+  if (appendtosepbar && *appendtosepbar) {
+    getyx(sepwin, y, x);
+    wmove(sepwin, y, x+2);
+    waddstr(sepwin, " ");
+    waddstr(sepwin, owl_global_get_appendtosepbar(&g));
+    waddstr(sepwin, " ");
+  }
+
+  getyx(sepwin, y, x);
+  wmove(sepwin, y, owl_global_get_cols(&g)-1);
+    
+  wattroff(sepwin, A_BOLD);
+  wattroff(sepwin, A_REVERSE);
+}
Index: stubgen.pl
===================================================================
--- stubgen.pl	(revision d7cc50b9bac64c7e71cb55c8686076c33f1f07dd)
+++ stubgen.pl	(revision ea7daa88a058dbbe1dfd9aa2f138f80d030b2f9c)
@@ -12,21 +12,37 @@
     my $altvarname = $2;
     $altvarname = $3 if ($3);
+    my $detailname = $altvarname;
+    $detailname =~ s/[^a-zA-Z0-9]/-/g;
+    $detailname =~ s/^[^a-zA-Z]+//;
     if ($vartype =~ /^BOOL/) {
-	print "void owl_global_set_${altvarname}_on(owl_global *g) {\n";
-	print "  owl_variable_set_bool_on(&g->vars, \"$varname\");\n}\n";
-	print "void owl_global_set_${altvarname}_off(owl_global *g) {\n";
-	print "  owl_variable_set_bool_off(&g->vars, \"$varname\");\n}\n";
-	print "int owl_global_is_$altvarname(const owl_global *g) {\n";
-	print "  return owl_variable_get_bool(&g->vars, \"$varname\");\n}\n";
+        print <<EOT;
+void owl_global_set_${altvarname}_on(owl_global *g) {
+  owl_variable_set_bool_on(&g->vars, "$altvarname");
+}
+void owl_global_set_${altvarname}_off(owl_global *g) {
+  owl_variable_set_bool_off(&g->vars, "$altvarname");
+}
+int owl_global_is_$altvarname(const owl_global *g) {
+  return owl_variable_get_bool(&g->vars, "$varname");
+}
+EOT
     } elsif ($vartype =~ /^PATH/ or $vartype =~ /^STRING/) {
-	print "void owl_global_set_$altvarname(owl_global *g, const char *text) {\n";
-	print "  owl_variable_set_string(&g->vars, \"$varname\", text);\n}\n";
-	print "const char *owl_global_get_$altvarname(const owl_global *g) {\n";
-	print "  return owl_variable_get_string(&g->vars, \"$varname\");\n}\n";
+        print <<EOT;
+void owl_global_set_${altvarname}(owl_global *g, const char *text) {
+  owl_variable_set_string(&g->vars, "$altvarname", text);
+}
+const char *owl_global_get_$altvarname(const owl_global *g) {
+  return owl_variable_get_string(&g->vars, "$varname");
+}
+EOT
     } elsif ($vartype =~ /^INT/ or $vartype =~ /^ENUM/) {
-	print "void owl_global_set_$altvarname(owl_global *g, int n) {\n";
-	print "  owl_variable_set_int(&g->vars, \"$varname\", n);\n}\n";
-	print "int owl_global_get_$altvarname(const owl_global *g) {\n";
-	print "  return owl_variable_get_int(&g->vars, \"$varname\");\n}\n";
+        print <<EOT;
+void owl_global_set_${altvarname}(owl_global *g, int n) {
+  owl_variable_set_int(&g->vars, "$altvarname", n);
+}
+int owl_global_get_$altvarname(const owl_global *g) {
+  return owl_variable_get_int(&g->vars, "$varname");
+}
+EOT
     } 
     }
Index: util.c
===================================================================
--- util.c	(revision 9a7b4f23cba7fc71c4e918b5cb8d5ca6789924e3)
+++ util.c	(revision c1f1e1e3f61fcf6b7a60ce2c7a2656f31c6277c4)
@@ -8,117 +8,5 @@
 #include <sys/types.h>
 
-void sepbar(const char *in)
-{
-  WINDOW *sepwin;
-  const owl_messagelist *ml;
-  const owl_view *v;
-  int x, y, i;
-  const char *foo, *appendtosepbar;
-
-  sepwin=owl_global_get_curs_sepwin(&g);
-  ml=owl_global_get_msglist(&g);
-  v=owl_global_get_current_view(&g);
-
-  werase(sepwin);
-  wattron(sepwin, A_REVERSE);
-  if (owl_global_is_fancylines(&g)) {
-    whline(sepwin, ACS_HLINE, owl_global_get_cols(&g));
-  } else {
-    whline(sepwin, '-', owl_global_get_cols(&g));
-  }
-
-  if (owl_global_is_sepbar_disable(&g)) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, owl_global_get_cols(&g)-1);
-    return;
-  }
-
-  wmove(sepwin, 0, 2);
-
-  if (owl_messagelist_get_size(ml) == 0)
-    waddstr(sepwin, " (-/-) ");
-  else
-    wprintw(sepwin, " (%i/%i/%i) ", owl_global_get_curmsg(&g) + 1,
-            owl_view_get_size(v),
-            owl_messagelist_get_size(ml));
-
-  foo=owl_view_get_filtname(v);
-  if (strcmp(foo, owl_global_get_view_home(&g)))
-      wattroff(sepwin, A_REVERSE);
-  wprintw(sepwin, " %s ", owl_view_get_filtname(v));
-  if (strcmp(foo, owl_global_get_view_home(&g)))
-      wattron(sepwin, A_REVERSE);
-
-  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wattron(sepwin, A_BOLD);
-    waddstr(sepwin, " <truncated> ");
-    wattroff(sepwin, A_BOLD);
-  }
-
-  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
-  if ((i != -1) &&
-      (i < owl_view_get_size(v)-1)) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wattron(sepwin, A_BOLD);
-    waddstr(sepwin, " <more> ");
-    wattroff(sepwin, A_BOLD);
-  }
-
-  if (owl_global_get_rightshift(&g)>0) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wprintw(sepwin, " right: %i ", owl_global_get_rightshift(&g));
-  }
-
-  if (owl_global_is_zaway(&g) || owl_global_is_aaway(&g)) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wattron(sepwin, A_BOLD);
-    wattroff(sepwin, A_REVERSE);
-    if (owl_global_is_zaway(&g) && owl_global_is_aaway(&g)) {
-      waddstr(sepwin, " AWAY ");
-    } else if (owl_global_is_zaway(&g)) {
-      waddstr(sepwin, " Z-AWAY ");
-    } else if (owl_global_is_aaway(&g)) {
-      waddstr(sepwin, " A-AWAY ");
-    }
-    wattron(sepwin, A_REVERSE);
-    wattroff(sepwin, A_BOLD);
-  }
-
-  if (owl_global_get_curmsg_vert_offset(&g)) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    wattron(sepwin, A_BOLD);
-    wattroff(sepwin, A_REVERSE);
-    waddstr(sepwin, " SCROLL ");
-    wattron(sepwin, A_REVERSE);
-    wattroff(sepwin, A_BOLD);
-  }
-  
-  if (in) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    waddstr(sepwin, in);
-  }
-
-  appendtosepbar = owl_global_get_appendtosepbar(&g);
-  if (appendtosepbar && *appendtosepbar) {
-    getyx(sepwin, y, x);
-    wmove(sepwin, y, x+2);
-    waddstr(sepwin, " ");
-    waddstr(sepwin, owl_global_get_appendtosepbar(&g));
-    waddstr(sepwin, " ");
-  }
-
-  getyx(sepwin, y, x);
-  wmove(sepwin, y, owl_global_get_cols(&g)-1);
-    
-  wattroff(sepwin, A_BOLD);
-  wattroff(sepwin, A_REVERSE);
-}
+#include <glib-object.h>
 
 char **atokenize(const char *buffer, const char *sep, int *i)
@@ -786,2 +674,77 @@
   return buf;
 }
+
+gulong owl_dirty_window_on_signal(owl_window *w, gpointer sender, const gchar *detailed_signal)
+{
+  return owl_signal_connect_object(sender, detailed_signal, G_CALLBACK(owl_window_dirty), w, G_CONNECT_SWAPPED);
+}
+
+typedef struct { /*noproto*/
+  GObject  *sender;
+  gulong    signal_id;
+} SignalData;
+
+static void _closure_invalidated(gpointer data, GClosure *closure);
+
+/*
+ * GObject's g_signal_connect_object has a documented bug. This function is
+ * identical except it does not leak the signal handler.
+ */
+gulong owl_signal_connect_object(gpointer sender, const gchar *detailed_signal, GCallback c_handler, gpointer receiver, GConnectFlags connect_flags)
+{
+  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (sender), 0);
+  g_return_val_if_fail (detailed_signal != NULL, 0);
+  g_return_val_if_fail (c_handler != NULL, 0);
+
+  if (receiver) {
+    SignalData *sdata;
+    GClosure *closure;
+    gulong signal_id;
+
+    g_return_val_if_fail (G_IS_OBJECT (receiver), 0);
+
+    closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, receiver);
+    signal_id = g_signal_connect_closure (sender, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
+
+    /* Register the missing hooks */
+    sdata = g_slice_new0(SignalData);
+    sdata->sender = sender;
+    sdata->signal_id = signal_id;
+
+    g_closure_add_invalidate_notifier(closure, sdata, _closure_invalidated);
+
+    return signal_id;
+  } else {
+    return g_signal_connect_data(sender, detailed_signal, c_handler, NULL, NULL, connect_flags);
+  }
+}
+
+/*
+ * There are three ways the signal could come to an end:
+ * 
+ * 1. The user explicitly disconnects it with the returned signal_id.
+ *    - In that case, the disconnection unref's the closure, causing it
+ *      to first be invalidated. The handler's already disconnected, so
+ *      we have no work to do.
+ * 2. The sender gets destroyed.
+ *    - GObject will disconnect each signal which then goes into the above
+ *      case. Our handler does no work.
+ * 3. The receiver gets destroyed.
+ *    - The GClosure was created by g_cclosure_new_object_{,swap} which gets
+ *      invalidated when the receiver is destroyed. We then follow through case 1
+ *      again, but *this* time, the handler has not been disconnected. We then
+ *      clean up ourselves.
+ *
+ * We can't actually hook into this process earlier with weakrefs as GObject
+ * will, on object dispose, first disconnect signals, then invalidate closures,
+ * and notify weakrefs last.
+ */
+static void _closure_invalidated(gpointer data, GClosure *closure)
+{
+  SignalData *sdata = data;
+  if (g_signal_handler_is_connected(sdata->sender, sdata->signal_id)) {
+    g_signal_handler_disconnect(sdata->sender, sdata->signal_id);
+  }
+  g_slice_free(SignalData, sdata);
+}
+
Index: variable.c
===================================================================
--- variable.c	(revision d544237f77a88ded81cbe50b88544589a3e918f1)
+++ variable.c	(revision f6fae8d526ff0525c526bc767e88c386fde4f41b)
@@ -400,5 +400,5 @@
   int rv;
   rv = owl_variable_int_set_default(v, newval);
-  if (0 == rv) owl_global_set_relayout_pending(&g);
+  if (0 == rv) owl_mainpanel_layout_contents(&g.mainpanel);
   return(rv);
 }
Index: viewwin.c
===================================================================
--- viewwin.c	(revision fe4c786091ae4f138f1edc26a13b2b8d0b3a034d)
+++ viewwin.c	(revision 0b9e6070c08d1acf0e1258b9938f43062c01e491)
@@ -4,8 +4,10 @@
 #define BOTTOM_OFFSET 1
 
+static void owl_viewwin_redraw(owl_window *w, WINDOW *curswin, void *user_data);
+
 /* initialize the viewwin e.  'win' is an already initialzed curses
  * window that will be used by viewwin
  */
-void owl_viewwin_init_text(owl_viewwin *v, WINDOW *win, int winlines, int wincols, const char *text)
+void owl_viewwin_init_text(owl_viewwin *v, owl_window *win, const char *text)
 {
   owl_fmtext_init_null(&(v->fmtext));
@@ -19,8 +21,7 @@
   v->topline=0;
   v->rightshift=0;
-  v->winlines=winlines;
-  v->wincols=wincols;
-  v->curswin=win;
   v->onclose_hook = NULL;
+
+  owl_viewwin_set_window(v, win);
 }
 
@@ -28,4 +29,5 @@
     owl_fmtext_append_normal(&(v->fmtext), text);
     v->textlines=owl_fmtext_num_lines(&(v->fmtext));  
+    owl_window_dirty(v->window);
 }
 
@@ -33,5 +35,5 @@
  * window that will be used by viewwin
  */
-void owl_viewwin_init_fmtext(owl_viewwin *v, WINDOW *win, int winlines, int wincols, const owl_fmtext *fmtext)
+void owl_viewwin_init_fmtext(owl_viewwin *v, owl_window *win, const owl_fmtext *fmtext)
 {
   char *text;
@@ -46,14 +48,19 @@
   v->topline=0;
   v->rightshift=0;
-  v->winlines=winlines;
-  v->wincols=wincols;
-  v->curswin=win;
+
+  owl_viewwin_set_window(v, win);
 }
 
-void owl_viewwin_set_curswin(owl_viewwin *v, WINDOW *w, int winlines, int wincols)
+void owl_viewwin_set_window(owl_viewwin *v, owl_window *w)
 {
-  v->curswin=w;
-  v->winlines=winlines;
-  v->wincols=wincols;
+  if (v->window) {
+    g_signal_handler_disconnect(v->window, v->sig_redraw_id);
+    g_object_unref(v->window);
+  }
+  v->window = w;
+  if (w) {
+    g_object_ref(v->window);
+    v->sig_redraw_id = g_signal_connect(w, "redraw", G_CALLBACK(owl_viewwin_redraw), v);
+  }
 }
 
@@ -64,32 +71,32 @@
 
 /* regenerate text on the curses window. */
-void owl_viewwin_redisplay(owl_viewwin *v)
+static void owl_viewwin_redraw(owl_window *w, WINDOW *curswin, void *user_data)
 {
   owl_fmtext fm1, fm2;
+  owl_viewwin *v = user_data;
+  int winlines, wincols;
 
-  /* avoid segfault when screen too small to create curswin */
-  if (v->curswin == NULL)
-    return;
+  owl_window_get_position(w, &winlines, &wincols, 0, 0);
   
-  werase(v->curswin);
-  wmove(v->curswin, 0, 0);
+  werase(curswin);
+  wmove(curswin, 0, 0);
 
   owl_fmtext_init_null(&fm1);
   owl_fmtext_init_null(&fm2);
   
-  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, v->winlines-BOTTOM_OFFSET, &fm1);
-  owl_fmtext_truncate_cols(&fm1, v->rightshift, v->wincols-1+v->rightshift, &fm2);
+  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, winlines-BOTTOM_OFFSET, &fm1);
+  owl_fmtext_truncate_cols(&fm1, v->rightshift, wincols-1+v->rightshift, &fm2);
 
-  owl_fmtext_curs_waddstr_without_search(&fm2, v->curswin);
+  owl_fmtext_curs_waddstr_without_search(&fm2, curswin);
 
   /* print the message at the bottom */
-  wmove(v->curswin, v->winlines-1, 0);
-  wattrset(v->curswin, A_REVERSE);
-  if (v->textlines - v->topline > v->winlines-BOTTOM_OFFSET) {
-    waddstr(v->curswin, "--More-- (Space to see more, 'q' to quit)");
+  wmove(curswin, winlines-1, 0);
+  wattrset(curswin, A_REVERSE);
+  if (v->textlines - v->topline > winlines-BOTTOM_OFFSET) {
+    waddstr(curswin, "--More-- (Space to see more, 'q' to quit)");
   } else {
-    waddstr(v->curswin, "--End-- (Press 'q' to quit)");
+    waddstr(curswin, "--End-- (Press 'q' to quit)");
   }
-  wattroff(v->curswin, A_REVERSE);
+  wattroff(curswin, A_REVERSE);
 
   owl_fmtext_cleanup(&fm1);
@@ -99,22 +106,31 @@
 void owl_viewwin_pagedown(owl_viewwin *v)
 {
-  v->topline+=v->winlines - BOTTOM_OFFSET;
-  if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) {
-    v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+  int winlines;
+  owl_window_get_position(v->window, &winlines, 0, 0, 0);
+  v->topline+=winlines - BOTTOM_OFFSET;
+  if ( (v->topline+winlines-BOTTOM_OFFSET) > v->textlines) {
+    v->topline = v->textlines - winlines + BOTTOM_OFFSET;
   }
+  owl_window_dirty(v->window);
 }
 
 void owl_viewwin_linedown(owl_viewwin *v)
 {
+  int winlines;
+  owl_window_get_position(v->window, &winlines, 0, 0, 0);
   v->topline++;
-  if ( (v->topline+v->winlines-BOTTOM_OFFSET) > v->textlines) {
-    v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+  if ( (v->topline+winlines-BOTTOM_OFFSET) > v->textlines) {
+    v->topline = v->textlines - winlines + BOTTOM_OFFSET;
   }
+  owl_window_dirty(v->window);
 }
 
 void owl_viewwin_pageup(owl_viewwin *v)
 {
-  v->topline-=v->winlines;
+  int winlines;
+  owl_window_get_position(v->window, &winlines, 0, 0, 0);
+  v->topline-=winlines;
   if (v->topline<0) v->topline=0;
+  owl_window_dirty(v->window);
 }
 
@@ -123,4 +139,5 @@
   v->topline--;
   if (v->topline<0) v->topline=0;
+  owl_window_dirty(v->window);
 }
 
@@ -128,4 +145,5 @@
 {
   v->rightshift+=n;
+  owl_window_dirty(v->window);
 }
 
@@ -134,4 +152,5 @@
   v->rightshift-=n;
   if (v->rightshift<0) v->rightshift=0;
+  owl_window_dirty(v->window);
 }
 
@@ -140,13 +159,18 @@
   v->topline=0;
   v->rightshift=0;
+  owl_window_dirty(v->window);
 }
 
 void owl_viewwin_bottom(owl_viewwin *v)
 {
-  v->topline = v->textlines - v->winlines + BOTTOM_OFFSET;
+  int winlines;
+  owl_window_get_position(v->window, &winlines, 0, 0, 0);
+  v->topline = v->textlines - winlines + BOTTOM_OFFSET;
+  owl_window_dirty(v->window);
 }
 
 void owl_viewwin_cleanup(owl_viewwin *v)
 {
+  owl_viewwin_set_window(v, NULL);
   if (v->onclose_hook) {
     v->onclose_hook(v, v->onclose_hook_data);
Index: window.c
===================================================================
--- window.c	(revision 5cc7e5e45702da552c2ba2d3eb236eaeca359eb4)
+++ window.c	(revision 5cc7e5e45702da552c2ba2d3eb236eaeca359eb4)
@@ -0,0 +1,496 @@
+#include "owl.h"
+
+#include <assert.h>
+
+struct _owl_window { /*noproto*/
+  GObject object;
+  /* hierarchy information */
+  owl_window *parent;
+  owl_window *child;
+  owl_window *next, *prev;
+  /* flags */
+  int dirty : 1;
+  int dirty_subtree : 1;
+  int shown : 1;
+  int is_screen : 1;
+  /* window information */
+  WINDOW *win;
+  PANEL *pan;
+  int nlines, ncols;
+  int begin_y, begin_x;
+};
+
+enum {
+  REDRAW,
+  RESIZED,
+  LAST_SIGNAL
+};
+
+static guint window_signals[LAST_SIGNAL] = { 0 };
+
+static void owl_window_dispose(GObject *gobject);
+static void owl_window_finalize(GObject *gobject);
+
+static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x);
+
+static void _owl_window_link(owl_window *w, owl_window *parent);
+
+static void _owl_window_create_curses(owl_window *w);
+static void _owl_window_destroy_curses(owl_window *w);
+
+static void _owl_window_realize(owl_window *w);
+static void _owl_window_unrealize(owl_window *w);
+
+static owl_window *cursor_owner;
+static owl_window *default_cursor;
+
+G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
+
+static void owl_window_class_init (OwlWindowClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  /* Set up the vtabl */
+  gobject_class->dispose = owl_window_dispose;
+  gobject_class->finalize = owl_window_finalize;
+
+  klass->redraw = NULL;
+  klass->resized = NULL;
+
+  /* Create the signals, remember IDs */
+  window_signals[REDRAW] =
+    g_signal_new("redraw",
+                 G_TYPE_FROM_CLASS(gobject_class),
+                 G_SIGNAL_RUN_CLEANUP,
+                 G_STRUCT_OFFSET(OwlWindowClass, redraw),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__POINTER,
+                 G_TYPE_NONE,
+                 1,
+                 G_TYPE_POINTER, NULL);
+
+  /* TODO: maybe type should be VOID__INT_INT_INT_INT; will need to generate a
+   * marshaller */
+  window_signals[RESIZED] =
+    g_signal_new("resized",
+                 G_TYPE_FROM_CLASS(gobject_class),
+                 G_SIGNAL_RUN_FIRST,
+                 G_STRUCT_OFFSET(OwlWindowClass, resized),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE,
+                 0,
+                 NULL);
+}
+
+static void owl_window_dispose (GObject *object)
+{
+  owl_window *w = OWL_WINDOW (object);
+
+  /* Unmap the window */
+  owl_window_hide (w);
+
+  /* Unlink and unref all children */
+  while (w->child) {
+    owl_window *child = w->child;
+    owl_window_unlink (child);
+  }
+
+  /* Remove from hierarchy */
+  owl_window_unlink (w);
+
+  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
+}
+
+static void owl_window_finalize (GObject *object)
+{
+  owl_window *w = OWL_WINDOW(object);
+
+  if (w->pan) {
+    del_panel(w->pan);
+    w->pan = NULL;
+  }
+
+  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
+}
+
+static void owl_window_init (owl_window *w)
+{
+}
+
+/** singletons **/
+
+static WINDOW *_dummy_window(void)
+{
+  static WINDOW *dummy = NULL;
+  if (!dummy) {
+    dummy = newwin(1, 1, 0, 0);
+  }
+  return dummy;
+}
+
+owl_window *owl_window_get_screen(void)
+{
+  static owl_window *screen = NULL;
+  if (!screen) {
+    /* The screen is magical. It's 'shown', but the only mean of it going
+     * invisible is if we're tore down curses (i.e. screen resize) */
+    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
+    screen->is_screen = 1;
+    owl_window_show(screen);
+  }
+  return screen;
+}
+
+/** Creation and Destruction **/
+
+owl_window *owl_window_new(owl_window *parent)
+{
+  if (!parent)
+    parent = owl_window_get_screen();
+  return _owl_window_new(parent, 0, 0, 0, 0);
+}
+
+static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
+{
+  owl_window *w;
+
+  w = g_object_new (OWL_TYPE_WINDOW, NULL);
+  if (w == NULL) g_error("Failed to create owl_window instance");
+
+  w->nlines = nlines;
+  w->ncols = ncols;
+  w->begin_y = begin_y;
+  w->begin_x = begin_x;
+
+  _owl_window_link(w, parent);
+  if (parent && parent->is_screen) {
+    w->pan = new_panel(_dummy_window());
+    set_panel_userptr(w->pan, w);
+  }
+
+  return w;
+}
+
+/** Hierarchy **/
+
+void owl_window_unlink(owl_window *w)
+{
+  /* make sure the window is unmapped first */
+  _owl_window_unrealize(w);
+  /* unlink parent/child information */
+  if (w->parent) {
+    if (w->prev)
+      w->prev->next = w->next;
+    if (w->next)
+      w->next->prev = w->prev;
+    if (w->parent->child == w)
+      w->parent->child = w->next;
+    w->parent = NULL;
+    g_object_unref (w);
+  }
+}
+
+static void _owl_window_link(owl_window *w, owl_window *parent)
+{
+  if (w->parent == parent)
+    return;
+
+  owl_window_unlink(w);
+  if (parent) {
+    w->parent = parent;
+    w->next = parent->child;
+    parent->child = w;
+    g_object_ref (w);
+  }
+}
+
+/* mimic g_list_foreach for consistency */
+void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
+{
+  owl_window *w;
+  for (w = parent->child;
+       w != NULL;
+       w = w->next) {
+    func(w, user_data);
+  }
+}
+
+owl_window *owl_window_parent(owl_window *w)
+{
+  return w->parent;
+}
+
+owl_window *owl_window_first_child(owl_window *w)
+{
+  return w->child;
+}
+
+owl_window *owl_window_next_sibling(owl_window *w)
+{
+  return w->next;
+}
+
+owl_window *owl_window_previous_sibling(owl_window *w)
+{
+  return w->prev;
+}
+
+/** Internal window management **/
+
+static void _owl_window_create_curses(owl_window *w)
+{
+  if (w->is_screen) {
+    resizeterm(w->nlines, w->ncols);
+    w->win = stdscr;
+  } else {
+    /* Explicitly disallow realizing an unlinked non-root */
+    if (w->parent == NULL || w->parent->win == NULL)
+      return;
+    if (w->pan) {
+      w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
+      replace_panel(w->pan, w->win);
+    } else {
+      w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
+    }
+  }
+}
+
+static void _owl_window_destroy_curses(owl_window *w)
+{
+  if (w->is_screen) {
+    /* don't deallocate the dummy */
+    w->win = NULL;
+  } else {
+    if (w->pan) {
+      /* panels assume their windows always exist, so we put in a fake one */
+      replace_panel(w->pan, _dummy_window());
+    }
+    if (w->win) {
+      /* and destroy our own window */
+      delwin(w->win);
+      w->win = NULL;
+    }
+  }
+}
+
+void owl_window_show(owl_window *w)
+{
+  w->shown = 1;
+  _owl_window_realize(w);
+  if (w->pan)
+    show_panel(w->pan);
+}
+
+void owl_window_show_all(owl_window *w)
+{
+  owl_window_show(w);
+  owl_window_children_foreach(w, (GFunc)owl_window_show, 0);
+}
+
+void owl_window_hide(owl_window *w)
+{
+  /* you can't unmap the screen */
+  if (w->is_screen)
+    return;
+  w->shown = 0;
+  if (w->pan)
+    hide_panel(w->pan);
+  _owl_window_unrealize(w);
+}
+
+int owl_window_is_shown(owl_window *w)
+{
+  return w->shown;
+}
+
+int owl_window_is_realized(owl_window *w)
+{
+  return w->win != NULL;
+}
+
+int owl_window_is_toplevel(owl_window *w)
+{
+  return w->pan != NULL;
+}
+
+int owl_window_is_subwin(owl_window *w)
+{
+  return w->pan == NULL && !w->is_screen;
+}
+
+static void _owl_window_realize(owl_window *w)
+{
+  /* check if we can create a window */
+  if ((w->parent && w->parent->win == NULL)
+      || !w->shown
+      || w->win != NULL)
+    return;
+  if (w->nlines <= 0 || w->ncols <= 0)
+    return;
+  _owl_window_create_curses(w);
+  if (w->win == NULL)
+    return;
+  /* schedule a repaint */
+  owl_window_dirty(w);
+  /* map the children */
+  owl_window_children_foreach(w, (GFunc)_owl_window_realize, 0);
+}
+
+static void _owl_window_unrealize(owl_window *w)
+{
+  if (w->win == NULL)
+    return;
+  /* unmap all the children */
+  owl_window_children_foreach(w, (GFunc)_owl_window_unrealize, 0);
+  _owl_window_destroy_curses(w);
+  w->dirty = w->dirty_subtree = 0;
+  /* subwins leave a mess in the parent; dirty it */
+  if (w->parent)
+    owl_window_dirty(w->parent);
+}
+
+/** Painting and book-keeping **/
+
+void owl_window_set_cursor(owl_window *w)
+{
+  if (cursor_owner)
+    g_object_remove_weak_pointer(G_OBJECT(cursor_owner), (gpointer*) &cursor_owner);
+  cursor_owner = w;
+  if (w)
+    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
+  owl_window_dirty(owl_window_get_screen());
+}
+
+void owl_window_set_default_cursor(owl_window *w)
+{
+  if (default_cursor)
+    g_object_remove_weak_pointer(G_OBJECT(default_cursor), (gpointer*) &default_cursor);
+  default_cursor = w;
+  if (w)
+    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &default_cursor);
+  owl_window_dirty(owl_window_get_screen());
+}
+
+static owl_window *_get_cursor(void)
+{
+  if (cursor_owner && owl_window_is_realized(cursor_owner))
+    return cursor_owner;
+  if (default_cursor && owl_window_is_realized(default_cursor))
+    return default_cursor;
+  return owl_window_get_screen();
+}
+
+void owl_window_dirty(owl_window *w)
+{
+  if (!owl_window_is_realized(w))
+    return;
+  if (!w->dirty) {
+    w->dirty = 1;
+    while (w && !w->dirty_subtree) {
+      w->dirty_subtree = 1;
+      w = w->parent;
+    }
+  }
+}
+
+void owl_window_dirty_children(owl_window *w)
+{
+  owl_window_children_foreach(w, (GFunc)owl_window_dirty, 0);
+}
+
+static void _owl_window_redraw(owl_window *w)
+{
+  if (!w->dirty) return;
+  if (w->win && !w->is_screen) {
+    if (owl_window_is_subwin(w)) {
+      /* If a subwin, we might have gotten random touched lines from wsyncup or
+       * past drawing. That information is useless, so we discard it all */
+      untouchwin(w->win);
+    }
+    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
+    wsyncup(w->win);
+  }
+  w->dirty = 0;
+}
+
+static void _owl_window_redraw_subtree(owl_window *w)
+{
+  if (!w->dirty_subtree)
+    return;
+  _owl_window_redraw(w);
+  owl_window_children_foreach(w, (GFunc)_owl_window_redraw_subtree, 0);
+  w->dirty_subtree = 0;
+}
+
+/*
+Redraw all the windows with scheduled redraws.
+NOTE: This function shouldn't be called outside the event loop
+*/
+void owl_window_redraw_scheduled(void)
+{
+  owl_window *cursor;
+  owl_window *screen = owl_window_get_screen();
+
+  if (!screen->dirty_subtree)
+    return;
+  _owl_window_redraw_subtree(screen);
+  update_panels();
+  cursor = _get_cursor();
+  if (cursor && cursor->win) {
+    /* untouch it to avoid drawing; window should be clean now, but we must
+     * clean up in case there was junk left over on a subwin (cleaning up after
+     * subwin drawing isn't sufficient because a wsyncup messes up subwin
+     * ancestors */
+    untouchwin(cursor->win);
+    wnoutrefresh(cursor->win);
+  }
+  doupdate();
+}
+
+/** Window position **/
+
+void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
+{
+  if (nlines)  *nlines  = w->nlines;
+  if (ncols)   *ncols   = w->ncols;
+  if (begin_y) *begin_y = w->begin_y;
+  if (begin_x) *begin_x = w->begin_x;
+}
+
+void owl_window_move(owl_window *w, int begin_y, int begin_x)
+{
+  /* It is possible to move a window efficiently with move_panel and mvderwin,
+   * but begy and begx are then wrong. Currently, this only effects the
+   * wnoutrefresh to move cursor. TODO: fix that and reinstate that
+   * optimization if it's worth the trouble */
+  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
+}
+
+void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
+{
+  int resized;
+
+  if (w->nlines == nlines && w->ncols == ncols
+      && w->begin_y == begin_y && w->begin_x == begin_x) {
+    return;
+  }
+  resized = w->nlines != nlines || w->ncols != ncols;
+
+  _owl_window_unrealize(w);
+  w->begin_y = begin_y;
+  w->begin_x = begin_x;
+  w->nlines = nlines;
+  w->ncols = ncols;
+  if (resized)
+    g_signal_emit(w, window_signals[RESIZED], 0);
+  if (w->shown) {
+    /* ncurses is screwy: give up and recreate windows at the right place */
+    _owl_window_realize(w);
+  }
+}
+
+void owl_window_resize(owl_window *w, int nlines, int ncols)
+{
+  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
+}
Index: window.h
===================================================================
--- window.h	(revision 9379760d70b9135d8e94813d2edd59dfa9c7516c)
+++ window.h	(revision 9379760d70b9135d8e94813d2edd59dfa9c7516c)
@@ -0,0 +1,69 @@
+#ifndef __BARNOWL_WINDOW_H__
+#define __BARNOWL_WINDOW_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define OWL_TYPE_WINDOW                  (owl_window_get_type ())
+#define OWL_WINDOW(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OWL_TYPE_WINDOW, OwlWindow))
+#define OWL_IS_WINDOW(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OWL_TYPE_WINDOW))
+#define OWL_WINDOW_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), OWL_TYPE_WINDOW, OwlWindowClass))
+#define OWL_IS_WINDOW_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), OWL_TYPE_WINDOW))
+#define OWL_WINDOW_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), OWL_TYPE_WINDOW, OwlWindowClass))
+
+typedef struct _owl_window OwlWindow;
+typedef struct _OwlWindowClass OwlWindowClass;
+typedef OwlWindow owl_window; /* meh */
+
+struct _OwlWindowClass
+{
+  GObjectClass parent_class;
+  /* default implementations for signals */
+  void (*redraw)(owl_window *, WINDOW *);
+  void (*resized)(owl_window *);
+};
+
+GType owl_window_get_type(void);
+
+owl_window *owl_window_get_screen(void);
+
+owl_window *owl_window_new(owl_window *parent);
+void owl_window_unlink(owl_window *w);
+
+void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data);
+
+owl_window *owl_window_parent(owl_window *parent);
+owl_window *owl_window_first_child(owl_window *parent);
+owl_window *owl_window_next_sibling(owl_window *w);
+owl_window *owl_window_previous_sibling(owl_window *w);
+
+void owl_window_show(owl_window *w);
+void owl_window_show_all(owl_window *w);
+void owl_window_hide(owl_window *w);
+int owl_window_is_shown(owl_window *w);
+int owl_window_is_realized(owl_window *w);
+int owl_window_is_toplevel(owl_window *w);
+int owl_window_is_subwin(owl_window *w);
+
+void owl_window_set_cursor(owl_window *w);
+void owl_window_set_default_cursor(owl_window *w);
+
+void owl_window_dirty(owl_window *w);
+void owl_window_dirty_children(owl_window *w);
+void owl_window_redraw_scheduled(void);
+
+void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x);
+void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x);
+void owl_window_move(owl_window *w, int begin_y, int begin_x);
+void owl_window_resize(owl_window *w, int nlines, int ncols);
+
+/* Standard callback functions in windowcb.c */
+
+void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data);
+void owl_window_fill_parent_cb(owl_window *parent, void *user_data);
+
+G_END_DECLS
+
+#endif /* __BARNOWL_WINDOW_H__ */
Index: windowcb.c
===================================================================
--- windowcb.c	(revision 853f397531e75fe1ed03d6f52bcfdd77d837a815)
+++ windowcb.c	(revision 853f397531e75fe1ed03d6f52bcfdd77d837a815)
@@ -0,0 +1,17 @@
+#include "owl.h"
+
+void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
+{
+  werase(win);
+  owl_window_dirty_children(w);
+}
+
+void owl_window_fill_parent_cb(owl_window *parent, void *user_data)
+{
+  int lines, cols;
+  owl_window *window = user_data;
+
+  /* Make this panel full-screen */
+  owl_window_get_position(parent, &lines, &cols, NULL, NULL);
+  owl_window_set_position(window, lines, cols, 0, 0);
+}
