source: window.c @ e8128c5

release-1.10release-1.7release-1.8release-1.9
Last change on this file since e8128c5 was f70a7a3, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Add a default cursor and fallback finally on the screen Also get rid of useless prototype.
  • Property mode set to 100644
File size: 12.9 KB
RevLine 
[449af72]1#include "owl.h"
2
3#include <assert.h>
4
5struct _owl_window { /*noproto*/
[053f751]6  GObject object;
[449af72]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;
[50031f0]14  int shown : 1;
[449af72]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
[7a6e6c7]23enum {
24  REDRAW,
25  RESIZED,
26  LAST_SIGNAL
27};
28
29static guint window_signals[LAST_SIGNAL] = { 0 };
30
[053f751]31static void owl_window_dispose(GObject *gobject);
32static void owl_window_finalize(GObject *gobject);
33
[449af72]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
[46e2e56]41static void _owl_window_realize(owl_window *w);
42static void _owl_window_unrealize(owl_window *w);
[449af72]43
[84a4aca]44static owl_window *cursor_owner;
[f70a7a3]45static owl_window *default_cursor;
[84a4aca]46
[053f751]47G_DEFINE_TYPE (OwlWindow, owl_window, G_TYPE_OBJECT)
48
49static void owl_window_class_init (OwlWindowClass *klass)
50{
[7a6e6c7]51  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
52
53  /* Set up the vtabl */
54  gobject_class->dispose = owl_window_dispose;
55  gobject_class->finalize = owl_window_finalize;
56
[f91767d]57  klass->redraw = NULL;
[7a6e6c7]58  klass->resized = NULL;
59
60  /* Create the signals, remember IDs */
61  window_signals[REDRAW] =
62    g_signal_new("redraw",
63                 G_TYPE_FROM_CLASS(gobject_class),
64                 G_SIGNAL_RUN_CLEANUP,
65                 G_STRUCT_OFFSET(OwlWindowClass, redraw),
66                 NULL, NULL,
67                 g_cclosure_marshal_VOID__POINTER,
68                 G_TYPE_NONE,
69                 1,
70                 G_TYPE_POINTER, NULL);
71
72  /* TODO: maybe type should be VOID__INT_INT_INT_INT; will need to generate a
73   * marshaller */
74  window_signals[RESIZED] =
75    g_signal_new("resized",
76                 G_TYPE_FROM_CLASS(gobject_class),
77                 G_SIGNAL_RUN_FIRST,
78                 G_STRUCT_OFFSET(OwlWindowClass, resized),
79                 NULL, NULL,
80                 g_cclosure_marshal_VOID__VOID,
81                 G_TYPE_NONE,
82                 0,
83                 NULL);
[053f751]84}
85
86static void owl_window_dispose (GObject *object)
87{
88  owl_window *w = OWL_WINDOW (object);
89
90  /* Unmap the window */
91  owl_window_hide (w);
92
93  /* Unlink and unref all children */
94  while (w->child) {
95    owl_window *child = w->child;
96    owl_window_unlink (child);
97  }
98
99  /* Remove from hierarchy */
100  owl_window_unlink (w);
101
102  G_OBJECT_CLASS (owl_window_parent_class)->dispose (object);
103}
104
105static void owl_window_finalize (GObject *object)
106{
107  owl_window *w = OWL_WINDOW(object);
108
109  if (w->pan) {
110    del_panel(w->pan);
111    w->pan = NULL;
112  }
113
114  G_OBJECT_CLASS (owl_window_parent_class)->finalize (object);
115}
116
117static void owl_window_init (owl_window *w)
118{
119}
120
[449af72]121/** singletons **/
122
123static WINDOW *_dummy_window(void)
124{
125  static WINDOW *dummy = NULL;
126  if (!dummy) {
127    dummy = newwin(1, 1, 0, 0);
128  }
129  return dummy;
130}
131
132owl_window *owl_window_get_screen(void)
133{
134  static owl_window *screen = NULL;
135  if (!screen) {
[50031f0]136    /* The screen is magical. It's 'shown', but the only mean of it going
[449af72]137     * invisible is if we're tore down curses (i.e. screen resize) */
138    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
139    screen->is_screen = 1;
[402ed3d3]140    owl_window_show(screen);
[449af72]141  }
142  return screen;
143}
144
145/** Creation and Destruction **/
146
[d106110]147owl_window *owl_window_new(owl_window *parent)
[449af72]148{
149  if (!parent)
150    parent = owl_window_get_screen();
[d106110]151  return _owl_window_new(parent, 0, 0, 0, 0);
[449af72]152}
153
154static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
155{
156  owl_window *w;
157
[053f751]158  w = g_object_new (OWL_TYPE_WINDOW, NULL);
159  if (w == NULL) g_error("Failed to create owl_window instance");
[449af72]160
161  w->nlines = nlines;
162  w->ncols = ncols;
163  w->begin_y = begin_y;
164  w->begin_x = begin_x;
165
166  _owl_window_link(w, parent);
167  if (parent && parent->is_screen) {
168    w->pan = new_panel(_dummy_window());
169    set_panel_userptr(w->pan, w);
170  }
171
172  return w;
173}
174
175/** Hierarchy **/
176
[3da1f4f]177void owl_window_unlink(owl_window *w)
[449af72]178{
[ec4ccfc]179  /* make sure the window is unmapped first */
[46e2e56]180  _owl_window_unrealize(w);
[449af72]181  /* unlink parent/child information */
182  if (w->parent) {
183    if (w->prev)
184      w->prev->next = w->next;
185    if (w->next)
186      w->next->prev = w->prev;
187    if (w->parent->child == w)
188      w->parent->child = w->next;
[933aa7d]189    w->parent = NULL;
[053f751]190    g_object_unref (w);
[449af72]191  }
192}
193
194static void _owl_window_link(owl_window *w, owl_window *parent)
195{
196  if (w->parent == parent)
197    return;
198
[933aa7d]199  owl_window_unlink(w);
[449af72]200  if (parent) {
[933aa7d]201    w->parent = parent;
[449af72]202    w->next = parent->child;
203    parent->child = w;
[053f751]204    g_object_ref (w);
[449af72]205  }
206}
207
208/* mimic g_list_foreach for consistency */
209void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
210{
211  owl_window *w;
212  for (w = parent->child;
213       w != NULL;
214       w = w->next) {
215    func(w, user_data);
216  }
217}
218
[b6cb985]219owl_window *owl_window_get_parent(owl_window *w)
220{
221  return w->parent;
222}
223
[449af72]224/** Internal window management **/
225
226static void _owl_window_create_curses(owl_window *w)
227{
228  if (w->is_screen) {
229    resizeterm(w->nlines, w->ncols);
[2dfccc7]230    w->win = stdscr;
[449af72]231  } else if (w->pan) {
232    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
233    replace_panel(w->pan, w->win);
234  } else {
[fe49685]235    if (w->parent == NULL || w->parent->win == NULL)
236      return;
[449af72]237    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
238  }
239}
240
241static void _owl_window_destroy_curses(owl_window *w)
242{
243  if (w->is_screen) {
244    /* don't deallocate the dummy */
245    w->win = NULL;
246  } else {
247    if (w->pan) {
248      /* panels assume their windows always exist, so we put in a fake one */
249      replace_panel(w->pan, _dummy_window());
250    }
251    if (w->win) {
252      /* and destroy our own window */
253      delwin(w->win);
254      w->win = NULL;
255    }
256  }
257}
258
[402ed3d3]259void owl_window_show(owl_window *w)
[449af72]260{
[50031f0]261  w->shown = 1;
[46e2e56]262  _owl_window_realize(w);
[449af72]263  if (w->pan)
264    show_panel(w->pan);
[402ed3d3]265}
266
267void owl_window_show_all(owl_window *w)
268{
269  owl_window_show(w);
[7a70e26]270  owl_window_children_foreach(w, (GFunc)owl_window_show, 0);
[449af72]271}
272
[50031f0]273void owl_window_hide(owl_window *w)
[449af72]274{
275  /* you can't unmap the screen */
276  if (w->is_screen)
277    return;
[50031f0]278  w->shown = 0;
[449af72]279  if (w->pan)
280    hide_panel(w->pan);
[46e2e56]281  _owl_window_unrealize(w);
[449af72]282}
283
[50031f0]284int owl_window_is_shown(owl_window *w)
[449af72]285{
[50031f0]286  return w->shown;
[449af72]287}
288
[ce7c6c3]289int owl_window_is_realized(owl_window *w)
[449af72]290{
291  return w->win != NULL;
292}
293
294int owl_window_is_toplevel(owl_window *w)
295{
296  return w->pan != NULL;
297}
298
[46e2e56]299static void _owl_window_realize(owl_window *w)
[449af72]300{
301  /* check if we can create a window */
302  if ((w->parent && w->parent->win == NULL)
[50031f0]303      || !w->shown
[449af72]304      || w->win != NULL)
305    return;
[f041595]306  if (w->nlines <= 0 || w->ncols <= 0)
307    return;
[449af72]308  _owl_window_create_curses(w);
[fe49685]309  if (w->win == NULL)
310    return;
[449af72]311  /* schedule a repaint */
312  owl_window_dirty(w);
[fe49685]313  /* map the children */
[7a70e26]314  owl_window_children_foreach(w, (GFunc)_owl_window_realize, 0);
[449af72]315}
316
[46e2e56]317static void _owl_window_unrealize(owl_window *w)
[449af72]318{
319  if (w->win == NULL)
320    return;
321  /* unmap all the children */
[7a70e26]322  owl_window_children_foreach(w, (GFunc)_owl_window_unrealize, 0);
[449af72]323  _owl_window_destroy_curses(w);
[cb5a9f3]324  w->dirty = w->dirty_subtree = 0;
[449af72]325  /* subwins leave a mess in the parent; dirty it */
326  if (w->parent)
327    owl_window_dirty(w->parent);
328}
329
330/** Painting and book-keeping **/
331
[84a4aca]332void owl_window_set_cursor(owl_window *w)
333{
[cc36f27]334  if (cursor_owner)
335    g_object_remove_weak_pointer(G_OBJECT(cursor_owner), (gpointer*) &cursor_owner);
[84a4aca]336  cursor_owner = w;
[f70a7a3]337  if (w)
338    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &cursor_owner);
[a5a9572]339  owl_global_set_needrefresh(&g);
[84a4aca]340}
341
[f70a7a3]342void owl_window_set_default_cursor(owl_window *w)
343{
344  if (default_cursor)
345    g_object_remove_weak_pointer(G_OBJECT(default_cursor), (gpointer*) &default_cursor);
346  default_cursor = w;
347  if (w)
348    g_object_add_weak_pointer(G_OBJECT(w), (gpointer*) &default_cursor);
349  owl_global_set_needrefresh(&g);
350}
351
352static owl_window *_get_cursor(void)
353{
354  if (cursor_owner && owl_window_is_realized(cursor_owner))
355    return cursor_owner;
356  if (default_cursor && owl_window_is_realized(default_cursor))
357    return default_cursor;
358  return owl_window_get_screen();
359}
360
[449af72]361void owl_window_dirty(owl_window *w)
362{
363  /* don't put the screen on this list; pointless */
364  if (w->is_screen)
365    return;
[7cbef8c]366  if (!owl_window_is_realized(w))
367    return;
[449af72]368  if (!w->dirty) {
369    w->dirty = 1;
370    while (w && !w->dirty_subtree) {
371      w->dirty_subtree = 1;
372      w = w->parent;
373    }
374    owl_global_set_needrefresh(&g);
375  }
376}
377
378void owl_window_dirty_children(owl_window *w)
379{
[7a70e26]380  owl_window_children_foreach(w, (GFunc)owl_window_dirty, 0);
[449af72]381}
382
383static void _owl_window_redraw(owl_window *w)
384{
385  if (!w->dirty) return;
[7a6e6c7]386  if (w->win) {
[f91767d]387    if (!owl_window_is_toplevel(w)) {
388      /* If a subwin, we might have gotten random touched lines from wsyncup or
389       * past drawing. That information is useless, so we discard it all */
390      untouchwin(w->win);
391    }
[7a6e6c7]392    g_signal_emit(w, window_signals[REDRAW], 0, w->win);
[f91767d]393    wsyncup(w->win);
[449af72]394  }
395  w->dirty = 0;
396}
397
398static void _owl_window_redraw_subtree(owl_window *w)
399{
400  if (!w->dirty_subtree)
401    return;
402  _owl_window_redraw(w);
[7a70e26]403  owl_window_children_foreach(w, (GFunc)_owl_window_redraw_subtree, 0);
[449af72]404}
405
406/*
407Redraw all the windows with scheduled redraws.
408NOTE: This function shouldn't be called outside the event loop
409*/
410void owl_window_redraw_scheduled(void)
411{
[f70a7a3]412  owl_window *cursor;
413
[449af72]414  _owl_window_redraw_subtree(owl_window_get_screen());
[a57f87a]415  update_panels();
[f70a7a3]416  cursor = _get_cursor();
417  if (cursor && cursor->win) {
[f91767d]418    /* untouch it to avoid drawing; window should be clean now, but we must
419     * clean up in case there was junk left over on a subwin (cleaning up after
420     * subwin drawing isn't sufficient because a wsyncup messes up subwin
421     * ancestors */
[f70a7a3]422    untouchwin(cursor->win);
423    wnoutrefresh(cursor->win);
[f91767d]424  }
[7a6e6c7]425}
426
[7c8811c]427void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
428{
429  werase(win);
430  owl_window_dirty_children(w);
431}
432
[449af72]433/** Window position **/
434
435void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
436{
437  if (nlines)  *nlines  = w->nlines;
438  if (ncols)   *ncols   = w->ncols;
439  if (begin_y) *begin_y = w->begin_y;
440  if (begin_x) *begin_x = w->begin_x;
441}
442
[b0cbde4]443void owl_window_move(owl_window *w, int begin_y, int begin_x)
444{
[d15ea5f]445  owl_window_set_position(w, w->nlines, w->ncols, begin_y, begin_x);
446#if 0
447  /* This routine tries to move a window efficiently, but begy and begx are
448   * then wrong. Currently, this only effects the wnoutrefresh to move cursor.
449   * TODO: fix that and reinstate this code if it's worth the trouble */
[6b93305]450  if (w->is_screen) return; /* can't move the screen */
451  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
[b0cbde4]452
453  w->begin_y = begin_y;
454  w->begin_x = begin_x;
[50031f0]455  if (w->shown) {
456    /* Window is shown, we must try to have a window at the end */
[b0cbde4]457    if (w->win) {
458      /* We actually do have a window; let's move it */
459      if (w->pan) {
460        if (move_panel(w->pan, begin_y, begin_x) == OK)
461          return;
462      } else {
463        if (mvderwin(w->win, begin_y, begin_x) == OK) {
464          /* now both we and the parent are dirty */
465          owl_window_dirty(w->parent);
466          owl_window_dirty(w);
467          return;
468        }
469      }
470    }
471    /* We don't have a window or failed to move it. Fine. Brute force. */
[46e2e56]472    _owl_window_unrealize(w);
473    _owl_window_realize(w);
[b0cbde4]474  }
[d15ea5f]475#endif
[b0cbde4]476}
477
[449af72]478void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
479{
480  /* can't move the screen */
481  if (w->is_screen) {
482    begin_y = begin_x = 0;
483  }
484
[d15ea5f]485  if (w->nlines == nlines && w->ncols == ncols
486      && w->begin_y == begin_y && w->begin_x == begin_x) {
[449af72]487    return;
488  }
489
[d15ea5f]490  _owl_window_unrealize(w);
[678a505c]491  w->begin_y = begin_y;
492  w->begin_x = begin_x;
493  w->nlines = nlines;
494  w->ncols = ncols;
[7a6e6c7]495  g_signal_emit(w, window_signals[RESIZED], 0);
[50031f0]496  if (w->shown) {
[d15ea5f]497    /* ncurses is screwy: give up and recreate windows at the right place */
[46e2e56]498    _owl_window_realize(w);
[449af72]499  }
500}
501
502void owl_window_resize(owl_window *w, int nlines, int ncols)
503{
504  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
505}
506
507/** Stacking order **/
508
509void owl_window_top(owl_window *w) {
510  if (!w->pan) {
[7c8811c]511    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
[449af72]512    return;
513  }
514  top_panel(w->pan);
515}
516
517owl_window *owl_window_above(owl_window *w) {
518  PANEL *pan;
519
520  if (!w->pan) {
521    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
522    return NULL;
523  }
524  pan = panel_above(w->pan);
525  /* cast because panel_userptr pointlessly returns a const void * */
526  return pan ? (void*) panel_userptr(pan) : NULL;
527}
528
529owl_window *owl_window_below(owl_window *w) {
530  PANEL *pan;
531
532  if (!w->pan) {
533    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
534    return NULL;
535  }
536  pan = panel_below(w->pan);
537  /* cast because panel_userptr pointlessly returns a const void * */
538  return pan ? (void*) panel_userptr(pan) : NULL;
539}
Note: See TracBrowser for help on using the repository browser.