source: window.c @ 8c3cae0

release-1.9
Last change on this file since 8c3cae0 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
Line 
1#include "owl.h"
2
3struct _owl_window { /*noproto*/
4  GObject object;
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;
12  int shown : 1;
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
21enum {
22  REDRAW,
23  RESIZED,
24  LAST_SIGNAL
25};
26
27static guint window_signals[LAST_SIGNAL] = { 0 };
28
29static void owl_window_dispose(GObject *gobject);
30static void owl_window_finalize(GObject *gobject);
31
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
39static void _owl_window_realize(owl_window *w);
40static void _owl_window_unrealize(owl_window *w);
41
42static owl_window *cursor_owner;
43static owl_window *default_cursor;
44
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
55G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
56
57static void owl_window_class_init (OwlWindowClass *klass)
58{
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
65  klass->redraw = NULL;
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);
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
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) {
144    /* The screen is magical. It's 'shown', but the only mean of it going
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;
148    owl_window_show(screen);
149  }
150  return screen;
151}
152
153/** Creation and Destruction **/
154
155owl_window *owl_window_new(owl_window *parent)
156{
157  if (!parent)
158    parent = owl_window_get_screen();
159  return _owl_window_new(parent, 0, 0, 0, 0);
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
166  w = g_object_new (OWL_TYPE_WINDOW, NULL);
167  if (w == NULL) g_error("Failed to create owl_window instance");
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
185void owl_window_unlink(owl_window *w)
186{
187  /* make sure the window is unmapped first */
188  _owl_window_unrealize(w);
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;
197    w->parent = NULL;
198    g_object_unref (w);
199  }
200}
201
202static void _owl_window_link(owl_window *w, owl_window *parent)
203{
204  if (w->parent == parent)
205    return;
206
207  owl_window_unlink(w);
208  if (parent) {
209    w->parent = parent;
210    w->next = parent->child;
211    if (w->next)
212      w->next->prev = w;
213    parent->child = w;
214    g_object_ref (w);
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
229owl_window *owl_window_parent(owl_window *w)
230{
231  return w->parent;
232}
233
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
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);
255    w->win = stdscr;
256  } else {
257    /* Explicitly disallow realizing an unlinked non-root */
258    if (w->parent == NULL || w->parent->win == NULL)
259      return;
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    }
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
287void owl_window_show(owl_window *w)
288{
289  w->shown = 1;
290  _owl_window_realize(w);
291  if (w->pan)
292    show_panel(w->pan);
293}
294
295void owl_window_show_all(owl_window *w)
296{
297  FuncOneArg ptr = (FuncOneArg)owl_window_show;
298  owl_window_show(w);
299  owl_window_children_foreach(w, first_arg_only, &ptr);
300}
301
302void owl_window_hide(owl_window *w)
303{
304  /* you can't unmap the screen */
305  if (w->is_screen)
306    return;
307  w->shown = 0;
308  if (w->pan)
309    hide_panel(w->pan);
310  _owl_window_unrealize(w);
311}
312
313bool owl_window_is_shown(owl_window *w)
314{
315  return w->shown;
316}
317
318bool owl_window_is_realized(owl_window *w)
319{
320  return w->win != NULL;
321}
322
323bool owl_window_is_toplevel(owl_window *w)
324{
325  return w->pan != NULL;
326}
327
328bool owl_window_is_subwin(owl_window *w)
329{
330  return w->pan == NULL && !w->is_screen;
331}
332
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
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
346static void _owl_window_realize(owl_window *w)
347{
348  FuncOneArg ptr = (FuncOneArg)_owl_window_realize_later;
349  /* check if we can create a window */
350  if (owl_window_is_realized(w) || !_owl_window_should_realize(w))
351    return;
352  if (w->nlines <= 0 || w->ncols <= 0)
353    return;
354  _owl_window_create_curses(w);
355  if (w->win == NULL)
356    return;
357  /* schedule a repaint */
358  owl_window_dirty(w);
359  /* map the children */
360  owl_window_children_foreach(w, first_arg_only, &ptr);
361}
362
363static void _owl_window_unrealize(owl_window *w)
364{
365  FuncOneArg ptr = (FuncOneArg)_owl_window_unrealize;
366  if (w->win == NULL)
367    return;
368  /* unmap all the children */
369  owl_window_children_foreach(w, first_arg_only, &ptr);
370  _owl_window_destroy_curses(w);
371  w->dirty = w->dirty_subtree = 0;
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
379void owl_window_set_cursor(owl_window *w)
380{
381  if (cursor_owner)
382    g_object_remove_weak_pointer(G_OBJECT(cursor_owner), (gpointer*) &cursor_owner);
383  cursor_owner = w;
384  if (w)
385    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
386  owl_window_dirty(owl_window_get_screen());
387}
388
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);
396  owl_window_dirty(owl_window_get_screen());
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
408void owl_window_dirty(owl_window *w)
409{
410  if (!_owl_window_should_realize(w))
411    return;
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{
423  FuncOneArg ptr = (FuncOneArg)owl_window_dirty;
424  owl_window_children_foreach(w, first_arg_only, &ptr);
425}
426
427static void _owl_window_redraw(owl_window *w)
428{
429  if (!w->dirty) return;
430  _owl_window_realize(w);
431  if (w->win && !w->is_screen) {
432    if (owl_window_is_subwin(w)) {
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    }
437    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
438    wsyncup(w->win);
439  }
440  w->dirty = 0;
441}
442
443static void _owl_window_redraw_subtree(owl_window *w)
444{
445  FuncOneArg ptr = (FuncOneArg)_owl_window_redraw_subtree;
446  if (!w->dirty_subtree)
447    return;
448  _owl_window_redraw(w);
449  owl_window_children_foreach(w, first_arg_only, &ptr);
450  w->dirty_subtree = 0;
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{
459  owl_window *cursor;
460  owl_window *screen = owl_window_get_screen();
461
462  if (!screen->dirty_subtree)
463    return;
464  _owl_window_redraw_subtree(screen);
465  update_panels();
466  cursor = _get_cursor();
467  if (cursor && cursor->win) {
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 */
472    untouchwin(cursor->win);
473    wnoutrefresh(cursor->win);
474  }
475  doupdate();
476}
477
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
488void owl_window_move(owl_window *w, int begin_y, int begin_x)
489{
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 */
494  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
495}
496
497void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
498{
499  int resized;
500
501  if (w->nlines == nlines && w->ncols == ncols
502      && w->begin_y == begin_y && w->begin_x == begin_x) {
503    return;
504  }
505  resized = w->nlines != nlines || w->ncols != ncols;
506
507  _owl_window_unrealize(w);
508  w->begin_y = begin_y;
509  w->begin_x = begin_x;
510  w->nlines = nlines;
511  w->ncols = ncols;
512  if (resized)
513    g_signal_emit(w, window_signals[RESIZED], 0);
514  if (w->shown) {
515    /* ncurses is screwy: give up and recreate windows at the right place */
516    _owl_window_realize_later(w);
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}
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();
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. */
551  cpmgr = owl_global_get_colorpair_mgr(&g);
552  if (cpmgr->overflow) {
553    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
554                          owl_util_get_colorpairs());
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
569CALLER_OWN GSource *owl_window_redraw_source_new(void)
570{
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.