source: window.c @ e96b4ce

release-1.10release-1.7release-1.8release-1.9
Last change on this file since e96b4ce was 5d74b7c, checked in by David Benjamin <davidben@mit.edu>, 15 years ago
Minor formatting
  • Property mode set to 100644
File size: 11.8 KB
Line 
1#include "owl.h"
2
3#include <assert.h>
4
5struct _owl_window { /*noproto*/
6  /* hierarchy information */
7  owl_window *parent;
8  owl_window *child;
9  owl_window *next, *prev;
10  /* flags */
11  int dirty : 1;
12  int dirty_subtree : 1;
13  int mapped : 1;
14  int is_screen : 1;
15  /* window information */
16  WINDOW *win;
17  PANEL *pan;
18  int nlines, ncols;
19  int begin_y, begin_x;
20  /* hooks */
21  void (*redraw_cb)(owl_window *, WINDOW *, void *);
22  void  *redraw_cbdata;
23  void (*redraw_cbdata_destroy)(void *);
24
25  void (*size_cb)(owl_window *, void *);
26  void  *size_cbdata;
27  void (*size_cbdata_destroy)(void *);
28
29  void (*destroy_cb)(owl_window *, void *);
30  void  *destroy_cbdata;
31  void (*destroy_cbdata_destroy)(void *);
32};
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_unlink(owl_window *w);
37static void _owl_window_link(owl_window *w, owl_window *parent);
38
39static void _owl_window_create_curses(owl_window *w);
40static void _owl_window_destroy_curses(owl_window *w);
41
42static void _owl_window_map_internal(owl_window *w);
43static void _owl_window_unmap_internal(owl_window *w);
44
45/** singletons **/
46
47static WINDOW *_dummy_window(void)
48{
49  static WINDOW *dummy = NULL;
50  if (!dummy) {
51    dummy = newwin(1, 1, 0, 0);
52  }
53  return dummy;
54}
55
56static void _screen_calculate_size(owl_window *screen, void *user_data)
57{
58  owl_global *g = user_data;
59  int lines, cols;
60  owl_global_get_terminal_size(&lines, &cols);
61  if (!g->lines) g->lines = lines;
62  if (!g->cols) g->cols = cols;
63  owl_window_resize(screen, g->lines, g->cols);
64}
65
66owl_window *owl_window_get_screen(void)
67{
68  static owl_window *screen = NULL;
69  if (!screen) {
70    /* The screen is magical. It's 'mapped', but the only mean of it going
71     * invisible is if we're tore down curses (i.e. screen resize) */
72    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
73    screen->is_screen = 1;
74    owl_window_set_size_cb(screen, _screen_calculate_size, &g, 0);
75    owl_window_map(screen, 0);
76  }
77  return screen;
78}
79
80/** Creation and Destruction **/
81
82owl_window *owl_window_new(owl_window *parent)
83{
84  if (!parent)
85    parent = owl_window_get_screen();
86  return _owl_window_new(parent, 0, 0, 0, 0);
87}
88
89static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
90{
91  owl_window *w;
92
93  w = owl_malloc(sizeof(owl_window));
94  memset(w, 0, sizeof(*w));
95
96  w->nlines = nlines;
97  w->ncols = ncols;
98  w->begin_y = begin_y;
99  w->begin_x = begin_x;
100
101  _owl_window_link(w, parent);
102  if (parent && parent->is_screen) {
103    w->pan = new_panel(_dummy_window());
104    set_panel_userptr(w->pan, w);
105  }
106
107  return w;
108}
109
110void owl_window_delete(owl_window *w)
111{
112  /* destroy all children */
113  owl_window_children_foreach_onearg(w, owl_window_delete);
114
115  /* notify everyone of your imminent demise */
116  if (w->destroy_cb)
117    w->destroy_cb(w, w->destroy_cbdata);
118
119  /* clear all cbs */
120  owl_window_set_redraw_cb(w, 0, 0, 0);
121  owl_window_set_size_cb(w, 0, 0, 0);
122  owl_window_set_destroy_cb(w, 0, 0, 0);
123
124  /* destroy curses structures */
125  owl_window_unmap(w);
126  if (w->pan) {
127    del_panel(w->pan);
128  }
129
130  /* remove from hierarchy */
131  _owl_window_unlink(w);
132  owl_free(w);
133}
134
135/** Callbacks **/
136
137void owl_window_set_redraw_cb(owl_window *w, void (*cb)(owl_window*, WINDOW*, void*), void *cbdata, void (*cbdata_destroy)(void*))
138{
139  if (w->redraw_cbdata_destroy) {
140    w->redraw_cbdata_destroy(w->redraw_cbdata);
141    w->redraw_cbdata = 0;
142    w->redraw_cbdata_destroy = 0;
143  }
144
145  w->redraw_cb = cb;
146  w->redraw_cbdata = cbdata;
147  w->redraw_cbdata_destroy = cbdata_destroy;
148
149  /* mark the window as dirty, to take new cb in account */
150  owl_window_dirty(w);
151}
152
153void owl_window_set_size_cb(owl_window *w, void (*cb)(owl_window*, void*), void *cbdata, void (*cbdata_destroy)(void*))
154{
155  if (w->size_cbdata_destroy) {
156    w->size_cbdata_destroy(w->size_cbdata);
157    w->size_cbdata = 0;
158    w->size_cbdata_destroy = 0;
159  }
160
161  w->size_cb = cb;
162  w->size_cbdata = cbdata;
163  w->size_cbdata_destroy = cbdata_destroy;
164
165  owl_window_recompute_position(w);
166}
167
168void owl_window_set_destroy_cb(owl_window *w, void (*cb)(owl_window*, void*), void *cbdata, void (*cbdata_destroy)(void*))
169{
170  if (w->destroy_cbdata_destroy) {
171    w->destroy_cbdata_destroy(w->destroy_cbdata);
172    w->destroy_cbdata = 0;
173    w->destroy_cbdata_destroy = 0;
174  }
175
176  w->destroy_cb = cb;
177  w->destroy_cbdata = cbdata;
178  w->destroy_cbdata_destroy = cbdata_destroy;
179}
180
181/** Hierarchy **/
182
183static void _owl_window_unlink(owl_window *w)
184{
185  /* unlink parent/child information */
186  if (w->parent) {
187    if (w->prev)
188      w->prev->next = w->next;
189    if (w->next)
190      w->next->prev = w->prev;
191    if (w->parent->child == w)
192      w->parent->child = w->next;
193  }
194  w->parent = NULL;
195}
196
197static void _owl_window_link(owl_window *w, owl_window *parent)
198{
199  if (w->parent == parent)
200    return;
201  if (w->parent)
202    _owl_window_unlink(w);
203
204  w->parent = parent;
205  if (parent) {
206    w->next = parent->child;
207    parent->child = w;
208  }
209}
210
211/* mimic g_list_foreach for consistency */
212void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
213{
214  owl_window *w;
215  for (w = parent->child;
216       w != NULL;
217       w = w->next) {
218    func(w, user_data);
219  }
220}
221
222void owl_window_children_foreach_onearg(owl_window *parent, void (*func)(owl_window*))
223{
224  owl_window *w;
225  for (w = parent->child;
226       w != NULL;
227       w = w->next) {
228    func(w);
229  }
230}
231
232owl_window *owl_window_get_parent(owl_window *w)
233{
234  return w->parent;
235}
236
237/** Internal window management **/
238
239static void _owl_window_create_curses(owl_window *w)
240{
241  if (w->is_screen) {
242    resizeterm(w->nlines, w->ncols);
243    w->win = stdscr;
244  } else if (w->pan) {
245    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
246    replace_panel(w->pan, w->win);
247  } else {
248    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
249  }
250}
251
252static void _owl_window_destroy_curses(owl_window *w)
253{
254  if (w->is_screen) {
255    /* don't deallocate the dummy */
256    w->win = NULL;
257  } else {
258    if (w->pan) {
259      /* panels assume their windows always exist, so we put in a fake one */
260      replace_panel(w->pan, _dummy_window());
261    }
262    if (w->win) {
263      /* and destroy our own window */
264      delwin(w->win);
265      w->win = NULL;
266    }
267  }
268}
269
270static void _map_recurse_curry(owl_window *w)
271{
272  owl_window_map(w, 1);
273}
274
275void owl_window_map(owl_window *w, int recurse)
276{
277  w->mapped = 1;
278  _owl_window_map_internal(w);
279  if (w->pan)
280    show_panel(w->pan);
281  if (recurse)
282    owl_window_children_foreach_onearg(w, _map_recurse_curry);
283}
284
285void owl_window_unmap(owl_window *w)
286{
287  /* you can't unmap the screen */
288  if (w->is_screen)
289    return;
290  w->mapped = 0;
291  if (w->pan)
292    hide_panel(w->pan);
293  _owl_window_unmap_internal(w);
294}
295
296int owl_window_is_mapped(owl_window *w)
297{
298  return w->mapped;
299}
300
301int owl_window_is_visible(owl_window *w)
302{
303  return w->win != NULL;
304}
305
306int owl_window_is_toplevel(owl_window *w)
307{
308  return w->pan != NULL;
309}
310
311static void _owl_window_map_internal(owl_window *w)
312{
313  /* check if we can create a window */
314  if ((w->parent && w->parent->win == NULL)
315      || !w->mapped
316      || w->win != NULL)
317    return;
318  _owl_window_create_curses(w);
319  /* schedule a repaint */
320  owl_window_dirty(w);
321  /* map the children, unless we failed */
322  if (w->win)
323    owl_window_children_foreach_onearg(w, _owl_window_map_internal);
324}
325
326static void _owl_window_unmap_internal(owl_window *w)
327{
328  if (w->win == NULL)
329    return;
330  /* unmap all the children */
331  owl_window_children_foreach_onearg(w, _owl_window_unmap_internal);
332  _owl_window_destroy_curses(w);
333  /* subwins leave a mess in the parent; dirty it */
334  if (w->parent)
335    owl_window_dirty(w->parent);
336}
337
338/** Painting and book-keeping **/
339
340void owl_window_dirty(owl_window *w)
341{
342  /* don't put the screen on this list; pointless */
343  if (w->is_screen)
344    return;
345  if (!w->dirty) {
346    w->dirty = 1;
347    while (w && !w->dirty_subtree) {
348      w->dirty_subtree = 1;
349      w = w->parent;
350    }
351    owl_global_set_needrefresh(&g);
352  }
353}
354
355void owl_window_dirty_children(owl_window *w)
356{
357  owl_window_children_foreach_onearg(w, owl_window_dirty);
358}
359
360static void _owl_window_redraw(owl_window *w)
361{
362  if (!w->dirty) return;
363  if (w->win && w->redraw_cb) {
364    w->redraw_cb(w, w->win, w->redraw_cbdata);
365    wsyncup(w->win);
366  }
367  w->dirty = 0;
368}
369
370static void _owl_window_redraw_subtree(owl_window *w)
371{
372  if (!w->dirty_subtree)
373    return;
374  _owl_window_redraw(w);
375  owl_window_children_foreach_onearg(w, _owl_window_redraw_subtree);
376}
377
378/*
379Redraw all the windows with scheduled redraws.
380NOTE: This function shouldn't be called outside the event loop
381*/
382void owl_window_redraw_scheduled(void)
383{
384  _owl_window_redraw_subtree(owl_window_get_screen());
385}
386
387void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
388{
389  werase(win);
390  owl_window_dirty_children(w);
391}
392
393/** Window position **/
394
395void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
396{
397  if (nlines)  *nlines  = w->nlines;
398  if (ncols)   *ncols   = w->ncols;
399  if (begin_y) *begin_y = w->begin_y;
400  if (begin_x) *begin_x = w->begin_x;
401}
402
403void owl_window_move(owl_window *w, int begin_y, int begin_x)
404{
405  if (w->is_screen) return; /* can't move the screen */
406  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
407
408  w->begin_y = begin_y;
409  w->begin_x = begin_x;
410  if (w->mapped) {
411    /* Window is mapped, we must try to have a window at the end */
412    if (w->win) {
413      /* We actually do have a window; let's move it */
414      if (w->pan) {
415        if (move_panel(w->pan, begin_y, begin_x) == OK)
416          return;
417      } else {
418        if (mvderwin(w->win, begin_y, begin_x) == OK) {
419          /* now both we and the parent are dirty */
420          owl_window_dirty(w->parent);
421          owl_window_dirty(w);
422          return;
423        }
424      }
425    }
426    /* We don't have a window or failed to move it. Fine. Brute force. */
427    _owl_window_unmap_internal(w);
428    _owl_window_map_internal(w);
429  }
430}
431
432void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
433{
434  /* can't move the screen */
435  if (w->is_screen) {
436    begin_y = begin_x = 0;
437  }
438
439  if (w->nlines == nlines && w->ncols == ncols) {
440    /* moving is easier */
441    owl_window_move(w, begin_y, begin_x);
442    return;
443  }
444
445  w->begin_y = begin_y;
446  w->begin_x = begin_x;
447  w->nlines = nlines;
448  w->ncols = ncols;
449  /* window is mapped, we must try to have a window at the end */
450  if (w->mapped) {
451    /* resizing in ncurses is hard: give up do a unmap/map */
452    _owl_window_unmap_internal(w);
453  }
454  /* recalculate children sizes BEFORE remapping, so that everything can resize */
455  owl_window_children_foreach_onearg(w, owl_window_recompute_position);
456  if (w->mapped) {
457    _owl_window_map_internal(w);
458  }
459}
460
461void owl_window_resize(owl_window *w, int nlines, int ncols)
462{
463  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
464}
465
466void owl_window_recompute_position(owl_window *w)
467{
468  if (w->size_cb) {
469    /* TODO: size_cb probably wants to actually take four int*s */
470    w->size_cb(w, w->size_cbdata);
471  }
472}
473
474
475/** Stacking order **/
476
477void owl_window_top(owl_window *w) {
478  if (!w->pan) {
479    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
480    return;
481  }
482  top_panel(w->pan);
483}
484
485owl_window *owl_window_above(owl_window *w) {
486  PANEL *pan;
487
488  if (!w->pan) {
489    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
490    return NULL;
491  }
492  pan = panel_above(w->pan);
493  /* cast because panel_userptr pointlessly returns a const void * */
494  return pan ? (void*) panel_userptr(pan) : NULL;
495}
496
497owl_window *owl_window_below(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_below(w->pan);
505  /* cast because panel_userptr pointlessly returns a const void * */
506  return pan ? (void*) panel_userptr(pan) : NULL;
507}
Note: See TracBrowser for help on using the repository browser.