source: window.c @ 5175cf8

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