2 * Copyright Neil Brown ©2015-2023 <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 */
35 #define PANE_DATA_PTR_TYPE void *
37 #include "core-pane.h"
42 static void pane_init(struct pane *p safe, struct pane *par)
46 list_add(&p->siblings, &par->children);
50 INIT_LIST_HEAD(&p->siblings);
53 INIT_LIST_HEAD(&p->children);
54 INIT_LIST_HEAD(&p->notifiers);
55 INIT_LIST_HEAD(&p->notifiees);
56 p->x = p->y = p->z = 0;
60 /* reasonable defaults */
72 static void _pane_check(struct pane *p safe)
75 list_for_each_entry(c, &p->children, siblings) {
76 ASSERT(c->parent == p);
81 static void pane_check(struct pane *p safe)
83 _pane_check(pane_root(p));
86 DEF_CMD(pane_refresh);
88 * pane_damaged: mark a pane as being 'damaged', and make
89 * sure all parents know about it.
91 void pane_damaged(struct pane *p, int type)
94 struct pane *orig = p;
96 if (!p || (p->damaged | type) == p->damaged)
98 if (p == p->parent && !p->damaged)
99 if (call_comm("event:on-idle", p, &pane_refresh, 1) <= 0)
100 /* Cannot register an event yet, ignore damage */
102 if (type & (type-1)) {
103 /* multiple bits are set, handle
107 for (b = 1; type; b <<= 1) {
118 /* light-weight pane - never propagate damage */
120 if (type == DAMAGED_SIZE)
121 type = DAMAGED_SIZE_CHILD;
122 else if (type == DAMAGED_VIEW)
123 type = DAMAGED_VIEW_CHILD;
124 else if (type & DAMAGED_NEED_CALL)
125 type = DAMAGED_CHILD;
126 else if (type == DAMAGED_POSTORDER)
127 type = DAMAGED_POSTORDER_CHILD;
133 while ((p->damaged | type) != p->damaged) {
134 if (p->damaged & DAMAGED_DEBUG) {
135 LOG("damage %s %d (%d)", orig->name, orig_type, type);
139 if (p == p->parent && !p->damaged)
140 call_comm("event:on-idle", p, &pane_refresh, 1);
142 if (z > 0 && (type & DAMAGED_SIZE_CHILD))
143 /* overlay changed size, so we must refresh */
144 /* FIXME should this be a notification? */
145 p->damaged |= DAMAGED_REFRESH;
152 static struct pane *_do_pane_register(struct pane *parent, short z,
153 struct command *handle safe,
154 void *data, short data_size)
157 short alloc_size = data_size;
160 alloc_size = sizeof(data);
161 alloc_size += offsetof(struct pane, data);
163 p = alloc_zbuf(alloc_size, pane);
164 pane_init(p, parent);
165 p->alloc_size = alloc_size;
168 p->abs_z = parent->abs_z + 1;
169 p->handle = command_get(handle);
173 p->name = handle->name;
175 if (parent && parent->focus == NULL)
177 if (pane_call(parent, "Child-Notify", p, 1) < 0 ||
178 p->damaged & DAMAGED_CLOSED) {
179 /* ChildRegistered objected */
180 p->damaged |= DAMAGED_NOINIT;
183 } else if (parent && parent != pane_root(parent))
184 /* Need to set size of child, but not
185 * if parent is root and that make libevent
188 pane_damaged(parent, DAMAGED_SIZE);
193 struct pane *do_pane_register(struct pane *parent safe, short z,
194 struct command *handle safe,
195 void *data, short data_size)
197 return _do_pane_register(parent, z, handle, data, data_size);
200 struct pane *pane_register_root(struct command *handle safe,
201 void *data, short data_size)
203 return _do_pane_register(NULL, 0, handle, data, data_size);
206 void pane_update_handle(struct pane *p safe, struct command *handle safe)
208 command_put(p->handle);
209 p->handle = command_get(handle);
212 /* 'abs_z' is a global z-depth number (->z is relative to parent)
213 * 'abs_z' of root is 0, and abs_z of every other pane with z<=0 is 1 more than
214 * abs_z or parent, and abs_z of pane with z>0 is 1 more than max abs_z
215 * of all children of siblings with lower z.
218 static int pane_do_absz(struct pane *p safe, int absz)
227 int next_absz = absz + 1;
230 list_for_each_entry(c, &p->children, siblings) {
233 if (c->z < 0 && z == 0) {
234 c->abs_z = p->abs_z + 1;
240 if (nextz < 0 || c->z < nextz)
245 t = pane_do_absz(c, absz);
255 * If DAMAGED_SIZE is set on a pane, we call "Refresh:size".
256 * If it or DAMAGED_SIZE_CHILD was set, we recurse onto all children.
257 * If abs_z is not one more than parent, we also recurse.
259 static void pane_do_resize(struct pane *p safe, int damage)
262 if (!(p->damaged & DAMAGED_SIZE_CHILD))
265 while (p->damaged & DAMAGED_SIZE_CHILD) {
266 struct pane *parent, *c, *t;
268 /* Find a child with DAMGED_SIZE_CHILD.
269 * If it is DAMAGED_SIZE, handle that, else check
270 * its children. If no DAMAGED_SIZE_CHILD
271 * children are found, clear DAMAGED_SIZE_CHILD.
274 while (c && !(c->damaged & DAMAGED_CLOSED)) {
277 if (parent->damaged & DAMAGED_SIZE) {
278 parent->damaged &= ~DAMAGED_SIZE;
281 LOG("pane resize looped 1000 times - some pane must keep damaging itself.");
282 call("editor:notify:Message:broadcast",
284 "WARNING pane resize loop - see log");
289 if (pane_call(parent, "Refresh:size",
290 parent) == Efallthrough) {
291 /* Need to resize children ourselves */
292 list_for_each_entry(c, &parent->children,
299 pane_damaged(c, DAMAGED_SIZE);
301 /* Need to restart from root */
304 list_for_each_entry(t, &parent->children, siblings)
305 if (t->damaged & DAMAGED_SIZE_CHILD) {
310 parent->damaged &= ~DAMAGED_SIZE_CHILD;
316 static void pane_do_refresh(struct pane *p safe)
321 if (p->damaged & DAMAGED_CLOSED)
324 damage = p->damaged & (DAMAGED_CHILD|DAMAGED_REFRESH);
327 p->damaged &= ~damage;
328 if (damage & DAMAGED_REFRESH)
329 pane_call(p, "Refresh", pane_focus(p));
331 list_for_each_entry(c, &p->children, siblings)
332 c->damaged |= DAMAGED_NOT_HANDLED;
334 list_for_each_entry(c, &p->children, siblings) {
335 if (c->damaged & DAMAGED_NOT_HANDLED)
336 c->damaged &= ~DAMAGED_NOT_HANDLED;
338 /* Only handle each pane once */
347 static void pane_do_review(struct pane *p safe)
352 if (p->damaged & DAMAGED_CLOSED)
355 damage = p->damaged & (DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
356 p->damaged &= ~damage;
360 if (damage & DAMAGED_VIEW)
361 pane_call(p, "Refresh:view", pane_focus(p));
363 list_for_each_entry(c, &p->children, siblings)
364 c->damaged |= DAMAGED_NOT_HANDLED;
366 list_for_each_entry(c, &p->children, siblings) {
367 if (c->damaged & DAMAGED_NOT_HANDLED)
368 c->damaged &= ~DAMAGED_NOT_HANDLED;
370 /* Only handle each pane once */
379 static void pane_do_postorder(struct pane *p safe)
384 if (p->damaged & DAMAGED_CLOSED)
387 damage = p->damaged & (DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD);
388 p->damaged &= ~(DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD);
392 list_for_each_entry(c, &p->children, siblings)
393 c->damaged |= DAMAGED_NOT_HANDLED;
395 list_for_each_entry(c, &p->children, siblings) {
396 if (c->damaged & DAMAGED_NOT_HANDLED)
397 c->damaged &= ~DAMAGED_NOT_HANDLED;
399 /* Only handle each pane once */
401 pane_do_postorder(c);
404 if (damage & DAMAGED_POSTORDER)
405 call("Refresh:postorder", p);
408 REDEF_CMD(pane_refresh)
410 struct pane *p = ci->home;
413 if (p->damaged & DAMAGED_CLOSED)
415 call("editor:notify:Refresh-active", p, 1);
416 time_start(TIME_REFRESH);
419 ~(DAMAGED_CLOSED|DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD))) {
421 p->damaged |= DAMAGED_DEBUG;
422 pane_do_resize(p, 0);
425 p->damaged &= ~DAMAGED_DEBUG;
428 p->damaged |= DAMAGED_DEBUG;
429 pane_do_postorder(p);
430 p->damaged &= ~DAMAGED_DEBUG;
432 static time_t last_warn;
434 if (last_warn + 5 < time(NULL))
437 LOG("WARNING %sroot pane damaged after refresh: 0x%x",
438 p->parent != p ? "":"non-", p->damaged);
439 last_warn = time(NULL);
440 call("editor:notify:Message:broadcast",p, 0, NULL,
441 "Refresh looping - see log");
443 time_stop(TIME_REFRESH);
444 call("editor:notify:Refresh-active", p, 0);
448 void pane_add_notify(struct pane *target safe, struct pane *source safe,
449 const char *msg safe)
453 list_for_each_entry(n, &source->notifiees, notifier_link)
454 if (n->notifiee == target &&
455 strcmp(msg, n->notification) == 0)
456 /* Already notifying */
461 n->notifiee = target;
462 n->notification = strdup(msg);
464 list_add(&n->notifier_link, &source->notifiees);
465 list_add(&n->notifiee_link, &target->notifiers);
468 void pane_drop_notifiers(struct pane *p safe, char *notification)
473 list_for_each_entry_safe(n, t, &p->notifiers, notifiee_link) {
475 if (notification && strcmp(notification, n->notification) != 0)
477 list_del_init(&n->notifiee_link);
478 list_del_init(&n->notifier_link);
479 free(n->notification);
484 static void pane_notify_close(struct pane *p safe)
486 while (!list_empty(&p->notifiees)) {
487 struct notifier *n = list_first_entry(&p->notifiees,
490 list_del_init(&n->notifiee_link);
491 list_del_init(&n->notifier_link);
492 if (strcmp(n->notification, "Notify:Close") == 0)
493 pane_call(n->notifiee, n->notification, p);
494 free(n->notification);
499 int do_pane_notify(struct pane *home, const char *notification safe,
501 int num, struct mark *m, const char *str,
502 int num2, struct mark *m2, const char *str2,
503 struct command *comm2)
505 /* Return the largest absolute return value. If no notifiees are found.
510 struct notifier *n, *n2;
514 list_for_each_entry_reverse(n, &home->notifiees, notifier_link)
515 if (strcmp(n->notification, notification) == 0 &&
517 /* Nested notifications are not allowed. We cannot
518 * be sure the outer notification is sent, and it might
519 * contain different information.
521 LOG("Nested notification from %s to %s for %s not permitted.",
522 home->name, n->notifiee->name, notification);
524 call("editor:notify:Message:broadcast",
526 "WARNING nested notification blocked - see log");
530 list_for_each_entry(n, &home->notifiees, notifier_link) {
533 if (strcmp(n->notification, notification) == 0) {
536 r = pane_call(n->notifiee, notification, p,
538 num2, m2, str2, cnt,ret, comm2);
539 if (abs(r) > abs(ret))
542 /* Panes might have been closed or notifications removed
543 * so nothing can be trusted... except this home pane
544 * had better still exist.
546 list_for_each_entry(n2, &home->notifiees, notifier_link)
547 if (n2 == n && n->noted == 2) {
552 /* 'n' has been removed, restart */
556 list_for_each_entry(n, &home->notifiees, notifier_link)
557 if (strcmp(n->notification, notification) == 0)
562 static void pane_refocus(struct pane *p safe)
568 /* choose the worst credible focus - the oldest.
569 * Really some one else should be updating the focus, this is
572 list_for_each_entry_reverse(c, &p->children, siblings)
577 /* Tell the new focus to update - probably just a cursor update */
579 pt = call_ret(mark, "doc:point", p);
580 call("view:changed", p, 0, pt);
583 void pane_close(struct pane *p safe)
590 if (p->damaged & DAMAGED_CLOSED)
592 p->damaged |= DAMAGED_CLOSED;
597 /* Lots of panes might need to be notified of our closing.
598 * We try to notify the more "distant" first, so children are
599 * last and those registered for notification are first.
600 * Parent comes beween.
602 pane_notify_close(p);
604 if (!(p->parent->damaged & DAMAGED_CLOSED))
605 pane_call(p->parent, "Child-Notify", p, -1);
606 list_del_init(&p->siblings);
610 list_for_each_entry(c, &p->children, siblings) {
611 if (c->damaged & DAMAGED_CLOSED)
619 /* It is important not to drop notifiers until after all dependant
620 * panes are closed, as their closing might send a notification back
621 * to this pane. That happens with documents when the holder of
622 * a mark-view is closed.
624 pane_drop_notifiers(p, NULL);
626 infocus = pane_has_focus(p);
627 if (!(p->parent->damaged & DAMAGED_CLOSED) &&
628 p->parent->focus == p)
629 pane_refocus(p->parent);
631 if (!(p->damaged & DAMAGED_NOINIT))
632 pane_call(p, "Close", p, infocus);
634 /* If a child has not yet had "Close" called, we need to leave
635 * ->parent in place so a full range of commands are available.
637 p->damaged |= DAMAGED_DEAD;
639 editor_delayed_free(ed, p);
641 command_put(p->handle);
643 attr_free(&p->attrs);
648 void pane_free(struct pane *p safe)
651 unalloc_buf_safe(p, p->alloc_size, pane);
654 bool pane_resize(struct pane *p safe, int x, int y, int w, int h)
658 if (p->x != x || p->y != y) {
659 damage |= DAMAGED_SIZE;
664 (p->w != w || p->h != h)) {
665 damage |= DAMAGED_SIZE;
669 if (p->w < 0 || p->h < 0) {
675 /* tell the pane to resize its children later */
676 pane_damaged(p, damage);
678 pane_notify("Notify:resize", p);
682 void pane_reparent(struct pane *p safe, struct pane *newparent safe)
684 /* Change the parent of 'p' to be 'newparent.
685 * An important consideration is that a pane must never move up the
686 * hierarchy (towards the root), but only sideways or down. This ensures
687 * that any mark the pane (or a descendant thereof) might hold still gets
688 * delivered to the correct document. There is one exception that a pane
689 * that was newly created may be re-attached above some ancestors.
690 * There is not currently any test for "newness" though that may be added
693 * 'newparent' must be a descendant of p->parent.
694 * If it is a sibling of p or descentant thereof, p is simply detached from
695 * its parent and reattached below newparent.
696 * If it is a descendant of p (it cannot be p itself), then as well as p
697 * being detached from it parent and attached to newparent, newparent is detached
698 * and attached between p and p->parent, thus ensuring no loop is created.
701 struct pane *pc = pane_my_child(p->parent, newparent);
702 if (pc == NULL || newparent == p) {
703 LOG("Cannot reparent %s to %s, new parent must be a sibling or their descendant",
704 p->name, newparent->name);
709 list_del_init(&p->siblings);
710 if (p->parent->focus == p)
711 p->parent->focus = pc;
713 p->parent->focus = NULL;
714 /* newparent is below p, need to detach and reattach it */
715 if (newparent->parent->focus == newparent)
716 newparent->parent->focus = NULL;
717 pane_call(newparent->parent, "Child-Notify", newparent, -2);
718 newparent->parent = p->parent;
719 list_move(&newparent->siblings, &p->parent->children);
720 pane_resize(newparent, 0, 0, p->parent->w, p->parent->h);
723 pane_call(p->parent, "Child-Notify", p, -2);
724 /* Reattach p under newparent */
725 p->parent = newparent;
726 newparent->damaged |= p->damaged;
727 if (newparent->focus == NULL)
728 newparent->focus = p;
729 list_add(&p->siblings, &newparent->children);
730 pane_call(p->parent, "Child-Notify", p, 2);
732 pane_call(newparent->parent, "Child-Notify", newparent, 2);
735 void pane_move_after(struct pane *p safe, struct pane *after)
737 /* Move 'p' after 'after', or if !after, move to start */
738 if (p == p->parent || p == after)
741 if (p->parent != after->parent)
743 list_move(&p->siblings, &after->siblings);
745 list_move(&p->siblings, &p->parent->children);
749 void pane_subsume(struct pane *p safe, struct pane *parent safe)
751 /* move all content from p into parent, which must be empty,
752 * except possibly for 'p'.
753 * 'data' and 'handle' are swapped.
754 * Finally, p is freed
757 struct command *handle;
760 list_del_init(&p->siblings);
761 if (p->parent->focus == p)
762 pane_refocus(p->parent);
764 p->parent = pane_root(parent);
765 while (!list_empty(&p->children)) {
766 c = list_first_entry(&p->children, struct pane, siblings);
767 list_move(&c->siblings, &parent->children);
769 parent->damaged |= c->damaged;
771 parent->focus = p->focus;
773 handle = parent->handle;
774 parent->handle = p->handle;
778 parent->data = p->data;
781 parent->damaged |= p->damaged;
782 pane_damaged(p, DAMAGED_SIZE);
787 void pane_take_focus(struct pane *focus)
789 struct pane *p = focus;
793 /* refocus up to the display, but not to the root */
794 /* We have root->input->display. FIXME I need a better way
795 * to detect the 'display' level.
797 for (; p->parent->parent->parent != p->parent->parent; p = p->parent) {
798 struct pane *old = p->parent->focus;
801 p->parent->focus = p;
803 old = pane_focus(old);
804 pt = call_ret(mark, "doc:point", old);
805 call("view:changed", old, 0, pt);
806 home_call(old, "pane:defocus", focus);
809 pt = call_ret(mark, "doc:point", pane_focus(focus));
810 call("view:changed", pane_focus(focus), 0, pt);
811 call("pane:refocus", focus);
814 bool do_pane_has_focus(struct pane *focus, struct pane *root)
816 /* Test inf pane_take_focus() would change anything. */
817 struct pane *p = focus;
821 /* We check down to the declared root, or to on1 above
822 * the global root. Where the focus of the global root
827 && p->parent->parent->parent != p->parent->parent;
829 if (p->parent->focus != p)
834 char *pane_attr_get(struct pane *p, const char *key safe)
837 char *a = attr_find(p->attrs, key);
841 a = CALL(strsave, pane, p, "get-attr", p, 0, NULL, key);
851 char *pane_mark_attr(struct pane *p safe, struct mark *m safe,
852 const char *key safe)
854 return call_ret(strsave, "doc:get-attr", p, 0, m, key);
857 void pane_clone_children(struct pane *from, struct pane *to)
859 /* "to" is a clone of "from", but has no children or attributes.
860 * Copy the attributes and
861 * clone all the children of "from" to "to"
862 * Ignore z>0 children
868 if (from->attrs && !to->attrs)
869 to->attrs = attr_copy(from->attrs);
870 list_for_each_entry(c, &from->children, siblings)
871 c->damaged |= DAMAGED_NOT_HANDLED;
873 list_for_each_entry(c, &from->children, siblings) {
874 if (c->damaged & DAMAGED_NOT_HANDLED)
875 c->damaged &= ~DAMAGED_NOT_HANDLED;
877 /* Only handle each pane once */
881 pane_call(c, "Clone", to);
886 struct pane *pane_my_child(struct pane *p, struct pane *c)
888 while (c && c->parent != p) {
896 struct pane * safe pane_leaf(struct pane *p safe)
898 /* Find the only child with ->z of zero,
899 * and recurse on that.
900 * This ignores popups and stops when a pane
909 list_for_each_entry(c, &p->children, siblings) {
916 /* Two candidates, so further leaf - stop here */
926 struct call_return *cr = container_of(ci->comm, struct call_return, c);
934 cr->comm = ci->comm2;
935 cr->s = strsave(ci->focus, ci->str);
941 struct call_return *cr = container_of(ci->comm, struct call_return, c);
945 cr->s = strdup(ci->str);
951 struct call_return *cr = container_of(ci->comm, struct call_return, c);
953 cr->comm = command_get(ci->comm2);
959 struct call_return *cr = container_of(ci->comm, struct call_return, c);
961 if (!ci->str || ci->num < 0 || ci->num > 1000*1000*1000)
963 cr->s = malloc(ci->num);
966 memcpy(cr->s, ci->str, ci->num);
970 struct pane *do_call_pane(enum target_type type, struct pane *home,
971 struct command *comm2a,
972 const char *key safe, struct pane *focus safe,
973 int num, struct mark *m, const char *str,
974 int num2, struct mark *m2, const char *str2,
975 int x, int y, struct command *comm2b)
977 struct call_return cr = {};
980 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
981 num2, m2, str2, x, y, &cr.c);
987 struct mark *do_call_mark(enum target_type type, struct pane *home,
988 struct command *comm2a,
989 const char *key safe, struct pane *focus safe,
990 int num, struct mark *m, const char *str,
991 int num2, struct mark *m2, const char *str2,
992 int x, int y, struct command *comm2b)
994 struct call_return cr = {};
997 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
998 num2, m2, str2, x, y, &cr.c);
1004 struct mark *do_call_mark2(enum target_type type, struct pane *home,
1005 struct command *comm2a,
1006 const char *key safe, struct pane *focus safe,
1007 int num, struct mark *m, const char *str,
1008 int num2, struct mark *m2, const char *str2,
1009 int x, int y, struct command *comm2b)
1011 struct call_return cr = {};
1014 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1015 num2, m2, str2, x, y, &cr.c);
1021 struct command *do_call_comm(enum target_type type, struct pane *home,
1022 struct command *comm2a,
1023 const char *key safe, struct pane *focus safe,
1024 int num, struct mark *m, const char *str,
1025 int num2, struct mark *m2, const char *str2,
1026 int x, int y, struct command *comm2b)
1028 struct call_return cr = {};
1031 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1032 num2, m2, str2, x, y, &cr.c);
1038 char *do_call_strsave(enum target_type type, struct pane *home,
1039 struct command *comm2a,
1040 const char *key safe, struct pane *focus safe,
1041 int num, struct mark *m, const char *str,
1042 int num2, struct mark *m2, const char *str2,
1043 int x, int y, struct command *comm2b)
1045 struct call_return cr = {};
1048 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1049 num2, m2, str2, x, y, &cr.c);
1053 struct call_return do_call_all(enum target_type type, struct pane *home,
1054 struct command *comm2a,
1055 const char *key safe, struct pane *focus safe,
1056 int num, struct mark *m, const char *str,
1057 int num2, struct mark *m2, const char *str2,
1058 int x, int y, struct command *comm2b)
1060 struct call_return cr = {};
1063 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1064 num2, m2, str2, x, y, &cr.c);
1068 char *do_call_str(enum target_type type, struct pane *home,
1069 struct command *comm2a,
1070 const char *key safe, struct pane *focus safe,
1071 int num, struct mark *m, const char *str,
1072 int num2, struct mark *m2, const char *str2,
1073 int x, int y, struct command *comm2b)
1075 struct call_return cr = {};
1078 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1079 num2, m2, str2, x, y, &cr.c);
1087 struct call_return do_call_bytes(enum target_type type, struct pane *home,
1088 struct command *comm2a,
1089 const char *key safe, struct pane *focus safe,
1090 int num, struct mark *m, const char *str,
1091 int num2, struct mark *m2, const char *str2,
1092 int x, int y, struct command *comm2b)
1094 struct call_return cr = {};
1097 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1098 num2, m2, str2, x, y, &cr.c);
1106 struct xy pane_mapxy(struct pane *p safe, struct pane *target safe,
1107 short x, short y, bool clip)
1109 /* x and y are relative to p. The result xy is relative to target */
1112 /* This is a bit of a hack, but is needed to map lib-renderline
1113 * co-ordes to a pane which is parallel with the render-line
1114 * pane, but might be further from the root.
1115 * We move 'target' towards the root to a pane of exactly the
1116 * same size and position. This will not change a correct
1117 * result, and can make a correct result more likely.
1119 while (target->parent != target &&
1120 target->x == 0 && target->y == 0 &&
1121 target->parent->w == target->w &&
1122 target->parent->h == target->h)
1123 target = target->parent;
1125 while (p != target && p != p->parent) {
1126 if (clip && p->w > 0) {
1132 if (clip && p->h > 0) {
1147 struct xy pane_scale(struct pane *p safe)
1149 /* "scale" is roughly pixels-per-point * 1000
1150 * So 10*scale.x/100 is the width of a typical character in default font.
1151 * 10*scale.y/100 is the height.
1152 * scale.x should be passed to Draw:text-size and and Draw:text to get
1153 * correctly sized text
1155 * "scale:M" is provided by the display module and reports
1156 * the size of a captial M in default fond - width and height in pixels
1159 char *scM = pane_attr_get(p, "scale:M");
1167 sscanf(scM, "%dx%d", &mw, &mh) != 2 ||
1168 mw <= 0 || mh <= 0) {
1169 /* Fonts have fixed 1x1 size so scaling not supported */
1174 /* "scale" is a request to change from the default.
1175 * It can be a simple number, in which case it is 1000 times a scale
1176 * factor, so "500" would be half of default size.
1177 * It can be an x,y pair, e.g. "800x240".
1178 * This chooses a scale so that the given number of points, (1/10 of
1179 * size of default "M") will fit in the pane. If the pane is resized,
1180 * the scale will automatically adjust to fit the requested number
1183 sc = pane_attr_get(p, "scale");
1186 else if (sscanf(sc, "%dx%d", &w, &h) == 2) {
1187 /* choose scale so w,h point fits in pane */
1191 xscale = 1000 * p->w * 10 / mw / w;
1192 yscale = 1000 * p->h * 10 / mh / h;
1193 scale = (xscale < yscale) ? xscale : yscale;
1194 } else if (sscanf(sc, "%d", &scale) != 1)
1201 xy.x = scale * mw / 10;
1202 xy.y = scale * mh / 10;
1206 static inline unsigned int ts_to_ms(struct timespec *ts safe)
1208 return ts->tv_nsec / 1000 / 1000 + ts->tv_sec * 1000;
1211 bool pane_too_long(struct pane *p safe, unsigned int msec)
1214 unsigned int duration;
1216 if (p->timestamp == 0)
1218 if (p->timestamp == 1)
1220 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
1221 duration = ts_to_ms(&ts) - p->timestamp;
1224 if (duration <= msec)
1226 /* If running under gdb, then I was probaly delayed
1227 * by single-stepping, so don't through an error
1229 p->timestamp = ! debugger_is_present();
1230 return p->timestamp;
1233 void pane_set_time(struct pane *p safe)
1237 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
1238 p->timestamp = ts_to_ms(&ts);
1239 if (p->timestamp <= 1)
1243 DEF_CB(clear_consistency)
1245 ci->focus->consistency_checks = 0;
1249 bool pane_no_consistency(struct pane *p safe)
1251 p->consistency_checks += 1;
1252 if (p->consistency_checks == 50)
1253 call_comm("event:on-idle", p, &clear_consistency, 2);
1254 return p->consistency_checks > 60;