source: window.c @ 449af72

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