source: window.c @ 4751077a

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