source: window.c @ f271129

release-1.9
Last change on this file since f271129 was f271129, checked in by Jason Gross <jgross@mit.edu>, 10 years ago
Fix up headers The additions to owl.h and some of the removals were done by Alejandro Sedeño <asedeno@mit.edu> in commit 77a0258b3919468fc9d7f7602588ac427ab36e6c. Notes: * I think owl.c lost the need for sys/time.h when we punted select() in favor of glib's main loop. * We don't actually need to include things like stdarg.h, stdio.h, glib/gstdio.h, glib-object.h. I think they get indirectly included via owl.h and/or glib.h. They're left in (or added in to) the files that use functions/types from them. * I'm not entirely sure what sys/socket.h is doing in message.c. It is there from the initial commit. I suspect it might have had something to do with the call to getnameinfo. message.c compiles without it, but http://pubs.opengroup.org/onlinepubs/009695399/functions/getnameinfo.html suggests that we're supposed to include it? *shrugs* I'm leaving it in, for now. (Rather, I'll leave one copy of the #include in.)
  • Property mode set to 100644
File size: 13.9 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
399static owl_window *_get_cursor(void)
400{
401  if (cursor_owner && owl_window_is_realized(cursor_owner))
402    return cursor_owner;
403  if (default_cursor && owl_window_is_realized(default_cursor))
404    return default_cursor;
405  return owl_window_get_screen();
406}
407
[449af72]408void owl_window_dirty(owl_window *w)
409{
[4811422]410  if (!_owl_window_should_realize(w))
[7cbef8c]411    return;
[449af72]412  if (!w->dirty) {
413    w->dirty = 1;
414    while (w && !w->dirty_subtree) {
415      w->dirty_subtree = 1;
416      w = w->parent;
417    }
418  }
419}
420
421void owl_window_dirty_children(owl_window *w)
422{
[6ad7bed]423  FuncOneArg ptr = (FuncOneArg)owl_window_dirty;
424  owl_window_children_foreach(w, first_arg_only, &ptr);
[449af72]425}
426
427static void _owl_window_redraw(owl_window *w)
428{
429  if (!w->dirty) return;
[4811422]430  _owl_window_realize(w);
[1f4ea57]431  if (w->win && !w->is_screen) {
[9379760]432    if (owl_window_is_subwin(w)) {
[f91767d]433      /* If a subwin, we might have gotten random touched lines from wsyncup or
434       * past drawing. That information is useless, so we discard it all */
435      untouchwin(w->win);
436    }
[7a6e6c7]437    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
[f91767d]438    wsyncup(w->win);
[449af72]439  }
440  w->dirty = 0;
441}
442
443static void _owl_window_redraw_subtree(owl_window *w)
444{
[b31f1c9]445  FuncOneArg ptr = (FuncOneArg)_owl_window_redraw_subtree;
[449af72]446  if (!w->dirty_subtree)
447    return;
448  _owl_window_redraw(w);
[6ad7bed]449  owl_window_children_foreach(w, first_arg_only, &ptr);
[438409f]450  w->dirty_subtree = 0;
[449af72]451}
452
453/*
454Redraw all the windows with scheduled redraws.
455NOTE: This function shouldn't be called outside the event loop
456*/
457void owl_window_redraw_scheduled(void)
458{
[f70a7a3]459  owl_window *cursor;
[5cc7e5e]460  owl_window *screen = owl_window_get_screen();
[f70a7a3]461
[5cc7e5e]462  if (!screen->dirty_subtree)
463    return;
464  _owl_window_redraw_subtree(screen);
[a57f87a]465  update_panels();
[f70a7a3]466  cursor = _get_cursor();
467  if (cursor && cursor->win) {
[f91767d]468    /* untouch it to avoid drawing; window should be clean now, but we must
469     * clean up in case there was junk left over on a subwin (cleaning up after
470     * subwin drawing isn't sufficient because a wsyncup messes up subwin
471     * ancestors */
[f70a7a3]472    untouchwin(cursor->win);
473    wnoutrefresh(cursor->win);
[f91767d]474  }
[5cc7e5e]475  doupdate();
[7a6e6c7]476}
477
[449af72]478/** Window position **/
479
480void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
481{
482  if (nlines)  *nlines  = w->nlines;
483  if (ncols)   *ncols   = w->ncols;
484  if (begin_y) *begin_y = w->begin_y;
485  if (begin_x) *begin_x = w->begin_x;
486}
487
[b0cbde4]488void owl_window_move(owl_window *w, int begin_y, int begin_x)
489{
[b9d04ad]490  /* It is possible to move a window efficiently with move_panel and mvderwin,
491   * but begy and begx are then wrong. Currently, this only effects the
492   * wnoutrefresh to move cursor. TODO: fix that and reinstate that
493   * optimization if it's worth the trouble */
[d15ea5f]494  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
[b0cbde4]495}
496
[449af72]497void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
498{
[840032d]499  int resized;
[449af72]500
[d15ea5f]501  if (w->nlines == nlines && w->ncols == ncols
502      && w->begin_y == begin_y && w->begin_x == begin_x) {
[449af72]503    return;
504  }
[840032d]505  resized = w->nlines != nlines || w->ncols != ncols;
[449af72]506
[d15ea5f]507  _owl_window_unrealize(w);
[678a505]508  w->begin_y = begin_y;
509  w->begin_x = begin_x;
510  w->nlines = nlines;
511  w->ncols = ncols;
[840032d]512  if (resized)
513    g_signal_emit(w, window_signals[RESIZED], 0);
[50031f0]514  if (w->shown) {
[d15ea5f]515    /* ncurses is screwy: give up and recreate windows at the right place */
[4811422]516    _owl_window_realize_later(w);
[449af72]517  }
518}
519
520void owl_window_resize(owl_window *w, int nlines, int ncols)
521{
522  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
523}
[4cc49bc]524
525/** Redrawing main loop hooks **/
526
527static bool _owl_window_should_redraw(void) {
528  return g.resizepending || owl_window_get_screen()->dirty_subtree;
529}
530
531static gboolean _owl_window_redraw_prepare(GSource *source, int *timeout) {
532  *timeout = -1;
533  return _owl_window_should_redraw();
534}
535
536static gboolean _owl_window_redraw_check(GSource *source) {
537  return _owl_window_should_redraw();
538}
539
540static gboolean _owl_window_redraw_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
541  owl_colorpair_mgr *cpmgr;
542
543  /* if a resize has been scheduled, deal with it */
544  owl_global_check_resize(&g);
545  /* update the terminal if we need to */
546  owl_window_redraw_scheduled();
[f97c1a6]547  /* On colorpair shortage, reset and redraw /everything/. NOTE: if we
548   * still overflow, this be useless work. With 8-colors, we get 64
549   * pairs. With 256-colors, we get 32768 pairs with ext-colors
550   * support and 256 otherwise. */
[4cc49bc]551  cpmgr = owl_global_get_colorpair_mgr(&g);
552  if (cpmgr->overflow) {
[f97c1a6]553    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
554                          owl_util_get_colorpairs());
[4cc49bc]555    owl_fmtext_reset_colorpairs(cpmgr);
556    owl_function_full_redisplay();
557    owl_window_redraw_scheduled();
558  }
559  return TRUE;
560}
561
562static GSourceFuncs redraw_funcs = {
563  _owl_window_redraw_prepare,
564  _owl_window_redraw_check,
565  _owl_window_redraw_dispatch,
566  NULL
567};
568
[6829afc]569CALLER_OWN GSource *owl_window_redraw_source_new(void)
[d427f08]570{
[4cc49bc]571  GSource *source;
572  source = g_source_new(&redraw_funcs, sizeof(GSource));
573  /* TODO: priority?? */
574  return source;
575}
Note: See TracBrowser for help on using the repository browser.