source: window.c @ 5f7eadf

release-1.7release-1.8release-1.9
Last change on this file since 5f7eadf was 7c8811c, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
Fix a warning and add convenience paint callback
  • Property mode set to 100644
File size: 11.3 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
219owl_window *owl_window_get_parent(owl_window *w)
220{
221  return w->parent;
222}
223
224/** Internal window management **/
225
226static void _owl_window_create_curses(owl_window *w)
227{
228  if (w->is_screen) {
229    resizeterm(w->nlines, w->ncols);
230    w->win = _dummy_window();
231  } else if (w->pan) {
232    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
233    replace_panel(w->pan, w->win);
234  } else {
235    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
236  }
237}
238
239static void _owl_window_destroy_curses(owl_window *w)
240{
241  if (w->is_screen) {
242    /* don't deallocate the dummy */
243    w->win = NULL;
244  } else {
245    if (w->pan) {
246      /* panels assume their windows always exist, so we put in a fake one */
247      replace_panel(w->pan, _dummy_window());
248    }
249    if (w->win) {
250      /* and destroy our own window */
251      delwin(w->win);
252      w->win = NULL;
253    }
254  }
255}
256
257static void _map_recurse_curry(owl_window *w)
258{
259  owl_window_map(w, 1);
260}
261
262void owl_window_map(owl_window *w, int recurse)
263{
264  w->mapped = 1;
265  _owl_window_map_internal(w);
266  if (w->pan)
267    show_panel(w->pan);
268  if (recurse)
269    owl_window_children_foreach_onearg(w, _map_recurse_curry);
270}
271
272void owl_window_unmap(owl_window *w)
273{
274  /* you can't unmap the screen */
275  if (w->is_screen)
276    return;
277  w->mapped = 0;
278  if (w->pan)
279    hide_panel(w->pan);
280  _owl_window_unmap_internal(w);
281}
282
283int owl_window_is_mapped(owl_window *w)
284{
285  return w->mapped;
286}
287
288int owl_window_is_visible(owl_window *w)
289{
290  return w->win != NULL;
291}
292
293int owl_window_is_toplevel(owl_window *w)
294{
295  return w->pan != NULL;
296}
297
298static void _owl_window_map_internal(owl_window *w)
299{
300  /* check if we can create a window */
301  if ((w->parent && w->parent->win == NULL)
302      || !w->mapped
303      || w->win != NULL)
304    return;
305  _owl_window_create_curses(w);
306  /* schedule a repaint */
307  owl_window_dirty(w);
308  /* map the children, unless we failed */
309  if (w->win)
310    owl_window_children_foreach_onearg(w, _owl_window_map_internal);
311}
312
313static void _owl_window_unmap_internal(owl_window *w)
314{
315  if (w->win == NULL)
316    return;
317  /* unmap all the children */
318  owl_window_children_foreach_onearg(w, _owl_window_unmap_internal);
319  _owl_window_destroy_curses(w);
320  /* subwins leave a mess in the parent; dirty it */
321  if (w->parent)
322    owl_window_dirty(w->parent);
323}
324
325/** Painting and book-keeping **/
326
327void owl_window_dirty(owl_window *w)
328{
329  /* don't put the screen on this list; pointless */
330  if (w->is_screen)
331    return;
332  if (!w->dirty) {
333    w->dirty = 1;
334    while (w && !w->dirty_subtree) {
335      w->dirty_subtree = 1;
336      w = w->parent;
337    }
338    owl_global_set_needrefresh(&g);
339  }
340}
341
342void owl_window_dirty_children(owl_window *w)
343{
344  owl_window_children_foreach_onearg(w, owl_window_dirty);
345}
346
347static void _owl_window_redraw(owl_window *w)
348{
349  if (!w->dirty) return;
350  if (w->win && w->redraw_cb) {
351    w->redraw_cb(w, w->win, w->redraw_cbdata);
352    wsyncup(w->win);
353  }
354  w->dirty = 0;
355}
356
357static void _owl_window_redraw_subtree(owl_window *w)
358{
359  if (!w->dirty_subtree)
360    return;
361  _owl_window_redraw(w);
362  owl_window_children_foreach_onearg(w, _owl_window_redraw_subtree);
363}
364
365/*
366Redraw all the windows with scheduled redraws.
367NOTE: This function shouldn't be called outside the event loop
368*/
369void owl_window_redraw_scheduled(void)
370{
371  _owl_window_redraw_subtree(owl_window_get_screen());
372}
373
374void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
375{
376  werase(win);
377  owl_window_dirty_children(w);
378}
379
380/** Window position **/
381
382void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
383{
384  if (nlines)  *nlines  = w->nlines;
385  if (ncols)   *ncols   = w->ncols;
386  if (begin_y) *begin_y = w->begin_y;
387  if (begin_x) *begin_x = w->begin_x;
388}
389
390void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
391{
392  /* can't move the screen */
393  if (w->is_screen) {
394    begin_y = begin_x = 0;
395  }
396
397  if (w->nlines == nlines && w->ncols == ncols) {
398    /* moving is easier */
399    owl_window_move(w, begin_y, begin_x);
400    return;
401  }
402
403  w->begin_y = begin_y;
404  w->begin_x = begin_x;
405  w->nlines = nlines;
406  w->ncols = ncols;
407  /* window is mapped, we must try to have a window at the end */
408  if (w->mapped) {
409    /* resizing in ncurses is hard: give up do a unmap/map */
410    _owl_window_unmap_internal(w);
411  }
412  /* call the resize hooks BEFORE remapping, so that everything can resize */
413  if (w->resize_cb)
414    w->resize_cb(w, w->resize_cbdata);
415  if (w->mapped) {
416    _owl_window_map_internal(w);
417  }
418}
419
420void owl_window_resize(owl_window *w, int nlines, int ncols)
421{
422  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
423}
424
425void owl_window_move(owl_window *w, int begin_y, int begin_x)
426{
427  /* can't move the screen */
428  if (w->is_screen) return;
429
430  w->begin_y = begin_y;
431  w->begin_x = begin_x;
432  if (w->mapped) {
433    /* Window is mapped, we must try to have a window at the end */
434    if (w->win) {
435      /* We actually do have a window; let's move it */
436      if (w->pan) {
437        if (move_panel(w->pan, begin_y, begin_x) == OK)
438          return;
439      } else {
440        if (mvderwin(w->win, begin_y, begin_x) == OK) {
441          /* now both we and the parent are dirty */
442          owl_window_dirty(w->parent);
443          owl_window_dirty(w);
444          return;
445        }
446      }
447    }
448    /* We don't have a window or failed to move it. Fine. Brute force. */
449    _owl_window_unmap_internal(w);
450    _owl_window_map_internal(w);
451  }
452}
453
454/** Stacking order **/
455
456void owl_window_top(owl_window *w) {
457  if (!w->pan) {
458    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
459    return;
460  }
461  top_panel(w->pan);
462}
463
464owl_window *owl_window_above(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_above(w->pan);
472  /* cast because panel_userptr pointlessly returns a const void * */
473  return pan ? (void*) panel_userptr(pan) : NULL;
474}
475
476owl_window *owl_window_below(owl_window *w) {
477  PANEL *pan;
478
479  if (!w->pan) {
480    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
481    return NULL;
482  }
483  pan = panel_below(w->pan);
484  /* cast because panel_userptr pointlessly returns a const void * */
485  return pan ? (void*) panel_userptr(pan) : NULL;
486}
Note: See TracBrowser for help on using the repository browser.