source: window.c @ 697221f

release-1.10release-1.8release-1.9
Last change on this file since 697221f was f97c1a6, checked in by David Benjamin <davidben@mit.edu>, 13 years ago
Merge branch 'g_main_loop' The logic in owl_select_prune_bad_fds still needs to be reimplemented. Conflicts: configure.ac owl.c select.c
  • Property mode set to 100644
File size: 14.0 KB
RevLine 
[449af72]1#include "owl.h"
2
3#include <assert.h>
4
5struct _owl_window { /*noproto*/
[053f751]6  GObject object;
[449af72]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;
[50031f0]14  int shown : 1;
[449af72]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
[7a6e6c7]23enum {
24  REDRAW,
25  RESIZED,
26  LAST_SIGNAL
27};
28
29static guint window_signals[LAST_SIGNAL] = { 0 };
30
[053f751]31static void owl_window_dispose(GObject *gobject);
32static void owl_window_finalize(GObject *gobject);
33
[449af72]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
[46e2e56]41static void _owl_window_realize(owl_window *w);
42static void _owl_window_unrealize(owl_window *w);
[449af72]43
[84a4aca]44static owl_window *cursor_owner;
[f70a7a3]45static owl_window *default_cursor;
[84a4aca]46
[6ad7bed]47/* clang gets upset about the glib argument chopping hack because it manages to
48 * inline owl_window_children_foreach. user_data should be a pointer to a
49 * FuncOneArg. */
50typedef void (*FuncOneArg)(void *);
51static void first_arg_only(gpointer data, gpointer user_data)
52{
53  FuncOneArg *func = user_data;
54  (*func)(data);
55}
56
[053f751]57G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
58
59static void owl_window_class_init (OwlWindowClass *klass)
60{
[7a6e6c7]61  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
62
63  /* Set up the vtabl */
64  gobject_class->dispose = owl_window_dispose;
65  gobject_class->finalize = owl_window_finalize;
66
[f91767d]67  klass->redraw = NULL;
[7a6e6c7]68  klass->resized = NULL;
69
70  /* Create the signals, remember IDs */
71  window_signals[REDRAW] =
72    g_signal_new("redraw",
73                 G_TYPE_FROM_CLASS(gobject_class),
74                 G_SIGNAL_RUN_CLEANUP,
75                 G_STRUCT_OFFSET(OwlWindowClass, redraw),
76                 NULL, NULL,
77                 g_cclosure_marshal_VOID__POINTER,
78                 G_TYPE_NONE,
79                 1,
80                 G_TYPE_POINTER, NULL);
81
82  /* TODO: maybe type should be VOID__INT_INT_INT_INT; will need to generate a
83   * marshaller */
84  window_signals[RESIZED] =
85    g_signal_new("resized",
86                 G_TYPE_FROM_CLASS(gobject_class),
87                 G_SIGNAL_RUN_FIRST,
88                 G_STRUCT_OFFSET(OwlWindowClass, resized),
89                 NULL, NULL,
90                 g_cclosure_marshal_VOID__VOID,
91                 G_TYPE_NONE,
92                 0,
93                 NULL);
[053f751]94}
95
96static void owl_window_dispose (GObject *object)
97{
98  owl_window *w = OWL_WINDOW (object);
99
100  /* Unmap the window */
101  owl_window_hide (w);
102
103  /* Unlink and unref all children */
104  while (w->child) {
105    owl_window *child = w->child;
106    owl_window_unlink (child);
107  }
108
109  /* Remove from hierarchy */
110  owl_window_unlink (w);
111
112  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
113}
114
115static void owl_window_finalize (GObject *object)
116{
117  owl_window *w = OWL_WINDOW(object);
118
119  if (w->pan) {
120    del_panel(w->pan);
121    w->pan = NULL;
122  }
123
124  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
125}
126
127static void owl_window_init (owl_window *w)
128{
129}
130
[449af72]131/** singletons **/
132
133static WINDOW *_dummy_window(void)
134{
135  static WINDOW *dummy = NULL;
136  if (!dummy) {
137    dummy = newwin(1, 1, 0, 0);
138  }
139  return dummy;
140}
141
142owl_window *owl_window_get_screen(void)
143{
144  static owl_window *screen = NULL;
145  if (!screen) {
[50031f0]146    /* The screen is magical. It's 'shown', but the only mean of it going
[449af72]147     * invisible is if we're tore down curses (i.e. screen resize) */
148    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
149    screen->is_screen = 1;
[402ed3d3]150    owl_window_show(screen);
[449af72]151  }
152  return screen;
153}
154
155/** Creation and Destruction **/
156
[d106110]157owl_window *owl_window_new(owl_window *parent)
[449af72]158{
159  if (!parent)
160    parent = owl_window_get_screen();
[d106110]161  return _owl_window_new(parent, 0, 0, 0, 0);
[449af72]162}
163
164static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
165{
166  owl_window *w;
167
[053f751]168  w = g_object_new (OWL_TYPE_WINDOW, NULL);
169  if (w == NULL) g_error("Failed to create owl_window instance");
[449af72]170
171  w->nlines = nlines;
172  w->ncols = ncols;
173  w->begin_y = begin_y;
174  w->begin_x = begin_x;
175
176  _owl_window_link(w, parent);
177  if (parent && parent->is_screen) {
178    w->pan = new_panel(_dummy_window());
179    set_panel_userptr(w->pan, w);
180  }
181
182  return w;
183}
184
185/** Hierarchy **/
186
[3da1f4f]187void owl_window_unlink(owl_window *w)
[449af72]188{
[ec4ccfc]189  /* make sure the window is unmapped first */
[46e2e56]190  _owl_window_unrealize(w);
[449af72]191  /* unlink parent/child information */
192  if (w->parent) {
193    if (w->prev)
194      w->prev->next = w->next;
195    if (w->next)
196      w->next->prev = w->prev;
197    if (w->parent->child == w)
198      w->parent->child = w->next;
[933aa7d]199    w->parent = NULL;
[053f751]200    g_object_unref (w);
[449af72]201  }
202}
203
204static void _owl_window_link(owl_window *w, owl_window *parent)
205{
206  if (w->parent == parent)
207    return;
208
[933aa7d]209  owl_window_unlink(w);
[449af72]210  if (parent) {
[933aa7d]211    w->parent = parent;
[449af72]212    w->next = parent->child;
[5ca3fb7]213    if (w->next)
214      w->next->prev = w;
[449af72]215    parent->child = w;
[053f751]216    g_object_ref (w);
[449af72]217  }
218}
219
220/* mimic g_list_foreach for consistency */
221void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
222{
223  owl_window *w;
224  for (w = parent->child;
225       w != NULL;
226       w = w->next) {
227    func(w, user_data);
228  }
229}
230
[1aa4cc4]231owl_window *owl_window_parent(owl_window *w)
[b6cb985]232{
233  return w->parent;
234}
235
[1aa4cc4]236owl_window *owl_window_first_child(owl_window *w)
237{
238  return w->child;
239}
240
241owl_window *owl_window_next_sibling(owl_window *w)
242{
243  return w->next;
244}
245
246owl_window *owl_window_previous_sibling(owl_window *w)
247{
248  return w->prev;
249}
250
[449af72]251/** Internal window management **/
252
253static void _owl_window_create_curses(owl_window *w)
254{
255  if (w->is_screen) {
256    resizeterm(w->nlines, w->ncols);
[2dfccc7]257    w->win = stdscr;
[449af72]258  } else {
[aab7af1]259    /* Explicitly disallow realizing an unlinked non-root */
[fe49685]260    if (w->parent == NULL || w->parent->win == NULL)
261      return;
[aab7af1]262    if (w->pan) {
263      w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
264      replace_panel(w->pan, w->win);
265    } else {
266      w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
267    }
[449af72]268  }
269}
270
271static void _owl_window_destroy_curses(owl_window *w)
272{
273  if (w->is_screen) {
274    /* don't deallocate the dummy */
275    w->win = NULL;
276  } else {
277    if (w->pan) {
278      /* panels assume their windows always exist, so we put in a fake one */
279      replace_panel(w->pan, _dummy_window());
280    }
281    if (w->win) {
282      /* and destroy our own window */
283      delwin(w->win);
284      w->win = NULL;
285    }
286  }
287}
288
[402ed3d3]289void owl_window_show(owl_window *w)
[449af72]290{
[50031f0]291  w->shown = 1;
[46e2e56]292  _owl_window_realize(w);
[449af72]293  if (w->pan)
294    show_panel(w->pan);
[402ed3d3]295}
296
297void owl_window_show_all(owl_window *w)
298{
[6ad7bed]299  FuncOneArg ptr = (FuncOneArg)owl_window_show;
[b31f1c9]300  owl_window_show(w);
[6ad7bed]301  owl_window_children_foreach(w, first_arg_only, &ptr);
[449af72]302}
303
[50031f0]304void owl_window_hide(owl_window *w)
[449af72]305{
306  /* you can't unmap the screen */
307  if (w->is_screen)
308    return;
[50031f0]309  w->shown = 0;
[449af72]310  if (w->pan)
311    hide_panel(w->pan);
[46e2e56]312  _owl_window_unrealize(w);
[449af72]313}
314
[38e2250]315bool owl_window_is_shown(owl_window *w)
[449af72]316{
[50031f0]317  return w->shown;
[449af72]318}
319
[38e2250]320bool owl_window_is_realized(owl_window *w)
[449af72]321{
322  return w->win != NULL;
323}
324
[38e2250]325bool owl_window_is_toplevel(owl_window *w)
[449af72]326{
327  return w->pan != NULL;
328}
329
[38e2250]330bool owl_window_is_subwin(owl_window *w)
[9379760]331{
332  return w->pan == NULL && !w->is_screen;
333}
334
[5f0bcde]335static bool _owl_window_should_realize(owl_window *w)
336{
337  return owl_window_is_shown(w) &&
338    (!w->parent || owl_window_is_realized(w->parent));
339}
340
[4811422]341static void _owl_window_realize_later(owl_window *w)
342{
343  if (owl_window_is_realized(w) || !_owl_window_should_realize(w))
344    return;
345  owl_window_dirty(w);
346}
347
[46e2e56]348static void _owl_window_realize(owl_window *w)
[449af72]349{
[b31f1c9]350  FuncOneArg ptr = (FuncOneArg)_owl_window_realize_later;
[449af72]351  /* check if we can create a window */
[5f0bcde]352  if (owl_window_is_realized(w) || !_owl_window_should_realize(w))
[449af72]353    return;
[f041595]354  if (w->nlines <= 0 || w->ncols <= 0)
355    return;
[449af72]356  _owl_window_create_curses(w);
[fe49685]357  if (w->win == NULL)
358    return;
[449af72]359  /* schedule a repaint */
360  owl_window_dirty(w);
[fe49685]361  /* map the children */
[6ad7bed]362  owl_window_children_foreach(w, first_arg_only, &ptr);
[449af72]363}
364
[46e2e56]365static void _owl_window_unrealize(owl_window *w)
[449af72]366{
[b31f1c9]367  FuncOneArg ptr = (FuncOneArg)_owl_window_unrealize;
[449af72]368  if (w->win == NULL)
369    return;
370  /* unmap all the children */
[6ad7bed]371  owl_window_children_foreach(w, first_arg_only, &ptr);
[449af72]372  _owl_window_destroy_curses(w);
[cb5a9f3]373  w->dirty = w->dirty_subtree = 0;
[449af72]374  /* subwins leave a mess in the parent; dirty it */
375  if (w->parent)
376    owl_window_dirty(w->parent);
377}
378
379/** Painting and book-keeping **/
380
[84a4aca]381void owl_window_set_cursor(owl_window *w)
382{
[cc36f27]383  if (cursor_owner)
384    g_object_remove_weak_pointer(G_OBJECT(cursor_owner), (gpointer*) &cursor_owner);
[84a4aca]385  cursor_owner = w;
[f70a7a3]386  if (w)
387    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
[5cc7e5e]388  owl_window_dirty(owl_window_get_screen());
[84a4aca]389}
390
[f70a7a3]391void owl_window_set_default_cursor(owl_window *w)
392{
393  if (default_cursor)
394    g_object_remove_weak_pointer(G_OBJECT(default_cursor), (gpointer*) &default_cursor);
395  default_cursor = w;
396  if (w)
397    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &default_cursor);
[5cc7e5e]398  owl_window_dirty(owl_window_get_screen());
[f70a7a3]399}
400
401static owl_window *_get_cursor(void)
402{
403  if (cursor_owner && owl_window_is_realized(cursor_owner))
404    return cursor_owner;
405  if (default_cursor && owl_window_is_realized(default_cursor))
406    return default_cursor;
407  return owl_window_get_screen();
408}
409
[449af72]410void owl_window_dirty(owl_window *w)
411{
[4811422]412  if (!_owl_window_should_realize(w))
[7cbef8c]413    return;
[449af72]414  if (!w->dirty) {
415    w->dirty = 1;
416    while (w && !w->dirty_subtree) {
417      w->dirty_subtree = 1;
418      w = w->parent;
419    }
420  }
421}
422
423void owl_window_dirty_children(owl_window *w)
424{
[6ad7bed]425  FuncOneArg ptr = (FuncOneArg)owl_window_dirty;
426  owl_window_children_foreach(w, first_arg_only, &ptr);
[449af72]427}
428
429static void _owl_window_redraw(owl_window *w)
430{
431  if (!w->dirty) return;
[4811422]432  _owl_window_realize(w);
[1f4ea57]433  if (w->win && !w->is_screen) {
[9379760]434    if (owl_window_is_subwin(w)) {
[f91767d]435      /* If a subwin, we might have gotten random touched lines from wsyncup or
436       * past drawing. That information is useless, so we discard it all */
437      untouchwin(w->win);
438    }
[7a6e6c7]439    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
[f91767d]440    wsyncup(w->win);
[449af72]441  }
442  w->dirty = 0;
443}
444
445static void _owl_window_redraw_subtree(owl_window *w)
446{
[b31f1c9]447  FuncOneArg ptr = (FuncOneArg)_owl_window_redraw_subtree;
[449af72]448  if (!w->dirty_subtree)
449    return;
450  _owl_window_redraw(w);
[6ad7bed]451  owl_window_children_foreach(w, first_arg_only, &ptr);
[438409f]452  w->dirty_subtree = 0;
[449af72]453}
454
455/*
456Redraw all the windows with scheduled redraws.
457NOTE: This function shouldn't be called outside the event loop
458*/
459void owl_window_redraw_scheduled(void)
460{
[f70a7a3]461  owl_window *cursor;
[5cc7e5e]462  owl_window *screen = owl_window_get_screen();
[f70a7a3]463
[5cc7e5e]464  if (!screen->dirty_subtree)
465    return;
466  _owl_window_redraw_subtree(screen);
[a57f87a]467  update_panels();
[f70a7a3]468  cursor = _get_cursor();
469  if (cursor && cursor->win) {
[f91767d]470    /* untouch it to avoid drawing; window should be clean now, but we must
471     * clean up in case there was junk left over on a subwin (cleaning up after
472     * subwin drawing isn't sufficient because a wsyncup messes up subwin
473     * ancestors */
[f70a7a3]474    untouchwin(cursor->win);
475    wnoutrefresh(cursor->win);
[f91767d]476  }
[5cc7e5e]477  doupdate();
[7a6e6c7]478}
479
[449af72]480/** Window position **/
481
482void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
483{
484  if (nlines)  *nlines  = w->nlines;
485  if (ncols)   *ncols   = w->ncols;
486  if (begin_y) *begin_y = w->begin_y;
487  if (begin_x) *begin_x = w->begin_x;
488}
489
[b0cbde4]490void owl_window_move(owl_window *w, int begin_y, int begin_x)
491{
[b9d04ad]492  /* It is possible to move a window efficiently with move_panel and mvderwin,
493   * but begy and begx are then wrong. Currently, this only effects the
494   * wnoutrefresh to move cursor. TODO: fix that and reinstate that
495   * optimization if it's worth the trouble */
[d15ea5f]496  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
[b0cbde4]497}
498
[449af72]499void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
500{
[840032d]501  int resized;
[449af72]502
[d15ea5f]503  if (w->nlines == nlines && w->ncols == ncols
504      && w->begin_y == begin_y && w->begin_x == begin_x) {
[449af72]505    return;
506  }
[840032d]507  resized = w->nlines != nlines || w->ncols != ncols;
[449af72]508
[d15ea5f]509  _owl_window_unrealize(w);
[678a505c]510  w->begin_y = begin_y;
511  w->begin_x = begin_x;
512  w->nlines = nlines;
513  w->ncols = ncols;
[840032d]514  if (resized)
515    g_signal_emit(w, window_signals[RESIZED], 0);
[50031f0]516  if (w->shown) {
[d15ea5f]517    /* ncurses is screwy: give up and recreate windows at the right place */
[4811422]518    _owl_window_realize_later(w);
[449af72]519  }
520}
521
522void owl_window_resize(owl_window *w, int nlines, int ncols)
523{
524  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
525}
[4cc49bc]526
527/** Redrawing main loop hooks **/
528
529static bool _owl_window_should_redraw(void) {
530  return g.resizepending || owl_window_get_screen()->dirty_subtree;
531}
532
533static gboolean _owl_window_redraw_prepare(GSource *source, int *timeout) {
534  *timeout = -1;
535  return _owl_window_should_redraw();
536}
537
538static gboolean _owl_window_redraw_check(GSource *source) {
539  return _owl_window_should_redraw();
540}
541
542static gboolean _owl_window_redraw_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
543  owl_colorpair_mgr *cpmgr;
544
545  /* if a resize has been scheduled, deal with it */
546  owl_global_check_resize(&g);
547  /* update the terminal if we need to */
548  owl_window_redraw_scheduled();
[f97c1a6]549  /* On colorpair shortage, reset and redraw /everything/. NOTE: if we
550   * still overflow, this be useless work. With 8-colors, we get 64
551   * pairs. With 256-colors, we get 32768 pairs with ext-colors
552   * support and 256 otherwise. */
[4cc49bc]553  cpmgr = owl_global_get_colorpair_mgr(&g);
554  if (cpmgr->overflow) {
[f97c1a6]555    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
556                          owl_util_get_colorpairs());
[4cc49bc]557    owl_fmtext_reset_colorpairs(cpmgr);
558    owl_function_full_redisplay();
559    owl_window_redraw_scheduled();
560  }
561  return TRUE;
562}
563
564static GSourceFuncs redraw_funcs = {
565  _owl_window_redraw_prepare,
566  _owl_window_redraw_check,
567  _owl_window_redraw_dispatch,
568  NULL
569};
570
571GSource *owl_window_redraw_source_new(void) {
572  GSource *source;
573  source = g_source_new(&redraw_funcs, sizeof(GSource));
574  /* TODO: priority?? */
575  return source;
576}
Note: See TracBrowser for help on using the repository browser.