2 * Copyright Neil Brown ©2015-2019 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
7 * There is a list of 'panes' which can display rendered content
8 * and can optionally receive input.
9 * A pane is registered as a child of an existing pane and indicates
10 * a 'z' depth, and whether it can take input.
12 * The owner of a pane can:
13 * - register sub-panes
14 * - ask for text to be rendered at any time,
15 * - can request or discard focus. When discarded it returns to lower z level.
17 * The pane can tell the owner:
18 * - to refresh - possibly because it has been resized
19 * - that keyboard input has arrived
20 * - that a mouse click has arrived
22 * A pane can extend beyond the size of its parent, but is always
23 * clipped to the parent. If two children of a parent overlap and
24 * have the same Z-depth the result is undefined.
27 #define _GNU_SOURCE /* for asprintf */
38 static void pane_init(struct pane *p safe, struct pane *par)
42 list_add(&p->siblings, &par->children);
45 INIT_LIST_HEAD(&p->siblings);
47 INIT_LIST_HEAD(&p->children);
48 INIT_LIST_HEAD(&p->notifiers);
49 INIT_LIST_HEAD(&p->notifiees);
50 p->x = p->y = p->z = 0;
54 /* reasonable defaults */
58 p->abs_z = p->abs_zhi = 0;
61 p->data = safe_cast NULL;
65 pane_damaged(p, DAMAGED_SIZE);
68 static void __pane_check(struct pane *p safe)
71 list_for_each_entry(c, &p->children, siblings) {
72 ASSERT(c->parent == p);
77 static void pane_check(struct pane *p safe)
79 __pane_check(pane_root(p));
82 * pane_damaged: mark a pane as being 'damaged', and make
83 * sure all parents know about it.
85 void pane_damaged(struct pane *p, int type)
88 if (!p || (p->damaged | type) == p->damaged)
90 if (type & (type-1)) {
91 /* multiple bits are set, handle
95 for (b = 1; type; b <<= 1) {
103 if (type == DAMAGED_SIZE)
104 pane_notify("Notify:resize", p);
108 /* light-weight pane - never propagate damage */
111 if (type & DAMAGED_SIZE)
112 type = DAMAGED_SIZE_CHILD;
113 else if (type & DAMAGED_VIEW)
114 type = DAMAGED_VIEW_CHILD;
115 else if (type & DAMAGED_NEED_CALL)
116 type = DAMAGED_CHILD;
120 while ((p->damaged | type) != p->damaged) {
121 if (z > 0 && (type & DAMAGED_SIZE_CHILD))
122 /* overlay changed size, so we must refresh */
123 p->damaged |= DAMAGED_CONTENT;
130 struct pane *safe __pane_register(struct pane *parent, short z,
131 struct command *handle safe,
132 void *data, short data_size)
137 pane_init(p, parent);
139 p->handle = command_get(handle);
141 /* type of 'data' should correlate with type of handle,
142 * which should be parameterised...
147 p->data_size = data_size;
149 if (parent && parent->focus == NULL)
151 pane_call(parent, "ChildRegistered", p);
156 /* 'abs_z' is a global z-depth number.
157 * 'abs_z' of root is 0, and abs_z of every other pane is 1 more than abs_zhi
158 * of siblings with lower 'z', or same as parent if no such siblings.
160 * If DAMAGED_SIZE is set on a pane, we call "Refresh:size".
161 * If it or DAMAGED_SIZE_CHILD was set, we recurse onto all children.
162 * If abs_z is not one more than parent, we also recurse.
164 static void pane_do_resize(struct pane *p safe, int damage)
168 int abs_z = p->abs_z + 1;
170 if (p->damaged & DAMAGED_CLOSED) {
174 if ((damage & DAMAGED_SIZE) && p->z == 0)
175 /* Parent was resized and didn't propagate, so we need to */
176 pane_resize(p, 0, 0, p->parent->w, p->parent->h);
178 damage |= p->damaged & (DAMAGED_SIZE | DAMAGED_SIZE_CHILD);
180 p->abs_z == p->parent->abs_z + abs(p->z))
183 if (p->focus == NULL) {
184 list_for_each_entry(c, &p->children, siblings)
191 if (damage & (DAMAGED_SIZE))
192 if (pane_call(p, "Refresh:size", p, 0, NULL,
194 /* No need to propagate, just check on children */
202 list_for_each_entry(c, &p->children, siblings) {
204 c->abs_z = c->parent->abs_z;
207 if (c->z > z && (nextz == -1 || c->z < nextz))
210 if (c->abs_z != abs_z)
212 pane_do_resize(c, damage & DAMAGED_SIZE);
213 if (c->abs_zhi > abs_zhi)
214 abs_zhi = c->abs_zhi;
217 p->abs_zhi = abs_zhi;
220 if (p->damaged & DAMAGED_SIZE) {
221 p->damaged &= ~(DAMAGED_SIZE | DAMAGED_SIZE_CHILD);
222 p->damaged |= DAMAGED_CONTENT | DAMAGED_CHILD;
224 p->damaged &= ~DAMAGED_SIZE_CHILD;
225 p->damaged |= DAMAGED_CHILD;
229 static void pane_do_refresh(struct pane *p safe, int damage)
232 struct list_head *tmp;
235 if (p->damaged & DAMAGED_CLOSED)
238 damage |= p->damaged & (DAMAGED_CHILD|DAMAGED_CONTENT|DAMAGED_CURSOR);
239 p->damaged &= ~(DAMAGED_CHILD|DAMAGED_CONTENT|DAMAGED_CURSOR);
242 list_for_each_entry_safe(c, tmp, &p->children, siblings)
245 pane_do_refresh(c, damage);
247 if (!sent && damage & (DAMAGED_NEED_CALL)) {
248 if (damage & DAMAGED_CONTENT)
249 damage |= DAMAGED_CURSOR;
250 call("Refresh", p, 0, NULL, NULL, damage);
253 if (p->damaged & DAMAGED_POSTORDER) {
254 /* post-order call was triggered */
255 p->damaged &= ~DAMAGED_POSTORDER;
256 pane_call(p, "Refresh:postorder", p, 0, NULL, NULL, damage);
260 static void pane_do_review(struct pane *p safe, int damage)
265 if (p->damaged & DAMAGED_CLOSED)
268 damage |= p->damaged & (DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
269 p->damaged &= ~(DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
272 list_for_each_entry(c, &p->children, siblings)
275 pane_do_review(c, damage);
277 if (!sent && damage & (DAMAGED_VIEW))
278 call("Refresh:view", p, 0, NULL, NULL, damage);
281 void pane_refresh(struct pane *p safe)
287 while (cnt-- && (p->damaged & ~DAMAGED_CLOSED)) {
288 pane_do_resize(p, 0);
289 pane_do_review(p, 0);
290 pane_do_refresh(p, 0);
294 "WARNING %sroot pane damaged after refresh: %d\n",
295 p->parent != p ? "":"non-", p->damaged);
298 void pane_add_notify(struct pane *target safe, struct pane *source safe,
299 const char *msg safe)
303 list_for_each_entry(n, &source->notifiees, notifier_link)
304 if (n->notifiee == target &&
305 strcmp(msg, n->notification) == 0)
306 /* Already notifying */
311 n->notifiee = target;
312 n->notification = strdup(msg);
314 list_add(&n->notifier_link, &source->notifiees);
315 list_add(&n->notifiee_link, &target->notifiers);
318 void pane_drop_notifiers(struct pane *p safe, char *notification)
323 list_for_each_entry_safe(n, t, &p->notifiers, notifiee_link) {
325 if (notification && strcmp(notification, n->notification) != 0)
327 list_del_init(&n->notifiee_link);
328 list_del_init(&n->notifier_link);
329 free(n->notification);
334 static void pane_notify_close(struct pane *p safe)
336 while (!list_empty(&p->notifiees)) {
337 struct notifier *n = list_first_entry(&p->notifiees,
340 list_del_init(&n->notifiee_link);
341 list_del_init(&n->notifier_link);
342 if (strcmp(n->notification, "Notify:Close") == 0)
343 pane_call(n->notifiee, n->notification, p);
344 free(n->notification);
349 int do_pane_notify(struct pane *home, const char *notification safe,
351 int num, struct mark *m, const char *str,
352 int num2, struct mark *m2, const char *str2,
353 struct command *comm2)
355 /* Return the largest absolute return value. If no notifiees are found.
360 struct notifier *n, *n2;
364 /* FIXME why no error below */
365 list_for_each_entry_reverse(n, &home->notifiees, notifier_link)
366 if (strcmp(n->notification, notification) == 0) {
368 /* Nested notification - fail */
373 list_for_each_entry(n, &home->notifiees, notifier_link) {
376 if (strcmp(n->notification, notification) == 0) {
379 r = pane_call(n->notifiee, notification, p,
381 num2, m2, str2, cnt,ret, comm2);
382 if (abs(r) > abs(ret))
385 /* Panes might have been closed or notifications removed
386 * so nothing can be trusted... except this home pane
387 * had better still exist.
389 list_for_each_entry(n2, &home->notifiees, notifier_link)
390 if (n2 == n && n->noted == 2) {
395 /* 'n' has been removed, restart */
402 void pane_close(struct pane *p safe)
407 if (p->damaged & DAMAGED_CLOSED)
409 p->damaged |= DAMAGED_CLOSED;
414 pane_drop_notifiers(p, NULL);
416 if (!(p->parent->damaged & DAMAGED_CLOSED))
417 pane_call(p->parent, "ChildClosed", p);
419 list_del_init(&p->siblings);
422 list_for_each_entry(c, &p->children, siblings) {
423 if (c->damaged & DAMAGED_CLOSED)
429 if (p->parent->focus == p) {
430 pane_damaged(p->parent, DAMAGED_CURSOR);
431 p->parent->focus = NULL;
433 pane_notify_close(p);
434 pane_call(p, "Close", p);
437 pane_damaged(p->parent, DAMAGED_CONTENT);
438 /* If a child has not yet had "Close" called, we need to leave
439 * ->parent in place so a full range of commands are available.
442 p->damaged |= DAMAGED_DEAD;
443 editor_delayed_free(ed, p);
445 pane_call(p, "Free", p);
446 command_put(p->handle);
448 attr_free(&p->attrs);
453 void pane_resize(struct pane *p safe, int x, int y, int w, int h)
458 (p->x != x || p->y != y)) {
459 damage |= DAMAGED_CONTENT | DAMAGED_SIZE;
464 (p->w != w || p->h != h)) {
465 damage |= DAMAGED_SIZE;
469 if (p->w < 0 || p->h < 0)
470 /* FIXME something more gentle */
476 pane_damaged(p, damage);
479 void pane_reparent(struct pane *p safe, struct pane *newparent safe)
481 /* detach p from its parent and attach beneath its sibling newparent */
483 // FIXME this should be a failure, possibly with warning, not an
484 // assert. I'm not sure just now how best to do warnings.
485 ASSERT(newparent->parent == p->parent);
486 list_del(&p->siblings);
487 if (p->parent->focus == p)
488 p->parent->focus = newparent;
489 if (newparent->parent == newparent) {
490 newparent->parent = p->parent;
491 list_add(&newparent->siblings, &p->parent->children);
492 pane_resize(newparent, 0, 0, p->parent->w, p->parent->h);
495 p->parent = newparent;
496 newparent->damaged |= p->damaged;
497 if (newparent->focus == NULL)
498 newparent->focus = p;
499 list_add(&p->siblings, &newparent->children);
500 pane_call(newparent->parent, "ChildMoved", p);
502 pane_call(newparent->parent, "ChildReplaced", newparent);
505 void pane_move_after(struct pane *p safe, struct pane *after)
507 /* Move 'p' after 'after', or if !after, move to start */
508 if (p == p->parent || p == after)
511 if (p->parent != after->parent)
513 list_move(&p->siblings, &after->siblings);
515 list_move(&p->siblings, &p->parent->children);
519 void pane_subsume(struct pane *p safe, struct pane *parent safe)
521 /* move all content from p into parent, which must be empty,
522 * except possibly for 'p'.
523 * 'data' and 'handle' are swapped.
524 * Finally, p is freed
527 struct command *handle;
530 list_del_init(&p->siblings);
531 if (p->parent->focus == p) {
532 pane_damaged(p->parent, DAMAGED_CURSOR);
533 p->parent->focus = NULL;
535 p->parent = pane_root(parent);
536 while (!list_empty(&p->children)) {
537 c = list_first_entry(&p->children, struct pane, siblings);
538 list_move(&c->siblings, &parent->children);
540 parent->damaged |= c->damaged;
542 parent->focus = p->focus;
544 handle = parent->handle;
545 parent->handle = p->handle;
549 parent->data = p->data;
552 parent->damaged |= p->damaged;
557 int pane_masked(struct pane *p safe, short x, short y, short abs_z,
560 /* Test if this pane, or its children, mask this location.
561 * i.e. they have a higher 'abs_z' and might draw here.
562 * If 'w' and 'h' are set then reduce them to confirm that
563 * everything from x to x+w and y to y+h is not masked.
564 * This allows cases where there is no masking to be handled
568 /* calculate the upper bounds */
569 int xh = x + (w ? *w : 1);
570 int yh = y + (h ? *h : 1);
572 if (x >= p->x+p->w || y >= p->y+p->h)
573 /* x,y is beyond this pane, no overlap possible */
575 if (xh <= p->x || yh <= p->y)
576 /* area is before this pane, no over lap possible */
579 if (p->abs_z > abs_z && p->z > 0) {
580 /* This pane does mask some of the region */
581 if (x >= p->x || y >= p->y)
582 /* pane masks x,y itself */
584 /* pane must just mask some of the region beyond x,y */
585 if (w && *w > p->x - x)
587 if (h && *h > p->y - y)
592 /* This pane doesn't mask (same z level) but a child still could */
595 list_for_each_entry(c, &p->children, siblings)
596 if (pane_masked(c, x, y, abs_z, w, h))
602 void pane_focus(struct pane *focus)
604 struct pane *p = focus;
607 pane_damaged(p, DAMAGED_CURSOR);
608 /* refocus up to the display, but not to the root */
609 /* We have root->input->display. FIXME I need a better way
610 * to detect the 'display' level.
612 while (p->parent->parent->parent != p->parent->parent) {
613 if (p->parent->focus &&
614 p->parent->focus != p) {
615 struct pane *old = p->parent->focus;
616 pane_damaged(old, DAMAGED_CURSOR);
617 p->parent->focus = p;
620 call("pane:defocus", old);
624 call("pane:refocus", focus);
627 char *pane_attr_get(struct pane *p, const char *key safe)
630 char *a = attr_find(p->attrs, key);
634 a = CALL(strsave, pane, p, "get-attr", p, 0, NULL, key);
644 char *pane_mark_attr(struct pane *p safe, struct mark *m safe,
645 const char *key safe)
647 return call_ret(strsave, "doc:get-attr", p, 0, m, key);
650 void pane_clone_children(struct pane *from, struct pane *to)
652 /* "to" is a clone of "from", but has no children.
653 * Clone all the children of "from" to "to"
654 * Ignore z>0 children
660 list_for_each_entry(c, &from->children, siblings) {
663 pane_call(c, "Clone", to);
667 struct pane *pane_my_child(struct pane *p, struct pane *c)
669 while (c && c->parent != p) {
679 struct call_return *cr = container_of(ci->comm, struct call_return, c);
687 cr->comm = ci->comm2;
688 cr->s = strsave(ci->focus, ci->str);
694 struct call_return *cr = container_of(ci->comm, struct call_return, c);
698 cr->s = strdup(ci->str);
703 struct pane *do_call_pane(enum target_type type, struct pane *home,
704 struct command *comm2a,
705 const char *key safe, struct pane *focus safe,
706 int num, struct mark *m, const char *str,
707 int num2, struct mark *m2, const char *str2,
708 int x, int y, struct command *comm2b,
709 struct commcache *ccache)
711 struct call_return cr = {};
714 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
715 num2, m2, str2, x, y, &cr.c, ccache);
721 struct mark *do_call_mark(enum target_type type, struct pane *home,
722 struct command *comm2a,
723 const char *key safe, struct pane *focus safe,
724 int num, struct mark *m, const char *str,
725 int num2, struct mark *m2, const char *str2,
726 int x, int y, struct command *comm2b,
727 struct commcache *ccache)
729 struct call_return cr = {};
732 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
733 num2, m2, str2, x, y, &cr.c, ccache);
739 struct mark *do_call_mark2(enum target_type type, struct pane *home,
740 struct command *comm2a,
741 const char *key safe, struct pane *focus safe,
742 int num, struct mark *m, const char *str,
743 int num2, struct mark *m2, const char *str2,
744 int x, int y, struct command *comm2b,
745 struct commcache *ccache)
747 struct call_return cr = {};
750 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
751 num2, m2, str2, x, y, &cr.c, ccache);
757 struct command *do_call_comm(enum target_type type, struct pane *home,
758 struct command *comm2a,
759 const char *key safe, struct pane *focus safe,
760 int num, struct mark *m, const char *str,
761 int num2, struct mark *m2, const char *str2,
762 int x, int y, struct command *comm2b,
763 struct commcache *ccache)
765 struct call_return cr = {};
768 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
769 num2, m2, str2, x, y, &cr.c, ccache);
775 char *do_call_strsave(enum target_type type, struct pane *home,
776 struct command *comm2a,
777 const char *key safe, struct pane *focus safe,
778 int num, struct mark *m, const char *str,
779 int num2, struct mark *m2, const char *str2,
780 int x, int y, struct command *comm2b,
781 struct commcache *ccache)
783 struct call_return cr = {};
786 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
787 num2, m2, str2, x, y, &cr.c, ccache);
791 struct call_return do_call_all(enum target_type type, struct pane *home,
792 struct command *comm2a,
793 const char *key safe, struct pane *focus safe,
794 int num, struct mark *m, const char *str,
795 int num2, struct mark *m2, const char *str2,
796 int x, int y, struct command *comm2b,
797 struct commcache *ccache)
799 struct call_return cr = {};
802 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
803 num2, m2, str2, x, y, &cr.c, ccache);
807 char *do_call_str(enum target_type type, struct pane *home,
808 struct command *comm2a,
809 const char *key safe, struct pane *focus safe,
810 int num, struct mark *m, const char *str,
811 int num2, struct mark *m2, const char *str2,
812 int x, int y, struct command *comm2b,
813 struct commcache *ccache)
815 struct call_return cr = {};
818 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
819 num2, m2, str2, x, y, &cr.c, ccache);
827 /* convert pane-relative co-ords to absolute */
828 void pane_absxy(struct pane *p, short *x safe, short *y safe,
829 short *w safe, short *h safe)
832 if (p->w > 0 && *x + *w > p->w)
834 if (p->h > 0 && *y + *h > p->h)
844 /* Convert absolute c-ords to relative */
845 void pane_relxy(struct pane *p, short *x safe, short *y safe)
856 void pane_map_xy(struct pane *orig, struct pane *target,
857 short *x safe, short *y safe)
860 if (orig != target) {
861 pane_absxy(orig, x, y, &w, &h);
862 pane_relxy(target, x, y);
866 struct xy pane_scale(struct pane *p safe)
868 /* "scale" is roughly pixels-per-point * 1000
869 * So 10*scale.x is the width of a typical character in default font.
870 * 10*scale.y is the height.
871 * scale.x should be passed to text-size and and Draw:text to get
872 * correctly sized text
875 char *scM = pane_attr_get(p, "scale:M");
883 sscanf(scM, "%dx%d", &mw, &mh) != 2 ||
884 mw <= 0 || mh <= 0) {
885 /* Fonts have fixed 1x1 size so scaling not supported */
890 sc = pane_attr_get(p, "scale");
893 else if (sscanf(sc, "x:%d,y:%d", &w, &h) == 2 ||
894 sscanf(sc, "%dx%d", &w, &h) == 2) {
895 /* choose scale so w,h point fits in pane */
899 xscale = 1000 * p->w * 10 / mw / w;
900 yscale = 1000 * p->h * 10 / mh / h;
902 /* Old style where 'y' was in 'width' units... */
904 scale = (xscale < yscale) ? xscale : yscale;
905 } else if (sscanf(sc, "%d", &scale) != 1)
912 xy.x = scale * mw / 10;
913 xy.y = scale * mh / 10;