source: window.c @ a57f87a

release-1.7release-1.8release-1.9
Last change on this file since a57f87a was a57f87a, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
Move the update_panels into owl_window_redraw_scheduled
  • Property mode set to 100644
File size: 11.3 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
[7a6e6c7]44static void _owl_window_redraw_cleanup(owl_window *w, WINDOW *win);
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
56  klass->redraw = _owl_window_redraw_cleanup;
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
329void owl_window_dirty(owl_window *w)
330{
331  /* don't put the screen on this list; pointless */
332  if (w->is_screen)
333    return;
[7cbef8c]334  if (!owl_window_is_realized(w))
335    return;
[449af72]336  if (!w->dirty) {
337    w->dirty = 1;
338    while (w && !w->dirty_subtree) {
339      w->dirty_subtree = 1;
340      w = w->parent;
341    }
342    owl_global_set_needrefresh(&g);
343  }
344}
345
346void owl_window_dirty_children(owl_window *w)
347{
[7a70e26]348  owl_window_children_foreach(w, (GFunc)owl_window_dirty, 0);
[449af72]349}
350
351static void _owl_window_redraw(owl_window *w)
352{
353  if (!w->dirty) return;
[7a6e6c7]354  if (w->win) {
355    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
[449af72]356  }
357  w->dirty = 0;
358}
359
360static void _owl_window_redraw_subtree(owl_window *w)
361{
362  if (!w->dirty_subtree)
363    return;
364  _owl_window_redraw(w);
[7a70e26]365  owl_window_children_foreach(w, (GFunc)_owl_window_redraw_subtree, 0);
[449af72]366}
367
368/*
369Redraw all the windows with scheduled redraws.
370NOTE: This function shouldn't be called outside the event loop
371*/
372void owl_window_redraw_scheduled(void)
373{
374  _owl_window_redraw_subtree(owl_window_get_screen());
[a57f87a]375  update_panels();
[449af72]376}
377
[7a6e6c7]378static void _owl_window_redraw_cleanup(owl_window *w, WINDOW *win)
379{
380  wsyncup(win);
381}
382
[7c8811c]383void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
384{
385  werase(win);
386  owl_window_dirty_children(w);
387}
388
[449af72]389/** Window position **/
390
391void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
392{
393  if (nlines)  *nlines  = w->nlines;
394  if (ncols)   *ncols   = w->ncols;
395  if (begin_y) *begin_y = w->begin_y;
396  if (begin_x) *begin_x = w->begin_x;
397}
398
[b0cbde4]399void owl_window_move(owl_window *w, int begin_y, int begin_x)
400{
[6b93305]401  if (w->is_screen) return; /* can't move the screen */
402  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
[b0cbde4]403
404  w->begin_y = begin_y;
405  w->begin_x = begin_x;
[50031f0]406  if (w->shown) {
407    /* Window is shown, we must try to have a window at the end */
[b0cbde4]408    if (w->win) {
409      /* We actually do have a window; let's move it */
410      if (w->pan) {
411        if (move_panel(w->pan, begin_y, begin_x) == OK)
412          return;
413      } else {
414        if (mvderwin(w->win, begin_y, begin_x) == OK) {
415          /* now both we and the parent are dirty */
416          owl_window_dirty(w->parent);
417          owl_window_dirty(w);
418          return;
419        }
420      }
421    }
422    /* We don't have a window or failed to move it. Fine. Brute force. */
[46e2e56]423    _owl_window_unrealize(w);
424    _owl_window_realize(w);
[b0cbde4]425  }
426}
427
[449af72]428void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
429{
430  /* can't move the screen */
431  if (w->is_screen) {
432    begin_y = begin_x = 0;
433  }
434
435  if (w->nlines == nlines && w->ncols == ncols) {
436    /* moving is easier */
437    owl_window_move(w, begin_y, begin_x);
438    return;
439  }
440
441  w->begin_y = begin_y;
442  w->begin_x = begin_x;
443  w->nlines = nlines;
444  w->ncols = ncols;
[50031f0]445  /* window is shown, we must try to have a window at the end */
446  if (w->shown) {
[46e2e56]447    /* resizing in ncurses is hard: give up do a unrealize/realize */
448    _owl_window_unrealize(w);
[449af72]449  }
[7a6e6c7]450  g_signal_emit(w, window_signals[RESIZED], 0);
[50031f0]451  if (w->shown) {
[46e2e56]452    _owl_window_realize(w);
[449af72]453  }
454}
455
456void owl_window_resize(owl_window *w, int nlines, int ncols)
457{
458  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
459}
460
461/** Stacking order **/
462
463void owl_window_top(owl_window *w) {
464  if (!w->pan) {
[7c8811c]465    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
[449af72]466    return;
467  }
468  top_panel(w->pan);
469}
470
471owl_window *owl_window_above(owl_window *w) {
472  PANEL *pan;
473
474  if (!w->pan) {
475    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
476    return NULL;
477  }
478  pan = panel_above(w->pan);
479  /* cast because panel_userptr pointlessly returns a const void * */
480  return pan ? (void*) panel_userptr(pan) : NULL;
481}
482
483owl_window *owl_window_below(owl_window *w) {
484  PANEL *pan;
485
486  if (!w->pan) {
487    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
488    return NULL;
489  }
490  pan = panel_below(w->pan);
491  /* cast because panel_userptr pointlessly returns a const void * */
492  return pan ? (void*) panel_userptr(pan) : NULL;
493}
Note: See TracBrowser for help on using the repository browser.