source: window.c @ 678a505

release-1.7release-1.8release-1.9
Last change on this file since 678a505 was 678a505, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
Move the attribute setting between unrealize/realize Doesn't do anything, but it makes the logic clearer.
  • Property mode set to 100644
File size: 11.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};
22
23enum {
24  REDRAW,
25  RESIZED,
26  LAST_SIGNAL
27};
28
29static guint window_signals[LAST_SIGNAL] = { 0 };
30
31static void owl_window_dispose(GObject *gobject);
32static void owl_window_finalize(GObject *gobject);
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
44static void _owl_window_redraw_cleanup(owl_window *w, WINDOW *win);
45
46static owl_window *cursor_owner;
47
48G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
49
50static void owl_window_class_init (OwlWindowClass *klass)
51{
52  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
53
54  /* Set up the vtabl */
55  gobject_class->dispose = owl_window_dispose;
56  gobject_class->finalize = owl_window_finalize;
57
58  klass->redraw = _owl_window_redraw_cleanup;
59  klass->resized = NULL;
60
61  /* Create the signals, remember IDs */
62  window_signals[REDRAW] =
63    g_signal_new("redraw",
64                 G_TYPE_FROM_CLASS(gobject_class),
65                 G_SIGNAL_RUN_CLEANUP,
66                 G_STRUCT_OFFSET(OwlWindowClass, redraw),
67                 NULL, NULL,
68                 g_cclosure_marshal_VOID__POINTER,
69                 G_TYPE_NONE,
70                 1,
71                 G_TYPE_POINTER, NULL);
72
73  /* TODO: maybe type should be VOID__INT_INT_INT_INT; will need to generate a
74   * marshaller */
75  window_signals[RESIZED] =
76    g_signal_new("resized",
77                 G_TYPE_FROM_CLASS(gobject_class),
78                 G_SIGNAL_RUN_FIRST,
79                 G_STRUCT_OFFSET(OwlWindowClass, resized),
80                 NULL, NULL,
81                 g_cclosure_marshal_VOID__VOID,
82                 G_TYPE_NONE,
83                 0,
84                 NULL);
85}
86
87static void owl_window_dispose (GObject *object)
88{
89  owl_window *w = OWL_WINDOW (object);
90
91  /* Unmap the window */
92  owl_window_hide (w);
93
94  /* Unlink and unref all children */
95  while (w->child) {
96    owl_window *child = w->child;
97    owl_window_unlink (child);
98  }
99
100  /* Remove from hierarchy */
101  owl_window_unlink (w);
102
103  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
104}
105
106static void owl_window_finalize (GObject *object)
107{
108  owl_window *w = OWL_WINDOW(object);
109
110  if (w->pan) {
111    del_panel(w->pan);
112    w->pan = NULL;
113  }
114
115  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
116}
117
118static void owl_window_init (owl_window *w)
119{
120}
121
122/** singletons **/
123
124static WINDOW *_dummy_window(void)
125{
126  static WINDOW *dummy = NULL;
127  if (!dummy) {
128    dummy = newwin(1, 1, 0, 0);
129  }
130  return dummy;
131}
132
133owl_window *owl_window_get_screen(void)
134{
135  static owl_window *screen = NULL;
136  if (!screen) {
137    /* The screen is magical. It's 'shown', but the only mean of it going
138     * invisible is if we're tore down curses (i.e. screen resize) */
139    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
140    screen->is_screen = 1;
141    owl_window_show(screen);
142  }
143  return screen;
144}
145
146/** Creation and Destruction **/
147
148owl_window *owl_window_new(owl_window *parent)
149{
150  if (!parent)
151    parent = owl_window_get_screen();
152  return _owl_window_new(parent, 0, 0, 0, 0);
153}
154
155static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
156{
157  owl_window *w;
158
159  w = g_object_new (OWL_TYPE_WINDOW, NULL);
160  if (w == NULL) g_error("Failed to create owl_window instance");
161
162  w->nlines = nlines;
163  w->ncols = ncols;
164  w->begin_y = begin_y;
165  w->begin_x = begin_x;
166
167  _owl_window_link(w, parent);
168  if (parent && parent->is_screen) {
169    w->pan = new_panel(_dummy_window());
170    set_panel_userptr(w->pan, w);
171  }
172
173  return w;
174}
175
176/** Hierarchy **/
177
178void owl_window_unlink(owl_window *w)
179{
180  /* make sure the window is unmapped first */
181  _owl_window_unrealize(w);
182  /* unlink parent/child information */
183  if (w->parent) {
184    if (w->prev)
185      w->prev->next = w->next;
186    if (w->next)
187      w->next->prev = w->prev;
188    if (w->parent->child == w)
189      w->parent->child = w->next;
190    w->parent = NULL;
191    g_object_unref (w);
192  }
193}
194
195static void _owl_window_link(owl_window *w, owl_window *parent)
196{
197  if (w->parent == parent)
198    return;
199
200  owl_window_unlink(w);
201  if (parent) {
202    w->parent = parent;
203    w->next = parent->child;
204    parent->child = w;
205    g_object_ref (w);
206  }
207}
208
209/* mimic g_list_foreach for consistency */
210void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
211{
212  owl_window *w;
213  for (w = parent->child;
214       w != NULL;
215       w = w->next) {
216    func(w, user_data);
217  }
218}
219
220owl_window *owl_window_get_parent(owl_window *w)
221{
222  return w->parent;
223}
224
225/** Internal window management **/
226
227static void _owl_window_create_curses(owl_window *w)
228{
229  if (w->is_screen) {
230    resizeterm(w->nlines, w->ncols);
231    w->win = stdscr;
232  } else if (w->pan) {
233    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
234    replace_panel(w->pan, w->win);
235  } else {
236    if (w->parent == NULL || w->parent->win == NULL)
237      return;
238    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
239  }
240}
241
242static void _owl_window_destroy_curses(owl_window *w)
243{
244  if (w->is_screen) {
245    /* don't deallocate the dummy */
246    w->win = NULL;
247  } else {
248    if (w->pan) {
249      /* panels assume their windows always exist, so we put in a fake one */
250      replace_panel(w->pan, _dummy_window());
251    }
252    if (w->win) {
253      /* and destroy our own window */
254      delwin(w->win);
255      w->win = NULL;
256    }
257  }
258}
259
260void owl_window_show(owl_window *w)
261{
262  w->shown = 1;
263  _owl_window_realize(w);
264  if (w->pan)
265    show_panel(w->pan);
266}
267
268void owl_window_show_all(owl_window *w)
269{
270  owl_window_show(w);
271  owl_window_children_foreach(w, (GFunc)owl_window_show, 0);
272}
273
274void owl_window_hide(owl_window *w)
275{
276  /* you can't unmap the screen */
277  if (w->is_screen)
278    return;
279  w->shown = 0;
280  if (w->pan)
281    hide_panel(w->pan);
282  _owl_window_unrealize(w);
283}
284
285int owl_window_is_shown(owl_window *w)
286{
287  return w->shown;
288}
289
290int owl_window_is_realized(owl_window *w)
291{
292  return w->win != NULL;
293}
294
295int owl_window_is_toplevel(owl_window *w)
296{
297  return w->pan != NULL;
298}
299
300static void _owl_window_realize(owl_window *w)
301{
302  /* check if we can create a window */
303  if ((w->parent && w->parent->win == NULL)
304      || !w->shown
305      || w->win != NULL)
306    return;
307  _owl_window_create_curses(w);
308  if (w->win == NULL)
309    return;
310  /* schedule a repaint */
311  owl_window_dirty(w);
312  /* map the children */
313  owl_window_children_foreach(w, (GFunc)_owl_window_realize, 0);
314}
315
316static void _owl_window_unrealize(owl_window *w)
317{
318  if (w->win == NULL)
319    return;
320  /* unmap all the children */
321  owl_window_children_foreach(w, (GFunc)_owl_window_unrealize, 0);
322  _owl_window_destroy_curses(w);
323  w->dirty = w->dirty_subtree = 0;
324  /* subwins leave a mess in the parent; dirty it */
325  if (w->parent)
326    owl_window_dirty(w->parent);
327}
328
329/** Painting and book-keeping **/
330
331void owl_window_set_cursor(owl_window *w)
332{
333  cursor_owner = w;
334  g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
335  owl_global_set_needrefresh(&g);
336}
337
338void owl_window_dirty(owl_window *w)
339{
340  /* don't put the screen on this list; pointless */
341  if (w->is_screen)
342    return;
343  if (!owl_window_is_realized(w))
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(w, (GFunc)owl_window_dirty, 0);
358}
359
360static void _owl_window_redraw(owl_window *w)
361{
362  if (!w->dirty) return;
363  if (w->win) {
364    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
365  }
366  w->dirty = 0;
367}
368
369static void _owl_window_redraw_subtree(owl_window *w)
370{
371  if (!w->dirty_subtree)
372    return;
373  _owl_window_redraw(w);
374  owl_window_children_foreach(w, (GFunc)_owl_window_redraw_subtree, 0);
375}
376
377/*
378Redraw all the windows with scheduled redraws.
379NOTE: This function shouldn't be called outside the event loop
380*/
381void owl_window_redraw_scheduled(void)
382{
383  _owl_window_redraw_subtree(owl_window_get_screen());
384  update_panels();
385  if (cursor_owner && cursor_owner->win)
386    wnoutrefresh(cursor_owner->win);
387}
388
389static void _owl_window_redraw_cleanup(owl_window *w, WINDOW *win)
390{
391  wsyncup(win);
392}
393
394void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
395{
396  werase(win);
397  owl_window_dirty_children(w);
398}
399
400/** Window position **/
401
402void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
403{
404  if (nlines)  *nlines  = w->nlines;
405  if (ncols)   *ncols   = w->ncols;
406  if (begin_y) *begin_y = w->begin_y;
407  if (begin_x) *begin_x = w->begin_x;
408}
409
410void owl_window_move(owl_window *w, int begin_y, int begin_x)
411{
412  if (w->is_screen) return; /* can't move the screen */
413  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
414
415  w->begin_y = begin_y;
416  w->begin_x = begin_x;
417  if (w->shown) {
418    /* Window is shown, we must try to have a window at the end */
419    if (w->win) {
420      /* We actually do have a window; let's move it */
421      if (w->pan) {
422        if (move_panel(w->pan, begin_y, begin_x) == OK)
423          return;
424      } else {
425        if (mvderwin(w->win, begin_y, begin_x) == OK) {
426          /* now both we and the parent are dirty */
427          owl_window_dirty(w->parent);
428          owl_window_dirty(w);
429          return;
430        }
431      }
432    }
433    /* We don't have a window or failed to move it. Fine. Brute force. */
434    _owl_window_unrealize(w);
435    _owl_window_realize(w);
436  }
437}
438
439void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
440{
441  /* can't move the screen */
442  if (w->is_screen) {
443    begin_y = begin_x = 0;
444  }
445
446  if (w->nlines == nlines && w->ncols == ncols) {
447    /* moving is easier */
448    owl_window_move(w, begin_y, begin_x);
449    return;
450  }
451
452  /* window is shown, we must try to have a window at the end */
453  if (w->shown) {
454    /* resizing in ncurses is hard: give up do a unrealize/realize */
455    _owl_window_unrealize(w);
456  }
457  w->begin_y = begin_y;
458  w->begin_x = begin_x;
459  w->nlines = nlines;
460  w->ncols = ncols;
461  g_signal_emit(w, window_signals[RESIZED], 0);
462  if (w->shown) {
463    _owl_window_realize(w);
464  }
465}
466
467void owl_window_resize(owl_window *w, int nlines, int ncols)
468{
469  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
470}
471
472/** Stacking order **/
473
474void owl_window_top(owl_window *w) {
475  if (!w->pan) {
476    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
477    return;
478  }
479  top_panel(w->pan);
480}
481
482owl_window *owl_window_above(owl_window *w) {
483  PANEL *pan;
484
485  if (!w->pan) {
486    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
487    return NULL;
488  }
489  pan = panel_above(w->pan);
490  /* cast because panel_userptr pointlessly returns a const void * */
491  return pan ? (void*) panel_userptr(pan) : NULL;
492}
493
494owl_window *owl_window_below(owl_window *w) {
495  PANEL *pan;
496
497  if (!w->pan) {
498    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
499    return NULL;
500  }
501  pan = panel_below(w->pan);
502  /* cast because panel_userptr pointlessly returns a const void * */
503  return pan ? (void*) panel_userptr(pan) : NULL;
504}
Note: See TracBrowser for help on using the repository browser.