source: window.c @ e00355d

release-1.7release-1.8release-1.9
Last change on this file since e00355d was e00355d, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
Drop the destroyed callback We probably don't need it with the new ownership semantics. If we do, gobject probably already has a mechanism.
  • Property mode set to 100644
File size: 12.0 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  /* hooks */
22  void (*redraw_cb)(owl_window *, WINDOW *, void *);
23  void  *redraw_cbdata;
24  void (*redraw_cbdata_destroy)(void *);
25
26  void (*size_cb)(owl_window *, void *);
27  void  *size_cbdata;
28  void (*size_cbdata_destroy)(void *);
29};
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
44G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
45
46static void owl_window_class_init (OwlWindowClass *klass)
47{
48  GObjectClass *object_class = G_OBJECT_CLASS (klass);
49
50  object_class->dispose = owl_window_dispose;
51  object_class->finalize = owl_window_finalize;
52}
53
54static void owl_window_dispose (GObject *object)
55{
56  owl_window *w = OWL_WINDOW (object);
57
58  /* Unmap the window */
59  owl_window_hide (w);
60
61  /* Unlink and unref all children */
62  while (w->child) {
63    owl_window *child = w->child;
64    owl_window_unlink (child);
65  }
66
67  /* Clear all cbs */
68  owl_window_set_redraw_cb (w, 0, 0, 0);
69  owl_window_set_size_cb (w, 0, 0, 0);
70
71  /* Remove from hierarchy */
72  owl_window_unlink (w);
73
74  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
75}
76
77static void owl_window_finalize (GObject *object)
78{
79  owl_window *w = OWL_WINDOW(object);
80
81  if (w->pan) {
82    del_panel(w->pan);
83    w->pan = NULL;
84  }
85
86  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
87}
88
89static void owl_window_init (owl_window *w)
90{
91}
92
93/** singletons **/
94
95static WINDOW *_dummy_window(void)
96{
97  static WINDOW *dummy = NULL;
98  if (!dummy) {
99    dummy = newwin(1, 1, 0, 0);
100  }
101  return dummy;
102}
103
104static void _screen_calculate_size(owl_window *screen, void *user_data)
105{
106  owl_global *g = user_data;
107  int lines, cols;
108  owl_global_get_terminal_size(&lines, &cols);
109  if (!g->lines) g->lines = lines;
110  if (!g->cols) g->cols = cols;
111  owl_window_resize(screen, g->lines, g->cols);
112}
113
114owl_window *owl_window_get_screen(void)
115{
116  static owl_window *screen = NULL;
117  if (!screen) {
118    /* The screen is magical. It's 'shown', but the only mean of it going
119     * invisible is if we're tore down curses (i.e. screen resize) */
120    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
121    screen->is_screen = 1;
122    owl_window_set_size_cb(screen, _screen_calculate_size, &g, 0);
123    owl_window_show(screen);
124  }
125  return screen;
126}
127
128/** Creation and Destruction **/
129
130owl_window *owl_window_new(owl_window *parent)
131{
132  if (!parent)
133    parent = owl_window_get_screen();
134  return _owl_window_new(parent, 0, 0, 0, 0);
135}
136
137static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
138{
139  owl_window *w;
140
141  w = g_object_new (OWL_TYPE_WINDOW, NULL);
142  if (w == NULL) g_error("Failed to create owl_window instance");
143
144  w->nlines = nlines;
145  w->ncols = ncols;
146  w->begin_y = begin_y;
147  w->begin_x = begin_x;
148
149  _owl_window_link(w, parent);
150  if (parent && parent->is_screen) {
151    w->pan = new_panel(_dummy_window());
152    set_panel_userptr(w->pan, w);
153  }
154
155  return w;
156}
157
158/** Callbacks **/
159
160void owl_window_set_redraw_cb(owl_window *w, void (*cb)(owl_window*, WINDOW*, void*), void *cbdata, void (*cbdata_destroy)(void*))
161{
162  if (w->redraw_cbdata_destroy) {
163    w->redraw_cbdata_destroy(w->redraw_cbdata);
164    w->redraw_cbdata = 0;
165    w->redraw_cbdata_destroy = 0;
166  }
167
168  w->redraw_cb = cb;
169  w->redraw_cbdata = cbdata;
170  w->redraw_cbdata_destroy = cbdata_destroy;
171
172  /* mark the window as dirty, to take new cb in account */
173  owl_window_dirty(w);
174}
175
176void owl_window_set_size_cb(owl_window *w, void (*cb)(owl_window*, void*), void *cbdata, void (*cbdata_destroy)(void*))
177{
178  if (w->size_cbdata_destroy) {
179    w->size_cbdata_destroy(w->size_cbdata);
180    w->size_cbdata = 0;
181    w->size_cbdata_destroy = 0;
182  }
183
184  w->size_cb = cb;
185  w->size_cbdata = cbdata;
186  w->size_cbdata_destroy = cbdata_destroy;
187
188  owl_window_recompute_position(w);
189}
190
191/** Hierarchy **/
192
193void owl_window_unlink(owl_window *w)
194{
195  /* make sure the window is unmapped first */
196  _owl_window_unrealize(w);
197  /* unlink parent/child information */
198  if (w->parent) {
199    if (w->prev)
200      w->prev->next = w->next;
201    if (w->next)
202      w->next->prev = w->prev;
203    if (w->parent->child == w)
204      w->parent->child = w->next;
205    w->parent = NULL;
206    g_object_unref (w);
207  }
208}
209
210static void _owl_window_link(owl_window *w, owl_window *parent)
211{
212  if (w->parent == parent)
213    return;
214
215  owl_window_unlink(w);
216  if (parent) {
217    w->parent = parent;
218    w->next = parent->child;
219    parent->child = w;
220    g_object_ref (w);
221  }
222}
223
224/* mimic g_list_foreach for consistency */
225void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
226{
227  owl_window *w;
228  for (w = parent->child;
229       w != NULL;
230       w = w->next) {
231    func(w, user_data);
232  }
233}
234
235void owl_window_children_foreach_onearg(owl_window *parent, void (*func)(owl_window*))
236{
237  owl_window_children_foreach(parent, (GFunc)func, NULL);
238}
239
240owl_window *owl_window_get_parent(owl_window *w)
241{
242  return w->parent;
243}
244
245/** Internal window management **/
246
247static void _owl_window_create_curses(owl_window *w)
248{
249  if (w->is_screen) {
250    resizeterm(w->nlines, w->ncols);
251    w->win = stdscr;
252  } else 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    if (w->parent == NULL || w->parent->win == NULL)
257      return;
258    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
259  }
260}
261
262static void _owl_window_destroy_curses(owl_window *w)
263{
264  if (w->is_screen) {
265    /* don't deallocate the dummy */
266    w->win = NULL;
267  } else {
268    if (w->pan) {
269      /* panels assume their windows always exist, so we put in a fake one */
270      replace_panel(w->pan, _dummy_window());
271    }
272    if (w->win) {
273      /* and destroy our own window */
274      delwin(w->win);
275      w->win = NULL;
276    }
277  }
278}
279
280void owl_window_show(owl_window *w)
281{
282  w->shown = 1;
283  _owl_window_realize(w);
284  if (w->pan)
285    show_panel(w->pan);
286}
287
288void owl_window_show_all(owl_window *w)
289{
290  owl_window_show(w);
291  owl_window_children_foreach_onearg(w, owl_window_show);
292}
293
294void owl_window_hide(owl_window *w)
295{
296  /* you can't unmap the screen */
297  if (w->is_screen)
298    return;
299  w->shown = 0;
300  if (w->pan)
301    hide_panel(w->pan);
302  _owl_window_unrealize(w);
303}
304
305int owl_window_is_shown(owl_window *w)
306{
307  return w->shown;
308}
309
310int owl_window_is_realized(owl_window *w)
311{
312  return w->win != NULL;
313}
314
315int owl_window_is_toplevel(owl_window *w)
316{
317  return w->pan != NULL;
318}
319
320static void _owl_window_realize(owl_window *w)
321{
322  /* check if we can create a window */
323  if ((w->parent && w->parent->win == NULL)
324      || !w->shown
325      || w->win != NULL)
326    return;
327  _owl_window_create_curses(w);
328  if (w->win == NULL)
329    return;
330  /* schedule a repaint */
331  owl_window_dirty(w);
332  /* map the children */
333  owl_window_children_foreach_onearg(w, _owl_window_realize);
334}
335
336static void _owl_window_unrealize(owl_window *w)
337{
338  if (w->win == NULL)
339    return;
340  /* unmap all the children */
341  owl_window_children_foreach_onearg(w, _owl_window_unrealize);
342  _owl_window_destroy_curses(w);
343  /* subwins leave a mess in the parent; dirty it */
344  if (w->parent)
345    owl_window_dirty(w->parent);
346}
347
348/** Painting and book-keeping **/
349
350void owl_window_dirty(owl_window *w)
351{
352  /* don't put the screen on this list; pointless */
353  if (w->is_screen)
354    return;
355  if (!owl_window_is_realized(w))
356    return;
357  if (!w->dirty) {
358    w->dirty = 1;
359    while (w && !w->dirty_subtree) {
360      w->dirty_subtree = 1;
361      w = w->parent;
362    }
363    owl_global_set_needrefresh(&g);
364  }
365}
366
367void owl_window_dirty_children(owl_window *w)
368{
369  owl_window_children_foreach_onearg(w, owl_window_dirty);
370}
371
372static void _owl_window_redraw(owl_window *w)
373{
374  if (!w->dirty) return;
375  if (w->win && w->redraw_cb) {
376    w->redraw_cb(w, w->win, w->redraw_cbdata);
377    wsyncup(w->win);
378  }
379  w->dirty = 0;
380}
381
382static void _owl_window_redraw_subtree(owl_window *w)
383{
384  if (!w->dirty_subtree)
385    return;
386  _owl_window_redraw(w);
387  owl_window_children_foreach_onearg(w, _owl_window_redraw_subtree);
388}
389
390/*
391Redraw all the windows with scheduled redraws.
392NOTE: This function shouldn't be called outside the event loop
393*/
394void owl_window_redraw_scheduled(void)
395{
396  _owl_window_redraw_subtree(owl_window_get_screen());
397}
398
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
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
415void owl_window_move(owl_window *w, int begin_y, int begin_x)
416{
417  if (w->is_screen) return; /* can't move the screen */
418  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
419
420  w->begin_y = begin_y;
421  w->begin_x = begin_x;
422  if (w->shown) {
423    /* Window is shown, we must try to have a window at the end */
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. */
439    _owl_window_unrealize(w);
440    _owl_window_realize(w);
441  }
442}
443
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
457  w->begin_y = begin_y;
458  w->begin_x = begin_x;
459  w->nlines = nlines;
460  w->ncols = ncols;
461  /* window is shown, we must try to have a window at the end */
462  if (w->shown) {
463    /* resizing in ncurses is hard: give up do a unrealize/realize */
464    _owl_window_unrealize(w);
465  }
466  /* recalculate children sizes BEFORE remapping, so that everything can resize */
467  owl_window_children_foreach_onearg(w, owl_window_recompute_position);
468  if (w->shown) {
469    _owl_window_realize(w);
470  }
471}
472
473void owl_window_resize(owl_window *w, int nlines, int ncols)
474{
475  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
476}
477
478void owl_window_recompute_position(owl_window *w)
479{
480  if (w->size_cb) {
481    /* TODO: size_cb probably wants to actually take four int*s */
482    w->size_cb(w, w->size_cbdata);
483  }
484}
485
486
487/** Stacking order **/
488
489void owl_window_top(owl_window *w) {
490  if (!w->pan) {
491    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
492    return;
493  }
494  top_panel(w->pan);
495}
496
497owl_window *owl_window_above(owl_window *w) {
498  PANEL *pan;
499
500  if (!w->pan) {
501    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
502    return NULL;
503  }
504  pan = panel_above(w->pan);
505  /* cast because panel_userptr pointlessly returns a const void * */
506  return pan ? (void*) panel_userptr(pan) : NULL;
507}
508
509owl_window *owl_window_below(owl_window *w) {
510  PANEL *pan;
511
512  if (!w->pan) {
513    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
514    return NULL;
515  }
516  pan = panel_below(w->pan);
517  /* cast because panel_userptr pointlessly returns a const void * */
518  return pan ? (void*) panel_userptr(pan) : NULL;
519}
Note: See TracBrowser for help on using the repository browser.