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 */
40 static void pane_init(struct pane *p safe, struct pane *par)
44 list_add(&p->siblings, &par->children);
48 INIT_LIST_HEAD(&p->siblings);
51 INIT_LIST_HEAD(&p->children);
52 INIT_LIST_HEAD(&p->notifiers);
53 INIT_LIST_HEAD(&p->notifiees);
54 p->x = p->y = p->z = 0;
58 /* reasonable defaults */
70 static void _pane_check(struct pane *p safe)
73 list_for_each_entry(c, &p->children, siblings) {
74 ASSERT(c->parent == p);
79 static void pane_check(struct pane *p safe)
81 _pane_check(pane_root(p));
84 DEF_CMD(pane_refresh);
86 * pane_damaged: mark a pane as being 'damaged', and make
87 * sure all parents know about it.
89 void pane_damaged(struct pane *p, int type)
92 struct pane *orig = p;
94 if (!p || (p->damaged | type) == p->damaged)
96 if (p == p->parent && !p->damaged)
97 if (call_comm("event:on-idle", p, &pane_refresh, 1) <= 0)
98 /* Cannot register an event yet, ignore damage */
100 if (type & (type-1)) {
101 /* multiple bits are set, handle
105 for (b = 1; type; b <<= 1) {
116 /* light-weight pane - never propagate damage */
118 if (type == DAMAGED_SIZE)
119 type = DAMAGED_SIZE_CHILD;
120 else if (type == DAMAGED_VIEW)
121 type = DAMAGED_VIEW_CHILD;
122 else if (type & DAMAGED_NEED_CALL)
123 type = DAMAGED_CHILD;
124 else if (type == DAMAGED_POSTORDER)
125 type = DAMAGED_POSTORDER_CHILD;
131 while ((p->damaged | type) != p->damaged) {
132 if (p->damaged & DAMAGED_DEBUG) {
133 LOG("damage %s %d (%d)", orig->name, orig_type, type);
137 if (p == p->parent && !p->damaged)
138 call_comm("event:on-idle", p, &pane_refresh, 1);
140 if (z > 0 && (type & DAMAGED_SIZE_CHILD))
141 /* overlay changed size, so we must refresh */
142 /* FIXME should this be a notification? */
143 p->damaged |= DAMAGED_REFRESH;
150 static struct pane *_do_pane_register(struct pane *parent, short z,
151 struct command *handle safe,
152 void *data, short data_size)
155 short alloc_size = data_size;
158 alloc_size = sizeof(data);
159 alloc_size += offsetof(struct pane, data);
161 p = alloc_zbuf(alloc_size, pane);
162 pane_init(p, parent);
163 p->alloc_size = alloc_size;
166 p->abs_z = parent->abs_z + 1;
167 p->handle = command_get(handle);
171 p->name = handle->name;
173 if (parent && parent->focus == NULL)
175 if (pane_call(parent, "Child-Notify", p, 1) < 0 ||
176 p->damaged & DAMAGED_CLOSED) {
177 /* ChildRegistered objected */
178 p->damaged |= DAMAGED_NOINIT;
181 } else if (parent && parent != pane_root(parent))
182 /* Need to set size of child, but not
183 * if parent is root and that make libevent
186 pane_damaged(parent, DAMAGED_SIZE);
191 struct pane *do_pane_register(struct pane *parent safe, short z,
192 struct command *handle safe,
193 void *data, short data_size)
195 return _do_pane_register(parent, z, handle, data, data_size);
198 struct pane *pane_register_root(struct command *handle safe,
199 void *data, short data_size)
201 return _do_pane_register(NULL, 0, handle, data, data_size);
204 void pane_update_handle(struct pane *p safe, struct command *handle safe)
206 command_put(p->handle);
207 p->handle = command_get(handle);
210 /* 'abs_z' is a global z-depth number (->z is relative to parent)
211 * 'abs_z' of root is 0, and abs_z of every other pane with z<=0 is 1 more than
212 * abs_z or parent, and abs_z of pane with z>0 is 1 more than max abs_z
213 * of all children of siblings with lower z.
216 static int pane_do_absz(struct pane *p safe, int absz)
225 int next_absz = absz + 1;
228 list_for_each_entry(c, &p->children, siblings) {
231 if (c->z < 0 && z == 0) {
232 c->abs_z = p->abs_z + 1;
238 if (nextz < 0 || c->z < nextz)
243 t = pane_do_absz(c, absz);
253 * If DAMAGED_SIZE is set on a pane, we call "Refresh:size".
254 * If it or DAMAGED_SIZE_CHILD was set, we recurse onto all children.
255 * If abs_z is not one more than parent, we also recurse.
257 static void pane_do_resize(struct pane *p safe, int damage)
260 if (!(p->damaged & DAMAGED_SIZE_CHILD))
263 while (p->damaged & DAMAGED_SIZE_CHILD) {
264 struct pane *parent, *c, *t;
266 /* Find a child with DAMGED_SIZE_CHILD.
267 * If it is DAMAGED_SIZE, handle that, else check
268 * its children. If no DAMAGED_SIZE_CHILD
269 * children are found, clear DAMAGED_SIZE_CHILD.
272 while (c && !(c->damaged & DAMAGED_CLOSED)) {
275 if (parent->damaged & DAMAGED_SIZE) {
276 parent->damaged &= ~DAMAGED_SIZE;
279 LOG("pane resize looped 1000 times - some pane must keep damaging itself.");
280 call("editor:notify:Message:broadcast",
282 "WARNING pane resize loop - see log");
287 if (pane_call(parent, "Refresh:size",
288 parent) == Efallthrough) {
289 /* Need to resize children ourselves */
290 list_for_each_entry(c, &parent->children,
297 pane_damaged(c, DAMAGED_SIZE);
299 /* Need to restart from root */
302 list_for_each_entry(t, &parent->children, siblings)
303 if (t->damaged & DAMAGED_SIZE_CHILD) {
308 parent->damaged &= ~DAMAGED_SIZE_CHILD;
314 static void pane_do_refresh(struct pane *p safe)
319 if (p->damaged & DAMAGED_CLOSED)
322 damage = p->damaged & (DAMAGED_CHILD|DAMAGED_REFRESH);
325 p->damaged &= ~damage;
326 if (damage & DAMAGED_REFRESH)
327 pane_call(p, "Refresh", pane_leaf(p));
329 list_for_each_entry(c, &p->children, siblings)
330 c->damaged |= DAMAGED_NOT_HANDLED;
332 list_for_each_entry(c, &p->children, siblings) {
333 if (c->damaged & DAMAGED_NOT_HANDLED)
334 c->damaged &= ~DAMAGED_NOT_HANDLED;
336 /* Only handle each pane once */
345 static void pane_do_review(struct pane *p safe)
350 if (p->damaged & DAMAGED_CLOSED)
353 damage = p->damaged & (DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
354 p->damaged &= ~damage;
358 if (damage & DAMAGED_VIEW)
359 pane_call(p, "Refresh:view", pane_leaf(p));
361 list_for_each_entry(c, &p->children, siblings)
362 c->damaged |= DAMAGED_NOT_HANDLED;
364 list_for_each_entry(c, &p->children, siblings) {
365 if (c->damaged & DAMAGED_NOT_HANDLED)
366 c->damaged &= ~DAMAGED_NOT_HANDLED;
368 /* Only handle each pane once */
377 static void pane_do_postorder(struct pane *p safe)
382 if (p->damaged & DAMAGED_CLOSED)
385 damage = p->damaged & (DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD);
386 p->damaged &= ~(DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD);
390 list_for_each_entry(c, &p->children, siblings)
391 c->damaged |= DAMAGED_NOT_HANDLED;
393 list_for_each_entry(c, &p->children, siblings) {
394 if (c->damaged & DAMAGED_NOT_HANDLED)
395 c->damaged &= ~DAMAGED_NOT_HANDLED;
397 /* Only handle each pane once */
399 pane_do_postorder(c);
402 if (damage & DAMAGED_POSTORDER)
403 call("Refresh:postorder", p);
406 REDEF_CMD(pane_refresh)
408 struct pane *p = ci->home;
411 if (p->damaged & DAMAGED_CLOSED)
414 time_start(TIME_REFRESH);
417 ~(DAMAGED_CLOSED|DAMAGED_POSTORDER|DAMAGED_POSTORDER_CHILD))) {
419 p->damaged |= DAMAGED_DEBUG;
420 pane_do_resize(p, 0);
423 p->damaged &= ~DAMAGED_DEBUG;
425 pane_do_postorder(p);
427 static time_t last_warn;
429 if (last_warn + 5 < time(NULL))
432 LOG("WARNING %sroot pane damaged after refresh: %d",
433 p->parent != p ? "":"non-", p->damaged);
434 last_warn = time(NULL);
435 call("editor:notify:Message:broadcast",p, 0, NULL,
436 "Refresh looping - see log");
438 time_stop(TIME_REFRESH);
442 void pane_add_notify(struct pane *target safe, struct pane *source safe,
443 const char *msg safe)
447 list_for_each_entry(n, &source->notifiees, notifier_link)
448 if (n->notifiee == target &&
449 strcmp(msg, n->notification) == 0)
450 /* Already notifying */
455 n->notifiee = target;
456 n->notification = strdup(msg);
458 list_add(&n->notifier_link, &source->notifiees);
459 list_add(&n->notifiee_link, &target->notifiers);
462 void pane_drop_notifiers(struct pane *p safe, char *notification)
467 list_for_each_entry_safe(n, t, &p->notifiers, notifiee_link) {
469 if (notification && strcmp(notification, n->notification) != 0)
471 list_del_init(&n->notifiee_link);
472 list_del_init(&n->notifier_link);
473 free(n->notification);
478 static void pane_notify_close(struct pane *p safe)
480 while (!list_empty(&p->notifiees)) {
481 struct notifier *n = list_first_entry(&p->notifiees,
484 list_del_init(&n->notifiee_link);
485 list_del_init(&n->notifier_link);
486 if (strcmp(n->notification, "Notify:Close") == 0)
487 pane_call(n->notifiee, n->notification, p);
488 /* Close:Notify can be delivered even to DAMAGED_CLOSED
491 if (strcmp(n->notification, "Close:Notify") == 0)
492 pane_call(n->notifiee, n->notification, p);
493 free(n->notification);
498 int do_pane_notify(struct pane *home, const char *notification safe,
500 int num, struct mark *m, const char *str,
501 int num2, struct mark *m2, const char *str2,
502 struct command *comm2)
504 /* Return the largest absolute return value. If no notifiees are found.
509 struct notifier *n, *n2;
513 list_for_each_entry_reverse(n, &home->notifiees, notifier_link)
514 if (strcmp(n->notification, notification) == 0 &&
516 /* Nested notifications are not allowed. We cannot
517 * be sure the outer notification is sent, and it might
518 * contain different information.
520 LOG("Nested notification from %s to %s for %s not permitted.",
521 home->name, n->notifiee->name, notification);
526 list_for_each_entry(n, &home->notifiees, notifier_link) {
529 if (strcmp(n->notification, notification) == 0) {
532 r = pane_call(n->notifiee, notification, p,
534 num2, m2, str2, cnt,ret, comm2);
535 if (abs(r) > abs(ret))
538 /* Panes might have been closed or notifications removed
539 * so nothing can be trusted... except this home pane
540 * had better still exist.
542 list_for_each_entry(n2, &home->notifiees, notifier_link)
543 if (n2 == n && n->noted == 2) {
548 /* 'n' has been removed, restart */
552 list_for_each_entry(n, &home->notifiees, notifier_link)
553 if (strcmp(n->notification, notification) == 0)
558 static void pane_refocus(struct pane *p safe)
564 /* choose the worst credible focus - the oldest.
565 * Really some one else should be updating the focus, this is
568 list_for_each_entry_reverse(c, &p->children, siblings)
573 /* Tell the new focus to update - probably just a cursor update */
575 pt = call_ret(mark, "doc:point", p);
576 call("view:changed", p, 0, pt);
579 void pane_close(struct pane *p safe)
586 if (p->damaged & DAMAGED_CLOSED)
588 p->damaged |= DAMAGED_CLOSED;
593 /* Lots of panes might need to be notified of our closing.
594 * We try to notify the more "distant" first, so children are
595 * last and those registered for notification are first.
596 * Parent comes beween.
598 pane_notify_close(p);
600 if (!(p->parent->damaged & DAMAGED_CLOSED))
601 pane_call(p->parent, "Child-Notify", p, -1);
602 list_del_init(&p->siblings);
606 list_for_each_entry(c, &p->children, siblings) {
607 if (c->damaged & DAMAGED_CLOSED)
615 /* It is important not to drop notifiers until after all dependant
616 * panes are closed, as their closing might send a notification back
617 * to this pane. That happens with documents when the holder of
618 * a mark-view is closed.
620 pane_drop_notifiers(p, NULL);
622 infocus = pane_has_focus(p);
623 if (!(p->parent->damaged & DAMAGED_CLOSED) &&
624 p->parent->focus == p)
625 pane_refocus(p->parent);
627 if (!(p->damaged & DAMAGED_NOINIT))
628 pane_call(p, "Close", p, infocus);
630 /* If a child has not yet had "Close" called, we need to leave
631 * ->parent in place so a full range of commands are available.
633 p->damaged |= DAMAGED_DEAD;
635 editor_delayed_free(ed, p);
637 command_put(p->handle);
639 attr_free(&p->attrs);
644 void pane_free(struct pane *p safe)
647 unalloc_buf_safe(p, p->alloc_size, pane);
650 bool pane_resize(struct pane *p safe, int x, int y, int w, int h)
654 if (p->x != x || p->y != y) {
655 damage |= DAMAGED_SIZE;
660 (p->w != w || p->h != h)) {
661 damage |= DAMAGED_SIZE;
665 if (p->w < 0 || p->h < 0) {
671 /* tell the pane to resize its children later */
672 pane_damaged(p, damage);
674 pane_notify("Notify:resize", p);
678 void pane_reparent(struct pane *p safe, struct pane *newparent safe)
680 /* Change the parent of 'p' to be 'newparent.
681 * An important consideration is that a pane must never move up the
682 * hierarchy (towards the root), but only sideways or down. This ensures
683 * that any mark the pane (or a descendant thereof) might hold still gets
684 * delivered to the correct document. There is one exception that a pane
685 * that was newly created may be re-attached above some ancestors.
686 * There is not currently any test for "newness" though that may be added
689 * 'newparent' must be a descendant of p->parent.
690 * If it is a sibling of p or descentant thereof, p is simply detached from
691 * its parent and reattached below newparent.
692 * If it is a descendant of p (it cannot be p itself), then as well as p
693 * being detached from it parent and attached to newparent, newparent is detached
694 * and attached between p and p->parent, thus ensuring no loop is created.
697 struct pane *pc = pane_my_child(p->parent, newparent);
698 if (pc == NULL || newparent == p) {
699 LOG("Cannot reparent %s to %s, new parent must be a sibling or their descendant",
700 p->name, newparent->name);
705 list_del_init(&p->siblings);
706 if (p->parent->focus == p)
707 p->parent->focus = pc;
709 p->parent->focus = NULL;
710 /* newparent is below p, need to detach and reattach it */
711 if (newparent->parent->focus == newparent)
712 newparent->parent->focus = NULL;
713 pane_call(newparent->parent, "Child-Notify", newparent, -2);
714 newparent->parent = p->parent;
715 list_move(&newparent->siblings, &p->parent->children);
716 pane_resize(newparent, 0, 0, p->parent->w, p->parent->h);
719 pane_call(p->parent, "Child-Notify", p, -2);
720 /* Reattach p under newparent */
721 p->parent = newparent;
722 newparent->damaged |= p->damaged;
723 if (newparent->focus == NULL)
724 newparent->focus = p;
725 list_add(&p->siblings, &newparent->children);
726 pane_call(p->parent, "Child-Notify", p, 2);
728 pane_call(newparent->parent, "Child-Notify", newparent, 2);
731 void pane_move_after(struct pane *p safe, struct pane *after)
733 /* Move 'p' after 'after', or if !after, move to start */
734 if (p == p->parent || p == after)
737 if (p->parent != after->parent)
739 list_move(&p->siblings, &after->siblings);
741 list_move(&p->siblings, &p->parent->children);
745 void pane_subsume(struct pane *p safe, struct pane *parent safe)
747 /* move all content from p into parent, which must be empty,
748 * except possibly for 'p'.
749 * 'data' and 'handle' are swapped.
750 * Finally, p is freed
753 struct command *handle;
756 list_del_init(&p->siblings);
757 if (p->parent->focus == p)
758 pane_refocus(p->parent);
760 p->parent = pane_root(parent);
761 while (!list_empty(&p->children)) {
762 c = list_first_entry(&p->children, struct pane, siblings);
763 list_move(&c->siblings, &parent->children);
765 parent->damaged |= c->damaged;
767 parent->focus = p->focus;
769 handle = parent->handle;
770 parent->handle = p->handle;
774 parent->data = p->data;
777 parent->damaged |= p->damaged;
778 pane_damaged(p, DAMAGED_SIZE);
783 void pane_focus(struct pane *focus)
785 struct pane *p = focus;
789 /* refocus up to the display, but not to the root */
790 /* We have root->input->display. FIXME I need a better way
791 * to detect the 'display' level.
793 for (; p->parent->parent->parent != p->parent->parent; p = p->parent) {
794 struct pane *old = p->parent->focus;
797 p->parent->focus = p;
799 old = pane_leaf(old);
800 pt = call_ret(mark, "doc:point", old);
801 call("view:changed", old, 0, pt);
802 home_call(old, "pane:defocus", focus);
805 pt = call_ret(mark, "doc:point", pane_leaf(focus));
806 call("view:changed", pane_leaf(focus), 0, pt);
807 call("pane:refocus", focus);
810 bool do_pane_has_focus(struct pane *focus, struct pane *root)
812 /* Would pane_focus change anything */
813 struct pane *p = focus;
817 /* We check down to the declared root, or to on1 above
818 * the global root. Where the focus of the global root
823 && p->parent->parent->parent != p->parent->parent;
825 if (p->parent->focus != p)
830 char *pane_attr_get(struct pane *p, const char *key safe)
833 char *a = attr_find(p->attrs, key);
837 a = CALL(strsave, pane, p, "get-attr", p, 0, NULL, key);
847 char *pane_mark_attr(struct pane *p safe, struct mark *m safe,
848 const char *key safe)
850 return call_ret(strsave, "doc:get-attr", p, 0, m, key);
853 void pane_clone_children(struct pane *from, struct pane *to)
855 /* "to" is a clone of "from", but has no children or attributes.
856 * Copy the attributes and
857 * clone all the children of "from" to "to"
858 * Ignore z>0 children
864 if (from->attrs && !to->attrs)
865 to->attrs = attr_copy(from->attrs);
866 list_for_each_entry(c, &from->children, siblings)
867 c->damaged |= DAMAGED_NOT_HANDLED;
869 list_for_each_entry(c, &from->children, siblings) {
870 if (c->damaged & DAMAGED_NOT_HANDLED)
871 c->damaged &= ~DAMAGED_NOT_HANDLED;
873 /* Only handle each pane once */
877 pane_call(c, "Clone", to);
882 struct pane *pane_my_child(struct pane *p, struct pane *c)
884 while (c && c->parent != p) {
894 struct call_return *cr = container_of(ci->comm, struct call_return, c);
902 cr->comm = ci->comm2;
903 cr->s = strsave(ci->focus, ci->str);
909 struct call_return *cr = container_of(ci->comm, struct call_return, c);
913 cr->s = strdup(ci->str);
919 struct call_return *cr = container_of(ci->comm, struct call_return, c);
921 cr->comm = command_get(ci->comm2);
927 struct call_return *cr = container_of(ci->comm, struct call_return, c);
929 if (!ci->str || ci->num < 0 || ci->num > 1000*1000*1000)
931 cr->s = malloc(ci->num);
934 memcpy(cr->s, ci->str, ci->num);
938 struct pane *do_call_pane(enum target_type type, struct pane *home,
939 struct command *comm2a,
940 const char *key safe, struct pane *focus safe,
941 int num, struct mark *m, const char *str,
942 int num2, struct mark *m2, const char *str2,
943 int x, int y, struct command *comm2b)
945 struct call_return cr = {};
948 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
949 num2, m2, str2, x, y, &cr.c);
955 struct mark *do_call_mark(enum target_type type, struct pane *home,
956 struct command *comm2a,
957 const char *key safe, struct pane *focus safe,
958 int num, struct mark *m, const char *str,
959 int num2, struct mark *m2, const char *str2,
960 int x, int y, struct command *comm2b)
962 struct call_return cr = {};
965 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
966 num2, m2, str2, x, y, &cr.c);
972 struct mark *do_call_mark2(enum target_type type, struct pane *home,
973 struct command *comm2a,
974 const char *key safe, struct pane *focus safe,
975 int num, struct mark *m, const char *str,
976 int num2, struct mark *m2, const char *str2,
977 int x, int y, struct command *comm2b)
979 struct call_return cr = {};
982 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
983 num2, m2, str2, x, y, &cr.c);
989 struct command *do_call_comm(enum target_type type, struct pane *home,
990 struct command *comm2a,
991 const char *key safe, struct pane *focus safe,
992 int num, struct mark *m, const char *str,
993 int num2, struct mark *m2, const char *str2,
994 int x, int y, struct command *comm2b)
996 struct call_return cr = {};
999 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1000 num2, m2, str2, x, y, &cr.c);
1006 char *do_call_strsave(enum target_type type, struct pane *home,
1007 struct command *comm2a,
1008 const char *key safe, struct pane *focus safe,
1009 int num, struct mark *m, const char *str,
1010 int num2, struct mark *m2, const char *str2,
1011 int x, int y, struct command *comm2b)
1013 struct call_return cr = {};
1016 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1017 num2, m2, str2, x, y, &cr.c);
1021 struct call_return do_call_all(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);
1036 char *do_call_str(enum target_type type, struct pane *home,
1037 struct command *comm2a,
1038 const char *key safe, struct pane *focus safe,
1039 int num, struct mark *m, const char *str,
1040 int num2, struct mark *m2, const char *str2,
1041 int x, int y, struct command *comm2b)
1043 struct call_return cr = {};
1046 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1047 num2, m2, str2, x, y, &cr.c);
1055 struct call_return do_call_bytes(enum target_type type, struct pane *home,
1056 struct command *comm2a,
1057 const char *key safe, struct pane *focus safe,
1058 int num, struct mark *m, const char *str,
1059 int num2, struct mark *m2, const char *str2,
1060 int x, int y, struct command *comm2b)
1062 struct call_return cr = {};
1065 cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
1066 num2, m2, str2, x, y, &cr.c);
1074 struct xy pane_mapxy(struct pane *p safe, struct pane *target safe,
1075 short x, short y, bool clip)
1077 /* x and y are relative to p. The result xy is relative to target */
1080 /* This is a bit of a hack, but is needed to map lib-renderline
1081 * co-ordes to a pane which is parallel with the render-line
1082 * pane, but might be further from the root.
1083 * We move 'target' towards the root to a pane of exactly the
1084 * same size and position. This will not change a correct
1085 * result, and can make a correct result more likely.
1087 while (target->parent != target &&
1088 target->x == 0 && target->y == 0 &&
1089 target->parent->w == target->w &&
1090 target->parent->h == target->h)
1091 target = target->parent;
1093 while (p != target && p != p->parent) {
1094 if (clip && p->w > 0) {
1100 if (clip && p->h > 0) {
1115 struct xy pane_scale(struct pane *p safe)
1117 /* "scale" is roughly pixels-per-point * 1000
1118 * So 10*scale.x/100 is the width of a typical character in default font.
1119 * 10*scale.y/100 is the height.
1120 * scale.x should be passed to Draw:text-size and and Draw:text to get
1121 * correctly sized text
1123 * "scale:M" is provided by the display module and reports
1124 * the size of a captial M in default fond - width and height in pixels
1127 char *scM = pane_attr_get(p, "scale:M");
1135 sscanf(scM, "%dx%d", &mw, &mh) != 2 ||
1136 mw <= 0 || mh <= 0) {
1137 /* Fonts have fixed 1x1 size so scaling not supported */
1142 /* "scale" is a request to change from the default.
1143 * It can be a simple number, in which case it is 1000 times a scale
1144 * factor, so "500" would be half of default size.
1145 * It can be an x,y pair, e.g. "800x240".
1146 * This chooses a scale so that the given number of points, (1/10 of
1147 * size of default "M") will fit in the pane. If the pane is resized,
1148 * the scale will automatically adjust to fit the requested number
1151 sc = pane_attr_get(p, "scale");
1154 else if (sscanf(sc, "%dx%d", &w, &h) == 2) {
1155 /* choose scale so w,h point fits in pane */
1159 xscale = 1000 * p->w * 10 / mw / w;
1160 yscale = 1000 * p->h * 10 / mh / h;
1161 scale = (xscale < yscale) ? xscale : yscale;
1162 } else if (sscanf(sc, "%d", &scale) != 1)
1169 xy.x = scale * mw / 10;
1170 xy.y = scale * mh / 10;
1174 static inline unsigned int ts_to_ms(struct timespec *ts safe)
1176 return ts->tv_nsec / 1000 / 1000 + ts->tv_sec * 1000;
1179 bool pane_too_long(struct pane *p safe, unsigned int msec)
1182 unsigned int duration;
1184 if (p->timestamp == 0)
1186 if (p->timestamp == 1)
1188 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
1189 duration = ts_to_ms(&ts) - p->timestamp;
1192 if (duration <= msec)
1194 /* If running under gdb, then I was probaly delayed
1195 * by single-stepping, so don't through an error
1197 p->timestamp = ! debugger_is_present();
1198 return p->timestamp;
1201 void pane_set_time(struct pane *p safe)
1205 clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
1206 p->timestamp = ts_to_ms(&ts);
1207 if (p->timestamp <= 1)