source: window.c @ 95b52d1

release-1.10release-1.8release-1.9
Last change on this file since 95b52d1 was d427f08, checked in by Nelson Elhage <nelhage@mit.edu>, 13 years ago
Use G_GNUC_WARN_UNUSED_RESULT Have gcc warn us when we ignore the result of a function that requires the caller to free the result, or an initilization function that can fail. This might help (slightly) with preventing leaks and segfaults. Additionally changed some functions that should never fail to not return values. (The owl_list_* functions changed only fail if list->size < 0, which we assume is not the case elsewhere.)
  • Property mode set to 100644
File size: 14.0 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 owl_window *cursor_owner;
45static owl_window *default_cursor;
46
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
57G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
58
59static void owl_window_class_init (OwlWindowClass *klass)
60{
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
67  klass->redraw = NULL;
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);
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
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) {
146    /* The screen is magical. It's 'shown', but the only mean of it going
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;
150    owl_window_show(screen);
151  }
152  return screen;
153}
154
155/** Creation and Destruction **/
156
157owl_window *owl_window_new(owl_window *parent)
158{
159  if (!parent)
160    parent = owl_window_get_screen();
161  return _owl_window_new(parent, 0, 0, 0, 0);
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
168  w = g_object_new (OWL_TYPE_WINDOW, NULL);
169  if (w == NULL) g_error("Failed to create owl_window instance");
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
187void owl_window_unlink(owl_window *w)
188{
189  /* make sure the window is unmapped first */
190  _owl_window_unrealize(w);
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;
199    w->parent = NULL;
200    g_object_unref (w);
201  }
202}
203
204static void _owl_window_link(owl_window *w, owl_window *parent)
205{
206  if (w->parent == parent)
207    return;
208
209  owl_window_unlink(w);
210  if (parent) {
211    w->parent = parent;
212    w->next = parent->child;
213    if (w->next)
214      w->next->prev = w;
215    parent->child = w;
216    g_object_ref (w);
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
231owl_window *owl_window_parent(owl_window *w)
232{
233  return w->parent;
234}
235
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
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);
257    w->win = stdscr;
258  } else {
259    /* Explicitly disallow realizing an unlinked non-root */
260    if (w->parent == NULL || w->parent->win == NULL)
261      return;
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    }
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
289void owl_window_show(owl_window *w)
290{
291  w->shown = 1;
292  _owl_window_realize(w);
293  if (w->pan)
294    show_panel(w->pan);
295}
296
297void owl_window_show_all(owl_window *w)
298{
299  FuncOneArg ptr = (FuncOneArg)owl_window_show;
300  owl_window_show(w);
301  owl_window_children_foreach(w, first_arg_only, &ptr);
302}
303
304void owl_window_hide(owl_window *w)
305{
306  /* you can't unmap the screen */
307  if (w->is_screen)
308    return;
309  w->shown = 0;
310  if (w->pan)
311    hide_panel(w->pan);
312  _owl_window_unrealize(w);
313}
314
315bool owl_window_is_shown(owl_window *w)
316{
317  return w->shown;
318}
319
320bool owl_window_is_realized(owl_window *w)
321{
322  return w->win != NULL;
323}
324
325bool owl_window_is_toplevel(owl_window *w)
326{
327  return w->pan != NULL;
328}
329
330bool owl_window_is_subwin(owl_window *w)
331{
332  return w->pan == NULL && !w->is_screen;
333}
334
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
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
348static void _owl_window_realize(owl_window *w)
349{
350  FuncOneArg ptr = (FuncOneArg)_owl_window_realize_later;
351  /* check if we can create a window */
352  if (owl_window_is_realized(w) || !_owl_window_should_realize(w))
353    return;
354  if (w->nlines <= 0 || w->ncols <= 0)
355    return;
356  _owl_window_create_curses(w);
357  if (w->win == NULL)
358    return;
359  /* schedule a repaint */
360  owl_window_dirty(w);
361  /* map the children */
362  owl_window_children_foreach(w, first_arg_only, &ptr);
363}
364
365static void _owl_window_unrealize(owl_window *w)
366{
367  FuncOneArg ptr = (FuncOneArg)_owl_window_unrealize;
368  if (w->win == NULL)
369    return;
370  /* unmap all the children */
371  owl_window_children_foreach(w, first_arg_only, &ptr);
372  _owl_window_destroy_curses(w);
373  w->dirty = w->dirty_subtree = 0;
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
381void owl_window_set_cursor(owl_window *w)
382{
383  if (cursor_owner)
384    g_object_remove_weak_pointer(G_OBJECT(cursor_owner), (gpointer*) &cursor_owner);
385  cursor_owner = w;
386  if (w)
387    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
388  owl_window_dirty(owl_window_get_screen());
389}
390
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);
398  owl_window_dirty(owl_window_get_screen());
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
410void owl_window_dirty(owl_window *w)
411{
412  if (!_owl_window_should_realize(w))
413    return;
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{
425  FuncOneArg ptr = (FuncOneArg)owl_window_dirty;
426  owl_window_children_foreach(w, first_arg_only, &ptr);
427}
428
429static void _owl_window_redraw(owl_window *w)
430{
431  if (!w->dirty) return;
432  _owl_window_realize(w);
433  if (w->win && !w->is_screen) {
434    if (owl_window_is_subwin(w)) {
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    }
439    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
440    wsyncup(w->win);
441  }
442  w->dirty = 0;
443}
444
445static void _owl_window_redraw_subtree(owl_window *w)
446{
447  FuncOneArg ptr = (FuncOneArg)_owl_window_redraw_subtree;
448  if (!w->dirty_subtree)
449    return;
450  _owl_window_redraw(w);
451  owl_window_children_foreach(w, first_arg_only, &ptr);
452  w->dirty_subtree = 0;
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{
461  owl_window *cursor;
462  owl_window *screen = owl_window_get_screen();
463
464  if (!screen->dirty_subtree)
465    return;
466  _owl_window_redraw_subtree(screen);
467  update_panels();
468  cursor = _get_cursor();
469  if (cursor && cursor->win) {
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 */
474    untouchwin(cursor->win);
475    wnoutrefresh(cursor->win);
476  }
477  doupdate();
478}
479
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
490void owl_window_move(owl_window *w, int begin_y, int begin_x)
491{
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 */
496  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
497}
498
499void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
500{
501  int resized;
502
503  if (w->nlines == nlines && w->ncols == ncols
504      && w->begin_y == begin_y && w->begin_x == begin_x) {
505    return;
506  }
507  resized = w->nlines != nlines || w->ncols != ncols;
508
509  _owl_window_unrealize(w);
510  w->begin_y = begin_y;
511  w->begin_x = begin_x;
512  w->nlines = nlines;
513  w->ncols = ncols;
514  if (resized)
515    g_signal_emit(w, window_signals[RESIZED], 0);
516  if (w->shown) {
517    /* ncurses is screwy: give up and recreate windows at the right place */
518    _owl_window_realize_later(w);
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}
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();
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. */
553  cpmgr = owl_global_get_colorpair_mgr(&g);
554  if (cpmgr->overflow) {
555    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
556                          owl_util_get_colorpairs());
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
571G_GNUC_WARN_UNUSED_RESULT GSource *owl_window_redraw_source_new(void)
572{
573  GSource *source;
574  source = g_source_new(&redraw_funcs, sizeof(GSource));
575  /* TODO: priority?? */
576  return source;
577}
Note: See TracBrowser for help on using the repository browser.