]> git.neil.brown.name Git - edlib.git/blob - core-pane.c
Fix assorting indenting.
[edlib.git] / core-pane.c
1 /*
2  * Copyright Neil Brown ©2015-2019 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * panes for edlib.
6  *
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.
11  *
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.
16  *
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
21  *
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.
25  */
26
27 #define _GNU_SOURCE /*  for asprintf */
28 #include <stdlib.h>
29 #include <string.h>
30 #include <wchar.h>
31 #include <ctype.h>
32 #include <stdio.h>
33
34 #include "core.h"
35
36 MEMPOOL(pane);
37
38 static void pane_init(struct pane *p safe, struct pane *par)
39 {
40         if (par) {
41                 p->parent = par;
42                 list_add(&p->siblings, &par->children);
43         } else {
44                 p->parent = p;
45                 INIT_LIST_HEAD(&p->siblings);
46         }
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;
51         p->cx = p->cy = -1;
52         p->h = p->w = 0;
53         if (par) {
54                 /* reasonable defaults */
55                 p->w = par->w;
56                 p->h = par->h;
57         }
58         p->abs_z = p->abs_zhi = 0;
59         p->focus = NULL;
60         p->handle = NULL;
61         p->data = safe_cast NULL;
62         p->damaged = 0;
63         p->attrs = NULL;
64         if (par)
65                 pane_damaged(p, DAMAGED_SIZE);
66 }
67
68 static void __pane_check(struct pane *p safe)
69 {
70         struct pane *c;
71         list_for_each_entry(c, &p->children, siblings) {
72                 ASSERT(c->parent == p);
73                 __pane_check(c);
74         }
75 }
76
77 static void pane_check(struct pane *p safe)
78 {
79         __pane_check(pane_root(p));
80 }
81 /*
82  * pane_damaged: mark a pane as being 'damaged', and make
83  * sure all parents know about it.
84  */
85 void pane_damaged(struct pane *p, int type)
86 {
87         int z;
88         if (!p || (p->damaged | type) == p->damaged)
89                 return;
90         if (type & (type-1)) {
91                 /* multiple bits are set, handle
92                  * them separately
93                  */
94                 int b;
95                 for (b = 1; type; b <<= 1) {
96                         if (b & type)
97                                 pane_damaged(p, b);
98                         type &= ~b;
99                 }
100                 return;
101         }
102         p->damaged |= type;
103         if (type == DAMAGED_SIZE)
104                 pane_notify("Notify:resize", p);
105
106         z = p->z;
107         if (z < 0)
108                 /* light-weight pane - never propagate damage */
109                 return;
110         p = p->parent;
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;
117         else
118                 return;
119
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;
124                 p->damaged |= type;
125                 z = p->z;
126                 p = p->parent;
127         }
128 }
129
130 struct pane *safe __pane_register(struct pane *parent, short z,
131                                   struct command *handle safe,
132                                   void *data, short data_size)
133 {
134         struct pane *p;
135
136         alloc(p, pane);
137         pane_init(p, parent);
138         p->z = z;
139         p->handle = command_get(handle);
140         if (!data)
141                 /* type of 'data' should correlate with type of handle,
142                  * which should be parameterised...
143                  */
144                 p->data = handle;
145         else
146                 p->data = data;
147         p->data_size = data_size;
148         if (z >= 0) {
149                 if (parent && parent->focus == NULL)
150                         parent->focus = p;
151                 pane_call(parent, "ChildRegistered", p);
152         }
153         return p;
154 }
155
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.
159  *
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.
163  */
164 static void pane_do_resize(struct pane *p safe, int damage)
165 {
166         struct pane *c;
167         int nextz;
168         int abs_z = p->abs_z + 1;
169
170         if (p->damaged & DAMAGED_CLOSED) {
171                 p->abs_zhi = abs_z;
172                 return;
173         }
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);
177
178         damage |= p->damaged & (DAMAGED_SIZE | DAMAGED_SIZE_CHILD);
179         if (!damage &&
180             p->abs_z == p->parent->abs_z + abs(p->z))
181                 return;
182
183         if (p->focus == NULL) {
184                 list_for_each_entry(c, &p->children, siblings)
185                         if (c->z >= 0) {
186                                 p->focus = c;
187                                 break;
188                         }
189         }
190
191         if (damage & (DAMAGED_SIZE))
192                 if (pane_call(p, "Refresh:size", p, 0, NULL,
193                               NULL, damage) != 0)
194                         /* No need to propagate, just check on children */
195                         damage = 0;
196
197         nextz = 0;
198         while (nextz >= 0) {
199                 int z = nextz;
200                 int abs_zhi = abs_z;
201                 nextz = -1;
202                 list_for_each_entry(c, &p->children, siblings) {
203                         if (c->z < 0) {
204                                 c->abs_z = c->parent->abs_z;
205                                 continue;
206                         }
207                         if (c->z > z && (nextz == -1 || c->z < nextz))
208                                 nextz = c->z;
209                         if (c->z == z) {
210                                 if (c->abs_z != abs_z)
211                                         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;
215                         }
216                 }
217                 p->abs_zhi = abs_zhi;
218                 abs_z = abs_zhi + 1;
219         }
220         if (p->damaged & DAMAGED_SIZE) {
221                 p->damaged &= ~(DAMAGED_SIZE | DAMAGED_SIZE_CHILD);
222                 p->damaged |= DAMAGED_CONTENT | DAMAGED_CHILD;
223         } else {
224                 p->damaged &= ~DAMAGED_SIZE_CHILD;
225                 p->damaged |= DAMAGED_CHILD;
226         }
227 }
228
229 static void pane_do_refresh(struct pane *p safe, int damage)
230 {
231         struct pane *c;
232         struct list_head *tmp;
233         int sent = 0;
234
235         if (p->damaged & DAMAGED_CLOSED)
236                 return;
237
238         damage |= p->damaged & (DAMAGED_CHILD|DAMAGED_CONTENT|DAMAGED_CURSOR);
239         p->damaged &= ~(DAMAGED_CHILD|DAMAGED_CONTENT|DAMAGED_CURSOR);
240         if (!damage)
241                 return;
242         list_for_each_entry_safe(c, tmp, &p->children, siblings)
243                 if (c->z >= 0) {
244                         sent = 1;
245                         pane_do_refresh(c, damage);
246                 }
247         if (!sent && damage & (DAMAGED_NEED_CALL)) {
248                 if (damage & DAMAGED_CONTENT)
249                         damage |= DAMAGED_CURSOR;
250                 call("Refresh", p, 0, NULL, NULL, damage);
251         }
252
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);
257         }
258 }
259
260 static void pane_do_review(struct pane *p safe, int damage)
261 {
262         struct pane *c;
263         int sent = 0;
264
265         if (p->damaged & DAMAGED_CLOSED)
266                 return;
267
268         damage |= p->damaged & (DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
269         p->damaged &= ~(DAMAGED_VIEW|DAMAGED_VIEW_CHILD);
270         if (!damage)
271                 return;
272         list_for_each_entry(c, &p->children, siblings)
273                 if (c->z >= 0) {
274                         sent = 1;
275                         pane_do_review(c, damage);
276                 }
277         if (!sent && damage & (DAMAGED_VIEW))
278                 call("Refresh:view", p, 0, NULL, NULL, damage);
279 }
280
281 void pane_refresh(struct pane *p safe)
282 {
283         int cnt = 5;
284         if (p->parent == p)
285                 p->abs_z = 0;
286
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);
291         }
292         if (p->damaged)
293                 fprintf(stderr,
294                         "WARNING %sroot pane damaged after refresh: %d\n",
295                         p->parent != p ? "":"non-", p->damaged);
296 }
297
298 void pane_add_notify(struct pane *target safe, struct pane *source safe,
299                      const char *msg safe)
300 {
301         struct notifier *n;
302
303         list_for_each_entry(n, &source->notifiees, notifier_link)
304                 if (n->notifiee == target &&
305                     strcmp(msg, n->notification) == 0)
306                         /* Already notifying */
307                         return;
308
309         alloc(n, pane);
310
311         n->notifiee = target;
312         n->notification = strdup(msg);
313         n->noted = 1;
314         list_add(&n->notifier_link, &source->notifiees);
315         list_add(&n->notifiee_link, &target->notifiers);
316 }
317
318 void pane_drop_notifiers(struct pane *p safe, char *notification)
319 {
320         struct list_head *t;
321         struct notifier *n;
322
323         list_for_each_entry_safe(n, t, &p->notifiers, notifiee_link) {
324
325                 if (notification && strcmp(notification, n->notification) != 0)
326                         continue;
327                 list_del_init(&n->notifiee_link);
328                 list_del_init(&n->notifier_link);
329                 free(n->notification);
330                 free(n);
331         }
332 }
333
334 static void pane_notify_close(struct pane *p safe)
335 {
336         while (!list_empty(&p->notifiees)) {
337                 struct notifier *n = list_first_entry(&p->notifiees,
338                                                       struct notifier,
339                                                       notifier_link);
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);
345                 free(n);
346         }
347 }
348
349 int do_pane_notify(struct pane *home, const char *notification safe,
350                    struct pane *p safe,
351                    int num, struct mark *m, const char *str,
352                    int num2, struct mark *m2, const char *str2,
353                    struct command *comm2)
354 {
355         /* Return the largest absolute return value. If no notifiees are found.
356          * return 0
357          */
358         int ret = 0;
359         int cnt = 0;
360         struct notifier *n, *n2;
361
362         if (!home)
363                 home = p;
364         /* FIXME why no error below */
365         list_for_each_entry_reverse(n, &home->notifiees, notifier_link)
366                 if (strcmp(n->notification, notification) == 0) {
367                         if (n->noted == 2)
368                                 /* Nested notification - fail */
369                                 return Efail;
370                         n->noted = 0;
371                 }
372 restart:
373         list_for_each_entry(n, &home->notifiees, notifier_link) {
374                 if (n->noted)
375                         continue;
376                 if (strcmp(n->notification, notification) == 0) {
377                         int r;
378                         n->noted = 2;
379                         r = pane_call(n->notifiee, notification, p,
380                                       num, m, str,
381                                       num2, m2, str2, cnt,ret, comm2);
382                         if (abs(r) > abs(ret))
383                                 ret = r;
384                         cnt += 1;
385                         /* Panes might have been closed or notifications removed
386                          * so nothing can be trusted... except this home pane
387                          * had better still exist.
388                          */
389                         list_for_each_entry(n2, &home->notifiees, notifier_link)
390                                 if (n2 == n && n->noted == 2) {
391                                         /* Still safe .. */
392                                         n->noted = 1;
393                                         continue;
394                                 }
395                         /* 'n' has been removed, restart */
396                         goto restart;
397                 }
398         }
399         return ret;
400 }
401
402 void pane_close(struct pane *p safe)
403 {
404         struct pane *c;
405         struct pane *ed;
406
407         if (p->damaged & DAMAGED_CLOSED)
408                 return;
409         p->damaged |= DAMAGED_CLOSED;
410         pane_check(p);
411
412         ed = pane_root(p);
413
414         pane_drop_notifiers(p, NULL);
415
416         if (!(p->parent->damaged & DAMAGED_CLOSED))
417                 pane_call(p->parent, "ChildClosed", p);
418
419         list_del_init(&p->siblings);
420
421 restart:
422         list_for_each_entry(c, &p->children, siblings) {
423                 if (c->damaged & DAMAGED_CLOSED)
424                         continue;
425                 pane_close(c);
426                 goto restart;
427         }
428
429         if (p->parent->focus == p) {
430                 pane_damaged(p->parent, DAMAGED_CURSOR);
431                 p->parent->focus = NULL;
432         }
433         pane_notify_close(p);
434         pane_call(p, "Close", p);
435
436         if (p->z >= 0)
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.
440          */
441         if (ed != p) {
442                 p->damaged |= DAMAGED_DEAD;
443                 editor_delayed_free(ed, p);
444         } else {
445                 pane_call(p, "Free", p);
446                 command_put(p->handle);
447                 p->handle = NULL;
448                 attr_free(&p->attrs);
449                 free(p);
450         }
451 }
452
453 void pane_resize(struct pane *p safe, int x, int y, int w, int h)
454 {
455         int damage = 0;
456
457         if (x >= 0 &&
458             (p->x != x || p->y != y)) {
459                 damage |= DAMAGED_CONTENT | DAMAGED_SIZE;
460                 p->x = x;
461                 p->y = y;
462         }
463         if (w > 0 &&
464             (p->w != w || p->h != h)) {
465                 damage |= DAMAGED_SIZE;
466                 p->w = w;
467                 p->h = h;
468         }
469         if (p->w < 0 || p->h < 0)
470                 /* FIXME something more gentle */
471                 abort();
472         if (p->w <= 0)
473                 p->w = 1;
474         if (p->h <= 0)
475                 p->h = 1;
476         pane_damaged(p, damage);
477 }
478
479 void pane_reparent(struct pane *p safe, struct pane *newparent safe)
480 {
481         /* detach p from its parent and attach beneath its sibling newparent */
482         int replaced = 0;
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);
493                 replaced = 1;
494         }
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);
501         if (replaced)
502                 pane_call(newparent->parent, "ChildReplaced", newparent);
503 }
504
505 void pane_move_after(struct pane *p safe, struct pane *after)
506 {
507         /* Move 'p' after 'after', or if !after, move to start */
508         if (p == p->parent || p == after)
509                 return;
510         if (after) {
511                 if (p->parent != after->parent)
512                         return;
513                 list_move(&p->siblings, &after->siblings);
514         } else {
515                 list_move(&p->siblings, &p->parent->children);
516         }
517 }
518
519 void pane_subsume(struct pane *p safe, struct pane *parent safe)
520 {
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
525          */
526         void *data;
527         struct command *handle;
528         struct pane *c;
529
530         list_del_init(&p->siblings);
531         if (p->parent->focus == p) {
532                 pane_damaged(p->parent, DAMAGED_CURSOR);
533                 p->parent->focus = NULL;
534         }
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);
539                 c->parent = parent;
540                 parent->damaged |= c->damaged;
541         }
542         parent->focus = p->focus;
543
544         handle = parent->handle;
545         parent->handle = p->handle;
546         p->handle = handle;
547
548         data = parent->data;
549         parent->data = p->data;
550         p->data = data;
551
552         parent->damaged |= p->damaged;
553
554         pane_close(p);
555 }
556
557 int pane_masked(struct pane *p safe, short x, short y, short abs_z,
558                 short *w, short *h)
559 {
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
565          * efficiently.
566          */
567         struct pane *c;
568         /* calculate the upper bounds */
569         int xh = x + (w ? *w : 1);
570         int yh = y + (h ? *h : 1);
571
572         if (x >= p->x+p->w || y >= p->y+p->h)
573                 /* x,y is beyond this pane, no overlap possible */
574                 return 0;
575         if (xh <= p->x || yh <= p->y)
576                 /* area is before this pane, no over lap possible */
577                 return 0;
578
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 */
583                         return 1;
584                 /* pane must just mask some of the region beyond x,y */
585                 if (w && *w > p->x - x)
586                         *w = p->x - x;
587                 if (h && *h > p->y - y)
588                         *h = p->y - y;
589
590                 return 0;
591         }
592         /* This pane doesn't mask (same z level) but a child still could */
593         x -= p->x;
594         y -= p->y;
595         list_for_each_entry(c, &p->children, siblings)
596                 if (pane_masked(c, x, y, abs_z, w, h))
597                         return 1;
598
599         return 0;
600 }
601
602 void pane_focus(struct pane *focus)
603 {
604         struct pane *p = focus;
605         if (!p)
606                 return;
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.
611          */
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;
618                         while (old->focus)
619                                 old = old->focus;
620                         call("pane:defocus", old);
621                 }
622                 p = p->parent;
623         }
624         call("pane:refocus", focus);
625 }
626
627 char *pane_attr_get(struct pane *p, const char *key safe)
628 {
629         while (p) {
630                 char *a = attr_find(p->attrs, key);
631
632                 if (a)
633                         return a;
634                 a = CALL(strsave, pane, p, "get-attr", p, 0, NULL, key);
635                 if (a)
636                         return a;
637                 if (p == p->parent)
638                         return NULL;
639                 p = p->parent;
640         }
641         return NULL;
642 }
643
644 char *pane_mark_attr(struct pane *p safe, struct mark *m safe,
645                      const char *key safe)
646 {
647         return call_ret(strsave, "doc:get-attr", p, 0, m, key);
648 }
649
650 void pane_clone_children(struct pane *from, struct pane *to)
651 {
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
655          */
656         struct pane *c;
657
658         if (!from || !to)
659                 return;
660         list_for_each_entry(c, &from->children, siblings) {
661                 if (c->z > 0)
662                         continue;
663                 pane_call(c, "Clone", to);
664         }
665 }
666
667 struct pane *pane_my_child(struct pane *p, struct pane *c)
668 {
669         while (c && c->parent != p) {
670                 if (c->parent == c)
671                         return NULL;
672                 c = c->parent;
673         }
674         return c;
675 }
676
677 DEF_CMD(take_simple)
678 {
679         struct call_return *cr = container_of(ci->comm, struct call_return, c);
680         cr->p = ci->focus;
681         cr->m = ci->mark;
682         cr->m2 = ci->mark2;
683         cr->i = ci->num;
684         cr->i2 = ci->num2;
685         cr->x = ci->x;
686         cr->y = ci->y;
687         cr->comm = ci->comm2;
688         cr->s = strsave(ci->focus, ci->str);
689         return 1;
690 }
691
692 DEF_CMD(take_str)
693 {
694         struct call_return *cr = container_of(ci->comm, struct call_return, c);
695
696         if (!ci->str)
697                 return 0;
698         cr->s = strdup(ci->str);
699         return 1;
700 }
701
702
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)
710 {
711         struct call_return cr = {};
712
713         cr.c = take_simple;
714         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
715                              num2, m2, str2, x, y, &cr.c, ccache);
716         if (cr.ret < 0)
717                 return NULL;
718         return cr.p;
719 }
720
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)
728 {
729         struct call_return cr = {};
730
731         cr.c = take_simple;
732         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
733                              num2, m2, str2, x, y, &cr.c, ccache);
734         if (cr.ret < 0)
735                 return NULL;
736         return cr.m;
737 }
738
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)
746 {
747         struct call_return cr = {};
748
749         cr.c = take_simple;
750         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
751                              num2, m2, str2, x, y, &cr.c, ccache);
752         if (cr.ret < 0)
753                 return NULL;
754         return cr.m2;
755 }
756
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)
764 {
765         struct call_return cr = {};
766
767         cr.c = take_simple;
768         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
769                              num2, m2, str2, x, y, &cr.c, ccache);
770         if (cr.ret < 0)
771                 return NULL;
772         return cr.comm;
773 }
774
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)
782 {
783         struct call_return cr = {};
784
785         cr.c = take_simple;
786         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
787                              num2, m2, str2, x, y, &cr.c, ccache);
788         return cr.s;
789 }
790
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)
798 {
799         struct call_return cr = {};
800
801         cr.c = take_simple;
802         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
803                              num2, m2, str2, x, y, &cr.c, ccache);
804         return cr;
805 }
806
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)
814 {
815         struct call_return cr = {};
816
817         cr.c = take_str;
818         cr.ret = do_call_val(type, home, comm2a, key, focus, num, m, str,
819                              num2, m2, str2, x, y, &cr.c, ccache);
820         if (cr.ret < 0) {
821                 free(cr.s);
822                 return NULL;
823         }
824         return cr.s;
825 }
826
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)
830 {
831         while (p) {
832                 if (p->w > 0 && *x + *w > p->w)
833                         *w = p->w - *x;
834                 if (p->h > 0 && *y + *h > p->h)
835                         *h = p->h - *y;
836                 *x += p->x;
837                 *y += p->y;
838                 if (p->parent == p)
839                         break;
840                 p = p->parent;
841         }
842 }
843
844 /* Convert absolute c-ords to relative */
845 void pane_relxy(struct pane *p, short *x safe, short *y safe)
846 {
847         while (p) {
848                 *x -= p->x;
849                 *y -= p->y;
850                 if (p->parent == p)
851                         break;
852                 p = p->parent;
853         }
854 }
855
856 void pane_map_xy(struct pane *orig, struct pane *target,
857                  short *x safe, short *y safe)
858 {
859         short w=0, h=0;
860         if (orig != target) {
861                 pane_absxy(orig, x, y, &w, &h);
862                 pane_relxy(target, x, y);
863         }
864 }
865
866 struct xy pane_scale(struct pane *p safe)
867 {
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
873          *
874          */
875         char *scM = pane_attr_get(p, "scale:M");
876         char *sc;
877         struct xy xy;
878         int w,h;
879         int mw, mh;
880         int scale;
881
882         if (!scM ||
883             sscanf(scM, "%dx%d", &mw, &mh) != 2 ||
884             mw <= 0 || mh <= 0) {
885                 /* Fonts have fixed 1x1 size so scaling not supported */
886                 xy.x = 100;
887                 xy.y = 100;
888                 return xy;
889         }
890         sc = pane_attr_get(p, "scale");
891         if (sc == NULL)
892                 scale = 1000;
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 */
896                 int xscale, yscale;
897                 if (w <= 0) w = 1;
898                 if (h <= 0) h = 1;
899                 xscale = 1000 * p->w * 10 / mw / w;
900                 yscale = 1000 * p->h * 10 / mh / h;
901                 if (sc[0] == 'x')
902                         /* Old style where 'y' was in 'width' units... */
903                         yscale *= 2;
904                 scale = (xscale < yscale) ? xscale :  yscale;
905         } else if (sscanf(sc, "%d", &scale) != 1)
906                 scale = 1000;
907
908         if (scale < 10)
909                 scale = 10;
910         if (scale > 100000)
911                 scale = 100000;
912         xy.x = scale * mw / 10;
913         xy.y = scale * mh / 10;
914         return xy;
915 }