source: window.c @ f91767d

release-1.7release-1.8release-1.9
Last change on this file since f91767d was f91767d, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
untouchwin any subwin before interacting with it subwins maintain dirtiness information that is useless. It messes up the wnoutrefresh when setting the cursor. Also, make the wsyncup more direct. I think there is still a cursor bug with begy/begx and subwins and mvderwin, but that can be fixed later.
  • Property mode set to 100644
File size: 11.9 KB
RevLine 
[449af72]1#include "owl.h"
2
3#include <assert.h>
4
5struct _owl_window { /*noproto*/
[053f751]6  GObject object;
[449af72]7  /* hierarchy information */
8  owl_window *parent;
9  owl_window *child;
10  owl_window *next, *prev;
11  /* flags */
12  int dirty : 1;
13  int dirty_subtree : 1;
[50031f0]14  int shown : 1;
[449af72]15  int is_screen : 1;
16  /* window information */
17  WINDOW *win;
18  PANEL *pan;
19  int nlines, ncols;
20  int begin_y, begin_x;
21};
22
[7a6e6c7]23enum {
24  REDRAW,
25  RESIZED,
26  LAST_SIGNAL
27};
28
29static guint window_signals[LAST_SIGNAL] = { 0 };
30
[053f751]31static void owl_window_dispose(GObject *gobject);
32static void owl_window_finalize(GObject *gobject);
33
[449af72]34static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x);
35
36static void _owl_window_link(owl_window *w, owl_window *parent);
37
38static void _owl_window_create_curses(owl_window *w);
39static void _owl_window_destroy_curses(owl_window *w);
40
[46e2e56]41static void _owl_window_realize(owl_window *w);
42static void _owl_window_unrealize(owl_window *w);
[449af72]43
[84a4aca]44static owl_window *cursor_owner;
45
[053f751]46G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
47
48static void owl_window_class_init (OwlWindowClass *klass)
49{
[7a6e6c7]50  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
51
52  /* Set up the vtabl */
53  gobject_class->dispose = owl_window_dispose;
54  gobject_class->finalize = owl_window_finalize;
55
[f91767d]56  klass->redraw = NULL;
[7a6e6c7]57  klass->resized = NULL;
58
59  /* Create the signals, remember IDs */
60  window_signals[REDRAW] =
61    g_signal_new("redraw",
62                 G_TYPE_FROM_CLASS(gobject_class),
63                 G_SIGNAL_RUN_CLEANUP,
64                 G_STRUCT_OFFSET(OwlWindowClass, redraw),
65                 NULL, NULL,
66                 g_cclosure_marshal_VOID__POINTER,
67                 G_TYPE_NONE,
68                 1,
69                 G_TYPE_POINTER, NULL);
70
71  /* TODO: maybe type should be VOID__INT_INT_INT_INT; will need to generate a
72   * marshaller */
73  window_signals[RESIZED] =
74    g_signal_new("resized",
75                 G_TYPE_FROM_CLASS(gobject_class),
76                 G_SIGNAL_RUN_FIRST,
77                 G_STRUCT_OFFSET(OwlWindowClass, resized),
78                 NULL, NULL,
79                 g_cclosure_marshal_VOID__VOID,
80                 G_TYPE_NONE,
81                 0,
82                 NULL);
[053f751]83}
84
85static void owl_window_dispose (GObject *object)
86{
87  owl_window *w = OWL_WINDOW (object);
88
89  /* Unmap the window */
90  owl_window_hide (w);
91
92  /* Unlink and unref all children */
93  while (w->child) {
94    owl_window *child = w->child;
95    owl_window_unlink (child);
96  }
97
98  /* Remove from hierarchy */
99  owl_window_unlink (w);
100
101  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
102}
103
104static void owl_window_finalize (GObject *object)
105{
106  owl_window *w = OWL_WINDOW(object);
107
108  if (w->pan) {
109    del_panel(w->pan);
110    w->pan = NULL;
111  }
112
113  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
114}
115
116static void owl_window_init (owl_window *w)
117{
118}
119
[449af72]120/** singletons **/
121
122static WINDOW *_dummy_window(void)
123{
124  static WINDOW *dummy = NULL;
125  if (!dummy) {
126    dummy = newwin(1, 1, 0, 0);
127  }
128  return dummy;
129}
130
131owl_window *owl_window_get_screen(void)
132{
133  static owl_window *screen = NULL;
134  if (!screen) {
[50031f0]135    /* The screen is magical. It's 'shown', but the only mean of it going
[449af72]136     * invisible is if we're tore down curses (i.e. screen resize) */
137    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
138    screen->is_screen = 1;
[402ed3d3]139    owl_window_show(screen);
[449af72]140  }
141  return screen;
142}
143
144/** Creation and Destruction **/
145
[d106110]146owl_window *owl_window_new(owl_window *parent)
[449af72]147{
148  if (!parent)
149    parent = owl_window_get_screen();
[d106110]150  return _owl_window_new(parent, 0, 0, 0, 0);
[449af72]151}
152
153static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
154{
155  owl_window *w;
156
[053f751]157  w = g_object_new (OWL_TYPE_WINDOW, NULL);
158  if (w == NULL) g_error("Failed to create owl_window instance");
[449af72]159
160  w->nlines = nlines;
161  w->ncols = ncols;
162  w->begin_y = begin_y;
163  w->begin_x = begin_x;
164
165  _owl_window_link(w, parent);
166  if (parent && parent->is_screen) {
167    w->pan = new_panel(_dummy_window());
168    set_panel_userptr(w->pan, w);
169  }
170
171  return w;
172}
173
174/** Hierarchy **/
175
[3da1f4f]176void owl_window_unlink(owl_window *w)
[449af72]177{
[ec4ccfc]178  /* make sure the window is unmapped first */
[46e2e56]179  _owl_window_unrealize(w);
[449af72]180  /* unlink parent/child information */
181  if (w->parent) {
182    if (w->prev)
183      w->prev->next = w->next;
184    if (w->next)
185      w->next->prev = w->prev;
186    if (w->parent->child == w)
187      w->parent->child = w->next;
[933aa7d]188    w->parent = NULL;
[053f751]189    g_object_unref (w);
[449af72]190  }
191}
192
193static void _owl_window_link(owl_window *w, owl_window *parent)
194{
195  if (w->parent == parent)
196    return;
197
[933aa7d]198  owl_window_unlink(w);
[449af72]199  if (parent) {
[933aa7d]200    w->parent = parent;
[449af72]201    w->next = parent->child;
202    parent->child = w;
[053f751]203    g_object_ref (w);
[449af72]204  }
205}
206
207/* mimic g_list_foreach for consistency */
208void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
209{
210  owl_window *w;
211  for (w = parent->child;
212       w != NULL;
213       w = w->next) {
214    func(w, user_data);
215  }
216}
217
[b6cb985]218owl_window *owl_window_get_parent(owl_window *w)
219{
220  return w->parent;
221}
222
[449af72]223/** Internal window management **/
224
225static void _owl_window_create_curses(owl_window *w)
226{
227  if (w->is_screen) {
228    resizeterm(w->nlines, w->ncols);
[2dfccc7]229    w->win = stdscr;
[449af72]230  } else if (w->pan) {
231    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
232    replace_panel(w->pan, w->win);
233  } else {
[fe49685]234    if (w->parent == NULL || w->parent->win == NULL)
235      return;
[449af72]236    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
237  }
238}
239
240static void _owl_window_destroy_curses(owl_window *w)
241{
242  if (w->is_screen) {
243    /* don't deallocate the dummy */
244    w->win = NULL;
245  } else {
246    if (w->pan) {
247      /* panels assume their windows always exist, so we put in a fake one */
248      replace_panel(w->pan, _dummy_window());
249    }
250    if (w->win) {
251      /* and destroy our own window */
252      delwin(w->win);
253      w->win = NULL;
254    }
255  }
256}
257
[402ed3d3]258void owl_window_show(owl_window *w)
[449af72]259{
[50031f0]260  w->shown = 1;
[46e2e56]261  _owl_window_realize(w);
[449af72]262  if (w->pan)
263    show_panel(w->pan);
[402ed3d3]264}
265
266void owl_window_show_all(owl_window *w)
267{
268  owl_window_show(w);
[7a70e26]269  owl_window_children_foreach(w, (GFunc)owl_window_show, 0);
[449af72]270}
271
[50031f0]272void owl_window_hide(owl_window *w)
[449af72]273{
274  /* you can't unmap the screen */
275  if (w->is_screen)
276    return;
[50031f0]277  w->shown = 0;
[449af72]278  if (w->pan)
279    hide_panel(w->pan);
[46e2e56]280  _owl_window_unrealize(w);
[449af72]281}
282
[50031f0]283int owl_window_is_shown(owl_window *w)
[449af72]284{
[50031f0]285  return w->shown;
[449af72]286}
287
[ce7c6c3]288int owl_window_is_realized(owl_window *w)
[449af72]289{
290  return w->win != NULL;
291}
292
293int owl_window_is_toplevel(owl_window *w)
294{
295  return w->pan != NULL;
296}
297
[46e2e56]298static void _owl_window_realize(owl_window *w)
[449af72]299{
300  /* check if we can create a window */
301  if ((w->parent && w->parent->win == NULL)
[50031f0]302      || !w->shown
[449af72]303      || w->win != NULL)
304    return;
305  _owl_window_create_curses(w);
[fe49685]306  if (w->win == NULL)
307    return;
[449af72]308  /* schedule a repaint */
309  owl_window_dirty(w);
[fe49685]310  /* map the children */
[7a70e26]311  owl_window_children_foreach(w, (GFunc)_owl_window_realize, 0);
[449af72]312}
313
[46e2e56]314static void _owl_window_unrealize(owl_window *w)
[449af72]315{
316  if (w->win == NULL)
317    return;
318  /* unmap all the children */
[7a70e26]319  owl_window_children_foreach(w, (GFunc)_owl_window_unrealize, 0);
[449af72]320  _owl_window_destroy_curses(w);
[cb5a9f3]321  w->dirty = w->dirty_subtree = 0;
[449af72]322  /* subwins leave a mess in the parent; dirty it */
323  if (w->parent)
324    owl_window_dirty(w->parent);
325}
326
327/** Painting and book-keeping **/
328
[84a4aca]329void owl_window_set_cursor(owl_window *w)
330{
331  cursor_owner = w;
332  g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
[a5a9572]333  owl_global_set_needrefresh(&g);
[84a4aca]334}
335
[449af72]336void owl_window_dirty(owl_window *w)
337{
338  /* don't put the screen on this list; pointless */
339  if (w->is_screen)
340    return;
[7cbef8c]341  if (!owl_window_is_realized(w))
342    return;
[449af72]343  if (!w->dirty) {
344    w->dirty = 1;
345    while (w && !w->dirty_subtree) {
346      w->dirty_subtree = 1;
347      w = w->parent;
348    }
349    owl_global_set_needrefresh(&g);
350  }
351}
352
353void owl_window_dirty_children(owl_window *w)
354{
[7a70e26]355  owl_window_children_foreach(w, (GFunc)owl_window_dirty, 0);
[449af72]356}
357
358static void _owl_window_redraw(owl_window *w)
359{
360  if (!w->dirty) return;
[7a6e6c7]361  if (w->win) {
[f91767d]362    if (!owl_window_is_toplevel(w)) {
363      /* If a subwin, we might have gotten random touched lines from wsyncup or
364       * past drawing. That information is useless, so we discard it all */
365      untouchwin(w->win);
366    }
[7a6e6c7]367    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
[f91767d]368    wsyncup(w->win);
[449af72]369  }
370  w->dirty = 0;
371}
372
373static void _owl_window_redraw_subtree(owl_window *w)
374{
375  if (!w->dirty_subtree)
376    return;
377  _owl_window_redraw(w);
[7a70e26]378  owl_window_children_foreach(w, (GFunc)_owl_window_redraw_subtree, 0);
[449af72]379}
380
381/*
382Redraw all the windows with scheduled redraws.
383NOTE: This function shouldn't be called outside the event loop
384*/
385void owl_window_redraw_scheduled(void)
386{
387  _owl_window_redraw_subtree(owl_window_get_screen());
[a57f87a]388  update_panels();
[f91767d]389  if (cursor_owner && cursor_owner->win) {
390    /* untouch it to avoid drawing; window should be clean now, but we must
391     * clean up in case there was junk left over on a subwin (cleaning up after
392     * subwin drawing isn't sufficient because a wsyncup messes up subwin
393     * ancestors */
394    untouchwin(cursor_owner->win);
[84a4aca]395    wnoutrefresh(cursor_owner->win);
[f91767d]396  }
[7a6e6c7]397}
398
[7c8811c]399void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
400{
401  werase(win);
402  owl_window_dirty_children(w);
403}
404
[449af72]405/** Window position **/
406
407void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
408{
409  if (nlines)  *nlines  = w->nlines;
410  if (ncols)   *ncols   = w->ncols;
411  if (begin_y) *begin_y = w->begin_y;
412  if (begin_x) *begin_x = w->begin_x;
413}
414
[b0cbde4]415void owl_window_move(owl_window *w, int begin_y, int begin_x)
416{
[6b93305]417  if (w->is_screen) return; /* can't move the screen */
418  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
[b0cbde4]419
420  w->begin_y = begin_y;
421  w->begin_x = begin_x;
[50031f0]422  if (w->shown) {
423    /* Window is shown, we must try to have a window at the end */
[b0cbde4]424    if (w->win) {
425      /* We actually do have a window; let's move it */
426      if (w->pan) {
427        if (move_panel(w->pan, begin_y, begin_x) == OK)
428          return;
429      } else {
430        if (mvderwin(w->win, begin_y, begin_x) == OK) {
431          /* now both we and the parent are dirty */
432          owl_window_dirty(w->parent);
433          owl_window_dirty(w);
434          return;
435        }
436      }
437    }
438    /* We don't have a window or failed to move it. Fine. Brute force. */
[46e2e56]439    _owl_window_unrealize(w);
440    _owl_window_realize(w);
[b0cbde4]441  }
442}
443
[449af72]444void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
445{
446  /* can't move the screen */
447  if (w->is_screen) {
448    begin_y = begin_x = 0;
449  }
450
451  if (w->nlines == nlines && w->ncols == ncols) {
452    /* moving is easier */
453    owl_window_move(w, begin_y, begin_x);
454    return;
455  }
456
[50031f0]457  /* window is shown, we must try to have a window at the end */
458  if (w->shown) {
[46e2e56]459    /* resizing in ncurses is hard: give up do a unrealize/realize */
460    _owl_window_unrealize(w);
[449af72]461  }
[678a505]462  w->begin_y = begin_y;
463  w->begin_x = begin_x;
464  w->nlines = nlines;
465  w->ncols = ncols;
[7a6e6c7]466  g_signal_emit(w, window_signals[RESIZED], 0);
[50031f0]467  if (w->shown) {
[46e2e56]468    _owl_window_realize(w);
[449af72]469  }
470}
471
472void owl_window_resize(owl_window *w, int nlines, int ncols)
473{
474  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
475}
476
477/** Stacking order **/
478
479void owl_window_top(owl_window *w) {
480  if (!w->pan) {
[7c8811c]481    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
[449af72]482    return;
483  }
484  top_panel(w->pan);
485}
486
487owl_window *owl_window_above(owl_window *w) {
488  PANEL *pan;
489
490  if (!w->pan) {
491    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
492    return NULL;
493  }
494  pan = panel_above(w->pan);
495  /* cast because panel_userptr pointlessly returns a const void * */
496  return pan ? (void*) panel_userptr(pan) : NULL;
497}
498
499owl_window *owl_window_below(owl_window *w) {
500  PANEL *pan;
501
502  if (!w->pan) {
503    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
504    return NULL;
505  }
506  pan = panel_below(w->pan);
507  /* cast because panel_userptr pointlessly returns a const void * */
508  return pan ? (void*) panel_userptr(pan) : NULL;
509}
Note: See TracBrowser for help on using the repository browser.