source: window.c

release-1.9
Last change on this file was 3442788, checked in by David Benjamin <davidben@mit.edu>, 6 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
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(bool *is_default)
400{
401  *is_default = false;
402  if (cursor_owner && owl_window_is_realized(cursor_owner))
403    return cursor_owner;
404  *is_default = true;
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 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
460static void _owl_window_redraw_subtree(owl_window *w)
461{
462  FuncOneArg ptr = (FuncOneArg)_owl_window_redraw_subtree;
463
464  if (!w->dirty_subtree)
465    return;
466
467  _owl_window_redraw(w);
468  owl_window_children_foreach(w, first_arg_only, &ptr);
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  }
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{
488  owl_window *cursor;
489  owl_window *screen = owl_window_get_screen();
490  bool default_cursor;
491
492  if (!screen->dirty_subtree)
493    return;
494  _owl_window_redraw_subtree(screen);
495  update_panels();
496  cursor = _get_cursor(&default_cursor);
497  if (cursor && cursor->win) {
498    /* If supported, hide the default cursor. */
499    curs_set(default_cursor ? 0 : 1);
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 */
504    untouchwin(cursor->win);
505    wnoutrefresh(cursor->win);
506  }
507  doupdate();
508}
509
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
520void owl_window_move(owl_window *w, int begin_y, int begin_x)
521{
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 */
526  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
527}
528
529void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
530{
531  int resized;
532
533  if (w->nlines == nlines && w->ncols == ncols
534      && w->begin_y == begin_y && w->begin_x == begin_x) {
535    return;
536  }
537  resized = w->nlines != nlines || w->ncols != ncols;
538
539  _owl_window_unrealize(w);
540  w->begin_y = begin_y;
541  w->begin_x = begin_x;
542  w->nlines = nlines;
543  w->ncols = ncols;
544  if (resized)
545    g_signal_emit(w, window_signals[RESIZED], 0);
546  if (w->shown) {
547    /* ncurses is screwy: give up and recreate windows at the right place */
548    _owl_window_realize_later(w);
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}
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();
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. */
583  cpmgr = owl_global_get_colorpair_mgr(&g);
584  if (cpmgr->overflow) {
585    owl_function_debugmsg("colorpairs: used all %d pairs; reset pairs and redraw.",
586                          owl_util_get_colorpairs());
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
601CALLER_OWN GSource *owl_window_redraw_source_new(void)
602{
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.