source: window.c @ 09065ed

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