source: window.c @ 053f751

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