2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Rendering for any document which presents as a sequence of lines.
7 * The underlying document, or an intervening filter, must return lines of
8 * text in response to the "doc:render-line" command.
9 * This takes a mark and moves it to the end of the rendered line
10 * so that another call will produce another line.
11 * "doc:render-line" must always return a full line including '\n'
12 * unless the result would be bigger than the 'max' passed in ->num or
13 * ->num < 0. In these cases it can stop before a '\n'. In each case,
14 * the mark is moved to the end of the region that was rendered;
15 * This allows a mark to be found for a given character position.
16 * If mark2 is given, the offset in the rendering when mark2 is reached
17 * is reported as ->num in the callback.
18 * For the standard 'render the whole line' functionality, ->num should
21 * The document or filter must also provide "doc:render-line-prev" which
22 * moves mark to a start-of-line. If num is 0, then don't skip over any
23 * newlines. If it is '1', then skip one newline.
25 * The returned line can contain attribute markings as <attr,attr>. </>
26 * is used to pop most recent attributes. << is used to include a
27 * literal '<'. Lines generally contain UTF-8. Control character '\n'
28 * is end of line and '\t' tabs 1-8 spaces. '\f' marks end of page -
29 * nothing after this will be displayed.
31 * Other control characters should be rendered as
32 * e.g. <fg:red>^X</> - in particular, nul must not appear in the line.
34 * We store all start-of-line the marks found while rendering a pane in
35 * a 'view' on the document. The line returned for a given mark is
36 * attached to extra space allocated for that mark. When a change
37 * notification is received for a mark we discard that string. So the
38 * string associated with a mark is certainly the string that would be
39 * rendered after that mark (though it may be truncated). The set of
40 * marks in a view should always identify exactly the set of lines to be
41 * displayed. Each mark should be at a start-of-line except possibly
42 * for the first and last. The first may be internal to a long line,
43 * but the line rendering attached will always continue to the
44 * end-of-line. We record the number of display lines in that first
46 * The last mark may also be mid-line, and it must never have an
48 * In the worst case of there being no newlines in the document, there
49 * will be precisely two marks: one contains a partial line and one that
50 * marks the end of that line. When point moves outside that range a
51 * new start will be chosen before point using "doc:render-line-prev"
52 * and the old start is discarded.
54 * To render the pane we:
55 * 1/ call 'render-line-prev' on a mark at the point and look for that mark
57 * 2/ If the mark matches and has a string, we have a starting point,
58 * else we call "doc:render-line" and store the result, thus
59 * producing a starting point. We determine how many display lines
60 * are needed to display this text-line and set 'y' accordingly.
61 * At this point we have two marks: start and end, with known text of known
63 * 3/ Then we move outwards, back from the first mark and forward from
64 * the last mark. If we find a mark already in the view in the
65 * desired direction with text attached it is correct and we use
66 * that. Otherwise we find start (when going backwards) and render a
67 * new line. Any old mark that is in the range is discarded.
68 * 4/ When we have a full set of marks and the full height of the pane,
69 * we discard marks outside the range and start rendering from the
70 * top. ARG how is cursor drawn.
72 * If we already have correct marks on one side and not the other, we prefer
73 * to advance on that first side to maximize the amount of text that was common
74 * with the previous rendering of the page.
76 * Sometimes we need to render without a point. In this case we start
77 * at the first mark in the view and move forward. If we can we do this
78 * anyway, and only try the slow way if the target point wasn't found.
81 #define MARK_DATA_PTR struct pane
82 #define _GNU_SOURCE /* for asprintf */
85 #include <stdio.h> /* snprintf */
88 * All functions involved in sending Draw and size requests
89 * to the display are given two panes: p and focus.
90 * 'p' is the pane where the drawing happens. 'focus' is the
91 * leaf on the current stack.
92 * These are different when the drawing is segmented into regions
93 * of the target pane, with light-weight panes being used to avoid
94 * having to refresh the whole target pane when the only change is
96 * The calls to the display are home_calls with 'focus' as the home
97 * pane, and 'p' as the focus. The x,y co-ords are, as always,
98 * relative to the focus pane 'p'.
102 int top_sol; /* true when first mark is at a start-of-line */
104 int skip_height; /* Skip display-lines for first "line" */
105 int skip_line_height; /* height of lines in skip_height */
106 int tail_height; /* display lines at eop not display */
107 int cursor_line; /* line that contains the cursor starts
109 short target_x, target_y;
110 short i_moved; /* I moved cursor, so don't clear
116 short shift_left_last_refresh;
119 int repositioned; /* send "render:reposition" when we know
120 * full position again.
122 short lines; /* lines drawn before we hit eof */
123 short cols; /* columns used for longest line */
124 short margin; /* distance from top/bottom required for cursor */
125 bool background_drawn;
126 bool background_uniform;
128 /* If cursor not visible, we add this pane in bottom-right and place
131 struct pane *cursor_pane;
134 static void vmark_clear(struct mark *m safe)
137 pane_close(m->mdata);
142 static void vmark_free(struct mark *m safe)
148 static void vmark_set(struct pane *p safe, struct pane *focus safe,
149 struct mark *m safe, char *line safe)
152 m->mdata = call_ret(pane, "attach-renderline", p, -1);
154 pane_call(m->mdata, "render-line:set", focus, -1, NULL, line);
157 static void vmark_invalidate(struct mark *m safe)
160 pane_damaged(m->mdata, DAMAGED_VIEW);
163 static bool vmark_is_valid(struct mark *m safe)
165 return mark_valid(m) && m->mdata && !(m->mdata->damaged & DAMAGED_VIEW);
168 /* Returns 'true' at end-of-page */
169 static int _measure_line(struct pane *p safe, struct pane *focus safe,
170 struct mark *mk safe, short cursor_offset,
173 struct pane *hp = mk->mdata;
174 struct call_return cr;
176 if (!mark_valid(mk) || !hp)
178 pane_resize(hp, hp->x, hp->y, p->w, p->h);
179 cr = pane_call_ret(all, hp, "render-line:measure",
180 focus, cursor_offset);
183 /* end-of-page flag */
186 #define measure_line(...) VFUNC(measure_line, __VA_ARGS__)
187 static inline int measure_line3(struct pane *p safe, struct pane *focus safe,
188 struct mark *mk safe)
190 return _measure_line(p, focus, mk, -1, NULL);
192 static inline int measure_line4(struct pane *p safe, struct pane *focus safe,
193 struct mark *mk safe, short cursor_offset)
195 return _measure_line(p, focus, mk, cursor_offset, NULL);
197 static inline int measure_line5(struct pane *p safe, struct pane *focus safe,
198 struct mark *mk safe, short cursor_offset,
201 return _measure_line(p, focus, mk, cursor_offset, cursor_attr);
204 /* Returns offset of posx,posy */
205 static int find_xy_line(struct pane *p safe, struct pane *focus safe,
206 struct mark *mk safe, short posx, short posy,
209 struct pane *hp = mk->mdata;
210 struct call_return cr;
214 cr = pane_call_ret(all, hp,
215 "render-line:findxy",
219 posx - hp->x, posy - hp->y);
223 return cr.ret > 0 ? (cr.ret - 1) : -1;
226 static void draw_line(struct pane *p safe, struct pane *focus safe,
227 struct mark *mk safe, short offset, bool refresh_all)
229 struct pane *hp = mk->mdata;
232 (refresh_all || hp->damaged & DAMAGED_REFRESH)) {
233 hp->damaged &= ~DAMAGED_REFRESH;
234 pane_call(hp, "render-line:draw", focus, offset);
238 static struct mark *call_render_line_prev(struct pane *p safe,
245 if (m->viewnum < 0) {
249 ret = call("doc:render-line-prev", p, n, m);
251 /* if n>0 we can fail because start-of-file was found before
252 * any newline. In that case ret == Efail, and we return NULL.
255 *found = (ret == Efail);
260 /* current line is start-of-file */
265 m2 = vmark_matching(m);
273 static void call_render_line(struct pane *home safe, struct pane *p safe,
274 struct mark *start safe, struct mark **end)
279 if (vmark_is_valid(start))
282 m = mark_dup_view(start);
283 if (doc_following(p, m) == WEOF) {
284 /* We only create a subpane for EOF when it is at start
285 * of line, else it is included in the preceding line.
287 call("doc:render-line-prev", p, 0, m);
288 if (!mark_same(m, start)) {
295 s = call_ret(strsave, "doc:render-line", p, -1, m);
297 if (!mark_valid(start)) {
302 vmark_set(home, p, start, s);
304 m2 = vmark_matching(m);
309 /*FIXME shouldn't be needed */
312 /* Any mark between start and m2 must be discarded,
314 while ((m = vmark_next(start)) != NULL &&
316 if (end && m == *end)
320 /* Any mark at same location as m2 must go too. */
321 while ((m = vmark_next(m2)) != NULL &&
323 if (end && m == *end)
327 /* Any mark at same location as start must go too. */
328 while ((m = vmark_prev(start)) != NULL &&
329 mark_same(m, start)) {
339 static struct mark *call_render_line_offset(struct pane *p safe,
340 struct mark *start safe, int offset)
344 m = mark_dup_view(start);
345 if (call_comm("doc:render-line", p, &no_save, offset, m) <= 0) {
360 static int call_render_line_to_point(struct pane *p safe, struct mark *pm safe,
361 struct mark *start safe)
364 struct mark *m = mark_dup_view(start);
366 len = call_comm("doc:render-line", p, &get_offset, -1, m, NULL, 0, pm);
374 /* Choose a new set of lines to display, and mark each one with a line marker.
375 * We start at pm and move both backwards and forwards one line at a time.
376 * We stop moving in one of the directions when
377 * - we hit start/end of file
378 * - when the edge in the *other* direction enters the previously visible
379 * area (if there was one). This increases stability of display when
380 * we move off a line or 2.
381 * - when we reach the given line count (vline). A positive count restricts
382 * backward movement, a negative restricts forwards movement.
385 static bool step_back(struct pane *p safe, struct pane *focus safe,
386 struct mark **startp safe, struct mark **endp,
387 short *y_pre safe, short *line_height_pre safe)
389 /* step backwards moving start */
390 struct rl_data *rl = p->data;
392 bool found_start = False;
393 struct mark *start = *startp;
397 m = call_render_line_prev(focus, mark_dup_view(start),
400 /* no text before 'start' */
405 call_render_line(p, focus, start, endp);
406 measure_line(p, focus, start);
407 h = start->mdata ? start->mdata->h : 0;
411 attr_find_int(start->mdata->attrs,
420 static bool step_fore(struct pane *p safe, struct pane *focus safe,
421 struct mark **startp safe, struct mark **endp safe,
422 short *y_post safe, short *line_height_post safe)
424 struct mark *end = *endp;
428 call_render_line(p, focus, end, startp);
429 measure_line(p, focus, end);
431 *y_post = end->mdata->h;
432 if (*y_post > 0 && end->mdata)
434 attr_find_int(end->mdata->attrs,
436 if (!end->mdata || !end->mdata->h)
439 end = vmark_next(end);
441 if (p->h >= *line_height_post *2)
449 static int consume_space(struct pane *p safe, int y,
450 short *y_prep safe, short *y_postp safe,
451 short *lines_above safe, short *lines_below safe,
452 int found_start, int found_end,
453 int line_height_pre, int line_height_post,
457 int y_post = *y_postp;
459 if (y_pre > 0 && y_post > 0 && !found_start && !found_end) {
460 int consume = (y_post < y_pre
461 ? y_post : y_pre) * 2;
463 if (consume > p->h - y)
465 if (line_at_a_time && consume > 2*line_height_pre &&
467 consume = 2*line_height_pre;
468 if (line_at_a_time && consume > 2*line_height_post &&
469 line_height_post > 0)
470 consume = 2*line_height_post;
471 if (y_pre > y_post) {
472 above = consume - (consume/2);
475 below = consume - (consume/2);
480 *lines_above += above / (line_height_pre?:1);
482 *lines_below += below / (line_height_post?:1);
483 /* We have just consumed all of one of
484 * lines_{above,below} so they are no longer
488 if (found_end && y_pre && !found_start) {
489 int consume = p->h - y;
492 if (line_at_a_time && consume > line_height_pre &&
494 consume = line_height_pre;
497 *lines_above += consume / (line_height_pre?:1);
499 if (found_start && y_post && !found_end) {
500 int consume = p->h - y;
501 if (consume > y_post)
503 if (line_at_a_time && consume > line_height_post &&
504 line_height_post > 0)
505 consume = line_height_post;
508 *lines_below += consume / (line_height_post?:1);
516 * Choose new start/end to be displayed in the given pane.
517 * 'pm' must be displayed, and if vline is not NO_NUMERIC,
518 * pm should be displayed on that line of the display, where
519 * negative numbers count from the bottom of the page.
520 * Otherwise pm should be at least rl->margin from top and bottom,
521 * but in no case should start-of-file be *after* top of display.
522 * If there is an existing display, move the display as little as
523 * possible while complying with the above.
525 * We start at 'pm' and move both forward and backward one line at a
526 * time measuring each line and assessing space used.
527 * - If the space above pm reaches positive vline, that will be top.
528 * - If the space below reaches negative vline, that will likely be bottom
529 * - If pm was before old top and we reach the old top going down,
530 * and if space measured before pm has reached ->margin, we stop
532 * - If pm was after old bottom and we reach the old bottom going up
533 * and if space measured after pm has reached ->margin, we stop
536 * If we decide to stop moving in both directions, but have not
537 * reached EOF or full height of display, keep moving downwards.
539 * "start" is a mark at the start of the first line we currently
540 * intend to display, and y_pre is the number of pixel from the top
541 * of the display of that line, to the top pixel that will be displayed.
542 * We only move 'start' backward when y_pre is zero, and initially y_pre
543 * is the full height of that line.
545 * Similarly "end" is the start of the last line we currently intend
546 * to display, and y_post is the number of pixel from the bottom of that display
547 * up to the point we currently intend to display. We only move "end" forward
548 * when y_post is zero, and when we do we set y_post to the full height of the
551 * Until we decide on the start or end (found_start, found_end), we
552 * repeatedly add equal parts of y_pre and y_post into the total to
553 * be display - consume_space() does this. The space removed from y_pre
554 * and y_post is added to 'y' - the total height.
555 * It is also included into lines_above and lines_below which count text lines,
556 * rather than pixels, using line_height_pre and line_height_post as scale
557 * factors. These are used to determine when vline or rl->margin requirements
560 static void find_lines(struct mark *pm safe, struct pane *p safe,
561 struct pane *focus safe,
564 struct rl_data *rl = p->data;
565 /* orig_top/bot bound what is currently displayed and
566 * are used to determine if the display has been repositioned.
567 * orig_bot is *after* the last displayed line. Its ->mdata
570 struct mark *orig_top, *orig_bot;
571 /* top and bot are used to enhance stability. They are NULL
572 * if vline is given, else they match orig_top/bot.
574 struct mark *top, *bot;
576 /* Current estimate of new display. From y_pre pixels down
577 * from the top of line at 'start', to y_post pixels up
578 * from the end of the line before 'end' there are 'y'
579 * pixel lines that we have committed to display.
581 struct mark *start, *end; // current estimate for new display
582 short y_pre = 0, y_post = 0;
584 /* Number of text-lines in the committed region above or below
585 * the baseline of the line containing pm. These lines might not
586 * all be the same height. line_height_pre/post are the heights of
587 * start and end-1 so changes in y_pre/y_post can be merged into these
590 short lines_above = 0, lines_below = 0; /* distance from start/end
593 short line_height_pre = 1, line_height_post = 1;
595 short offset; // pos of pm while measureing the line holding the cursor.
596 /* We set found_start we we don't want to consider anything above the
597 * top that we currently intend to display. Once it is set,
598 * 'start', y_pre, lines_above are all frozen.
599 * Similarly once found_end is set we freeze end, y_pos, lines_below,
600 * but we mught unfreeze those if there is room for more text at end of
602 * found_start is set:
603 * - when y_pre is zero and start is at top of file
604 * - when lines_above reaches positive vline
605 * - when intended display has grown down into the previous
606 * display. This means we have added enough lines above and
607 * don't want to scroll the display more than we need.
608 * - When we hit unexpected errors moving backwards
610 * - when we hit end-of-file
611 * - when lines_below reached -vline
612 * - when the top of the intended display overlaps the
615 bool found_start = False, found_end = False;
617 orig_top = vmark_first(focus, rl->typenum, p);
618 orig_bot = vmark_last(focus, rl->typenum, p);
619 /* Protect top/bot from being freed by call_render_line */
621 orig_top = mark_dup(orig_top);
623 orig_bot = mark_dup(orig_bot);
625 start = vmark_new(focus, rl->typenum, p);
628 /* FIXME why is this here. We set ->repositioned at the end
629 * if the marks move. Maybe we need to check if y_pre moves too.
631 rl->repositioned = 1;
632 mark_to_mark(start, pm);
633 start = call_render_line_prev(focus, start, 0, &rl->top_sol);
637 /* Render the cursor line, and find where the cursor is. */
638 offset = call_render_line_to_point(focus, pm, start);
639 call_render_line(p, focus, start, NULL);
640 end = vmark_next(start);
641 /* Note: 'end' might be NULL if 'start' is end-of-file, otherwise
642 * call_render_line() will have created 'end' if it didn't exist.
645 if (!rl->shift_locked)
649 struct pane *hp = start->mdata;
651 found_end = measure_line(p, focus, start, offset) & 2;
653 curs_width = pane_attr_get_int(
654 start->mdata, "curs_width", 1);
657 while (!rl->do_wrap && !rl->shift_locked &&
658 hp->cx + curs_width >= p->w) {
659 int shift = 8 * curs_width;
662 rl->shift_left += shift;
663 measure_line(p, focus, start, offset);
665 /* ->cy is top of cursor, we want to measure from bottom */
666 line_height_pre = attr_find_int(start->mdata->attrs, "line-height");
667 if (line_height_pre < 1)
669 /* We now have a better estimate than '1' */
670 line_height_post = line_height_pre;
671 y_pre = start->mdata->cy + line_height_pre;
672 y_post = start->mdata->h - y_pre;
674 /* Should never happen */
679 /* When cursor at EOF, leave 10% height of display
680 * blank at bottom to make this more obvious - unless
681 * the display is so small that might push the last line partly
682 * off display at the top.
684 if (p->h > line_height_pre * 2)
687 /* Small display, no space at EOF */
693 if (rl->header && rl->header->mdata)
694 y = rl->header->mdata->h;
696 /* We have start/end of the focus line. When rendered this,
697 * plus header and eof-footer would use y_pre + y + y_post
701 if (vline != NO_NUMERIC) {
702 /* ignore current position - top/bot irrelevant */
710 while ((!found_start || !found_end) && y < p->h) {
711 if (vline != NO_NUMERIC) {
712 /* As lines_above/below measure from the baseline
713 * of the cursor line, and as we want to see the top
714 * of he cursor line as well, these two cases are
717 if (!found_start && vline > 0 &&
718 lines_above >= vline)
720 if (!found_end && vline < 0 &&
721 lines_below >= -vline-1)
724 if (!found_start && y_pre <= 0)
725 found_start = step_back(p, focus, &start, &end,
726 &y_pre, &line_height_pre);
728 if (found_end && y_post && bot &&
729 mark_ordered_or_same(start, bot))
730 /* Extra vertical space gets inserted after EOF when
731 * there is a long jump to get there, but if we hit 'bot'
732 * soon when searching back, we discard any unused space.
736 if (!found_end && bot &&
737 (!end || mark_ordered_or_same(bot, end)) &&
738 lines_below >= rl->margin)
739 if (mark_ordered_not_same(start, bot) ||
740 /* Overlap original from below, so prefer to
741 * maximize that overlap.
743 (mark_same(start, bot) &&
744 y_pre - rl->skip_height >= y_post))
745 /* No overlap in marks yet, but over-lap in
746 * space, so same result as above.
750 if (!found_end && y_post <= 0)
752 found_end = step_fore(p, focus, &start, &end,
753 &y_post, &line_height_post);
755 /* This test has "> rl->margin" while found_end test has
756 * ">= rl->margin" due to the asymmetry of measuring from the
757 * baseline, not the centreling, of the target text.
759 if (!found_start && top && end &&
760 mark_ordered_or_same(start, top) &&
761 lines_above > rl->margin)
762 if (mark_ordered_not_same(top, end) ||
763 (mark_same(top, end) &&
764 y_post - rl->tail_height >= y_pre))
767 y = consume_space(p, y, &y_pre, &y_post,
768 &lines_above, &lines_below,
769 found_start, found_end,
770 line_height_pre, line_height_post,
771 vline && vline != NO_NUMERIC);
773 /* We might need to continue downwards even after found_end
774 * if there is more space.
776 found_end = end == NULL;
777 while (!found_end && y < p->h) {
779 found_end = step_fore(p, focus, &start, &end,
780 &y_post, &line_height_post);
781 y = consume_space(p, y, &y_pre, &y_post,
782 &lines_above, &lines_below,
783 found_start, found_end,
784 line_height_pre, line_height_post,
785 vline && vline != NO_NUMERIC);
788 if (start->mdata && start->mdata->h <= y_pre) {
790 m = vmark_next(start);
797 rl->skip_height = y_pre;
798 rl->skip_line_height = line_height_pre;
799 rl->tail_height = y_post;
800 /* Now discard any marks outside start-end */
801 if (end && end->seq < start->seq)
802 /* something confused, make sure we don't try to use 'end' after
806 while ((m = vmark_prev(start)) != NULL)
810 while ((m = vmark_next(end)) != NULL)
818 if (rl->header && rl->header->mdata) {
819 y = rl->header->mdata->h;
820 rl->cols = pane_attr_get_int(rl->header->mdata, "width", 0);
822 y -= rl->skip_height;
823 for (m = vmark_first(focus, rl->typenum, p);
824 m && m->mdata ; m = vmark_next(m)) {
825 struct pane *hp = m->mdata;
827 if (pane_resize(hp, hp->x, y, hp->w, hp->h) &&
828 !rl->background_uniform)
829 pane_damaged(hp, DAMAGED_REFRESH);
831 cols = pane_attr_get_int(hp, "width", 0);
836 pane_damaged(p, DAMAGED_REFRESH);
837 m = vmark_first(focus, rl->typenum, p);
838 if (!m || !orig_top || !mark_same(m, orig_top))
839 rl->repositioned = 1;
840 m = vmark_last(focus, rl->typenum, p);
841 if (!m || !orig_bot || !mark_same(m, orig_bot))
842 rl->repositioned = 1;
849 DEF_CMD(cursor_handle)
854 static int render(struct mark *pm, struct pane *p safe,
855 struct pane *focus safe)
857 struct rl_data *rl = p->data;
860 struct xy scale = pane_scale(focus);
862 bool hide_cursor = False;
863 bool cursor_drawn = False;
864 bool refresh_all = rl->shift_left != rl->shift_left_last_refresh;
866 rl->shift_left_last_refresh = rl->shift_left;
867 s = pane_attr_get(focus, "hide-cursor");
868 if (s && strcmp(s, "yes") == 0)
872 m = vmark_first(focus, rl->typenum, p);
873 if (!rl->background_drawn) {
875 rl->background_uniform = True;
877 s = pane_attr_get(focus, "background");
878 if (s && strstarts(s, "call:")) {
879 home_call(focus, "Draw:clear", p, 0, NULL, "");
880 home_call(focus, s+5, p, 0, m);
882 rl->background_uniform = False;
883 } else if (rl->background_drawn)
886 home_call(focus, "Draw:clear", p, 0, NULL, "");
887 else if (strstarts(s, "color:")) {
891 home_call(focus, "Draw:clear", p, 0, NULL, a);
893 } else if (strstarts(s, "image:")) {
894 home_call(focus, "Draw:clear", p);
895 home_call(focus, "Draw:image", p, 16, NULL, s+6);
896 rl->background_uniform = False;
898 home_call(focus, "Draw:clear", p, 0, NULL, "");
899 rl->background_drawn = True;
901 if (rl->header && vmark_is_valid(rl->header)) {
902 struct pane *hp = rl->header->mdata;
903 draw_line(p, focus, rl->header, -1, refresh_all);
905 rl->cols = pane_attr_get_int(hp, "width", 0);
907 y -= rl->skip_height;
912 while (m && m->mdata) {
914 if (!hide_cursor && p->cx <= 0 && pm &&
915 mark_ordered_or_same(m, pm) &&
916 (!(m2 && doc_following(focus, m2) != WEOF) ||
917 mark_ordered_not_same(pm, m2))) {
919 struct pane *hp = m->mdata;
920 short len = call_render_line_to_point(focus, pm,
922 draw_line(p, focus, m, len, True);
923 rl->cursor_line = hp->y + hp->cy;
924 curs = pane_mapxy(hp, p, hp->cx, hp->cy, False);
925 if (hp->cx < 0 || hp->cx >= hp->w) {
934 draw_line(p, focus, m, -1, refresh_all);
937 int cols = pane_attr_get_int(m->mdata, "width", 0);
940 y = m->mdata->y + m->mdata->h;
944 if (m && !m->mdata && vmark_next(m))
945 LOG("render-lines: break in vmark sequence");
946 if (!cursor_drawn && !hide_cursor) {
947 /* Place cursor in bottom right */
948 struct pane *cp = rl->cursor_pane;
953 cp = pane_register(p, -1, &cursor_handle);
954 rl->cursor_pane = cp;
959 m2 = vmark_last(focus, rl->typenum, p);
961 while (m2 && mwidth <= 0) {
963 mwidth = pane_attr_get_int(
964 m2->mdata, "curs_width", -1);
965 lineheight = pane_attr_get_int(
966 m2->mdata, "line-height", -1);
981 home_call(focus, "Draw:clear", cp);
982 home_call(focus, "Draw:text", cp, 0, NULL, " ",
986 } else if (rl->cursor_pane) {
987 pane_close(rl->cursor_pane);
988 rl->cursor_pane = NULL;
993 DEF_CMD(render_lines_get_attr)
995 struct rl_data *rl = ci->home->data;
997 if (ci->str && strcmp(ci->str, "shift_left") == 0) {
999 if (rl->do_wrap && !rl->shift_locked)
1000 return comm_call(ci->comm2, "cb", ci->focus,
1002 snprintf(ret, sizeof(ret), "%d", rl->shift_left);
1003 return comm_call(ci->comm2, "cb", ci->focus, 0, NULL, ret);
1005 return Efallthrough;
1008 DEF_CMD(render_lines_point_moving)
1010 struct pane *p = ci->home;
1011 struct rl_data *rl = p->data;
1012 struct mark *pt = call_ret(mark, "doc:point", ci->home);
1015 if (!pt || ci->mark != pt)
1017 /* Stop igoring point, because it is probably relevant now */
1018 rl->ignore_point = 0;
1020 /* Someone else moved the point, so reset target column */
1022 m = vmark_at_or_before(ci->focus, pt, rl->typenum, p);
1024 /* End marker is no use, want to refresh last line */
1026 if (m && m->mdata) {
1027 pane_damaged(m->mdata, DAMAGED_REFRESH);
1028 pane_damaged(m->mdata->parent, DAMAGED_REFRESH);
1033 static int revalidate_start(struct rl_data *rl safe,
1034 struct pane *p safe, struct pane *focus safe,
1035 struct mark *start safe, struct mark *pm,
1039 bool on_screen = False;
1040 struct mark *m, *m2;
1041 bool found_end = False;
1045 /* This loop is fragile and sometimes spins. So ensure we
1046 * never loop more than 1000 times.
1048 if (pm && !rl->do_wrap && !rl->shift_locked && shifts++ < 1000) {
1051 /* Need to check if side-shift is needed on cursor line */
1053 call("doc:render-line-prev", focus, 0, m2);
1055 m = vmark_at_or_before(focus, m2, rl->typenum, p);
1058 if (m && refresh_all)
1059 vmark_invalidate(m);
1060 if (m && m->mdata && !vmark_is_valid(m)) {
1061 pane_damaged(p, DAMAGED_REFRESH);
1062 call("doc:render-line-prev", focus, 0, m);
1063 call_render_line(p, focus, m, &start);
1065 if (m && m->mdata) {
1066 struct pane *hp = m->mdata;
1068 int offset = call_render_line_to_point(focus,
1070 measure_line(p, focus, m, offset);
1071 prefix_len = pane_attr_get_int(
1072 m->mdata, "prefix_len", -1);
1073 curs_width = pane_attr_get_int(
1074 m->mdata, "curs_width", 1);
1076 while (hp->cx + curs_width > p->w && shifts++ < 1000) {
1077 int shift = 8 * curs_width;
1080 rl->shift_left += shift;
1081 measure_line(p, focus, m, offset);
1084 /* We shift right is cursor is off the left end, or if
1085 * doing so wouldn't hide anything on the right end
1087 cols = pane_attr_get_int(hp, "width", 0);
1088 while ((hp->cx < prefix_len
1089 || cols + curs_width * 8 + curs_width < p->w) &&
1090 rl->shift_left > 0 &&
1092 hp->cx + curs_width * 8 < p->w) {
1093 int shift = 8 * curs_width;
1094 if (shift > rl->shift_left)
1095 shift = rl->shift_left;
1096 rl->shift_left -= shift;
1097 measure_line(p, focus, m, offset);
1098 cols = pane_attr_get_int(hp, "width", 0);
1105 struct pane *hp = rl->header->mdata;
1107 measure_line(p, focus, rl->header);
1109 pane_resize(hp, hp->x, y, hp->w, hp->h);
1114 y -= rl->skip_height;
1115 start_of_file = doc_prior(focus, start) == WEOF;
1116 for (m = start; m && !found_end && y < p->h; m = vmark_next(m)) {
1120 vmark_invalidate(m);
1121 call_render_line(p, focus, m, NULL);
1122 found = measure_line(p, focus, m);
1123 found_end = found & 2;
1125 if (!mark_valid(m) || !hp)
1129 pane_damaged(p, DAMAGED_REFRESH);
1130 hp->damaged &= ~DAMAGED_SIZE;
1131 pane_resize(hp, hp->x, y, hp->w, hp->h);
1132 if (hp->damaged & DAMAGED_SIZE && !rl->background_uniform)
1133 pane_damaged(hp, DAMAGED_REFRESH);
1137 /* The "found & 1" handles case when EOF is at the end
1138 * of a non-empty line.
1140 if (pm && m2 && mark_ordered_or_same(m, pm) &&
1141 (mark_ordered_not_same(pm, m2) ||
1142 (mark_same(pm, m2) && !(found & 1)))) {
1144 /* Cursor is on this line */
1145 int offset = call_render_line_to_point(focus,
1147 int lh = attr_find_int(hp->attrs,
1149 int cy = y - hp->h + hp->cy;
1152 measure_line(p, focus, m, offset);
1153 if (m == start && rl->skip_height > 0) {
1154 /* Point might be in this line, but off top
1157 if (hp->cy >= rl->skip_height + rl->margin)
1158 /* Cursor is visible on this line
1159 * and after margin from top.
1162 else if (start_of_file && rl->skip_height == 0)
1163 /* Cannot make more margin space */
1165 } else if (y >= p->h) {
1166 /* point might be in this line, but off end
1170 y - hp->h + hp->cy <= p->h - lh - rl->margin) {
1171 /* Cursor is on screen */
1174 } else if (rl->margin == 0)
1176 else if (cy >= rl->margin &&
1177 cy <= p->h - rl->margin - lh)
1178 /* Cursor at least margin from edge */
1183 rl->tail_height = p->h - y;
1185 rl->tail_height = 0;
1186 if (mark_valid(m)) {
1188 while (mark_valid(m2 = vmark_next(m)) && m2) {
1189 /* end of view has clearly changed */
1190 rl->repositioned = 1;
1194 if (!pm || on_screen) {
1195 if (rl->repositioned) {
1196 rl->repositioned = 0;
1197 call("render:reposition", focus,
1198 rl->lines, vmark_first(focus,
1201 rl->cols, vmark_last(focus,
1211 DEF_CMD(render_lines_revise)
1213 struct pane *p = ci->home;
1214 struct pane *focus = ci->focus;
1215 struct rl_data *rl = p->data;
1216 struct mark *pm = NULL;
1217 struct mark *m1, *m2;
1218 bool refresh_all = False;
1223 a = pane_attr_get(focus, "render-wrap");
1224 if (rl->do_wrap != (!a || strcmp(a, "yes") ==0)) {
1225 rl->do_wrap = (!a || strcmp(a, "yes") ==0);
1229 shift = pane_attr_get_int(focus, "shift-left", -1);
1231 if (rl->shift_left != shift)
1234 rl->shift_left = shift;
1235 rl->shift_locked = 1;
1237 if (rl->shift_locked)
1239 rl->shift_locked = 0;
1243 for (v = vmark_first(focus, rl->typenum, p);
1244 (v && v->mdata) ; v = vmark_next(v))
1245 pane_damaged(v->mdata, DAMAGED_REFRESH);
1248 rl->margin = pane_attr_get_int(focus, "render-vmargin", 0);
1249 if (rl->margin >= p->h/2)
1250 rl->margin = p->h/2;
1252 hdr = pane_attr_get(focus, "heading");
1258 rl->header = mark_new(focus);
1260 vmark_set(p, focus, rl->header, hdr);
1261 measure_line(p, focus, rl->header);
1263 } else if (rl->header) {
1264 vmark_free(rl->header);
1268 if (!rl->ignore_point)
1269 pm = call_ret(mark, "doc:point", focus);
1270 m1 = vmark_first(focus, rl->typenum, p);
1271 m2 = vmark_last(focus, rl->typenum, p);
1273 if (m1 && !vmark_is_valid(m1))
1274 /* newline before might have been deleted, better check */
1275 call("doc:render-line-prev", focus, 0, m1);
1276 // FIXME double check that we invalidate line before any change...
1279 (!pm || (mark_ordered_or_same(m1,pm)))) {
1280 /* We maybe be able to keep m1 as start, if things work out.
1281 * So check all sub-panes are still valid and properly
1284 if (revalidate_start(rl, p, focus, m1, pm, refresh_all))
1287 /* Need to find a new top-of-display */
1289 pm = call_ret(mark, "doc:point", focus);
1291 /* Don't know what to do here... */
1293 find_lines(pm, p, focus, NO_NUMERIC);
1294 rl->repositioned = 0;
1295 call("render:reposition", focus,
1296 rl->lines, vmark_first(focus, rl->typenum, p), NULL,
1297 rl->cols, vmark_last(focus, rl->typenum, p), NULL,
1302 DEF_CMD(render_lines_refresh)
1304 struct pane *p = ci->home;
1305 struct pane *focus = ci->focus;
1306 struct rl_data *rl = p->data;
1307 struct mark *m, *pm = NULL;
1308 int cols = rl->cols;
1309 int lines = rl->lines;
1311 //pane_damaged(p, DAMAGED_VIEW);
1313 pm = call_ret(mark, "doc:point", focus);
1315 m = vmark_first(focus, rl->typenum, p);
1320 rl->lines = render(pm, p, focus);
1321 if (rl->lines != lines || rl->cols != cols)
1322 call("render:reposition", focus, rl->lines, NULL, NULL, rl->cols);
1327 DEF_CMD(render_lines_close)
1329 struct rl_data *rl = ci->home->data;
1332 vmark_free(rl->header);
1338 DEF_CMD(render_lines_close_mark)
1340 struct mark *m = ci->mark;
1347 DEF_CMD(render_lines_abort)
1349 struct pane *p = ci->home;
1350 struct rl_data *rl = p->data;
1352 rl->ignore_point = 0;
1355 pane_damaged(p, DAMAGED_VIEW);
1357 /* Allow other handlers to complete the Abort */
1358 return Efallthrough;
1361 DEF_CMD(render_lines_move_view)
1364 * Find a new 'top' for the displayed region so that render()
1365 * will draw from there.
1366 * When moving backwards we move back a line and render it.
1367 * When moving forwards we render and then step forward
1368 * At each point we count the number of display lines that result.
1369 * When we choose a new start, we delete all earlier marks.
1370 * We also delete marks before current top when moving forward
1371 * where there are more than a page full.
1373 struct pane *p = ci->home;
1374 struct pane *focus = ci->focus;
1375 int rpt = RPT_NUM(ci);
1376 struct rl_data *rl = p->data;
1377 struct mark *top, *old_top;
1379 top = vmark_first(focus, rl->typenum, p);
1381 return Efallthrough;
1383 old_top = mark_dup(top);
1387 rl->ignore_point = 1;
1389 if (rl->skip_line_height <= 0)
1390 rl->skip_line_height = 1;
1393 /* Need to add new lines at the top and remove
1399 struct mark *prevtop = top;
1401 if (rl->skip_height) {
1402 rl->skip_height -= rl->skip_line_height;
1403 if (rl->skip_height < rl->skip_line_height/2)
1404 rl->skip_height = 0;
1405 rpt += rl->skip_line_height;
1411 m = mark_dup_view(top);
1412 top = call_render_line_prev(focus, m,
1414 if (!top && doc_prior(focus, prevtop) != WEOF) {
1415 /* Double check - maybe a soft top-of-file - Ctrl-L*/
1416 m = mark_dup(prevtop);
1418 top = call_render_line_prev(focus, m,
1424 while (m && m->seq < prevtop->seq &&
1425 !mark_same(m, prevtop)) {
1426 call_render_line(p, focus, m, NULL);
1427 if (m->mdata == NULL) {
1431 measure_line(p, focus, m);
1435 /* FIXME remove extra lines, maybe add */
1436 rl->skip_height = y;
1439 /* Need to remove lines from top */
1440 call_render_line(p, focus, top, NULL);
1441 measure_line(p, focus, top);
1442 while (top && top->mdata && rpt > 0) {
1446 if (rpt < y - rl->skip_height) {
1447 rl->skip_height += rpt;
1450 rpt -= y - rl->skip_height;
1451 rl->skip_height = 0;
1452 top = vmark_next(top);
1455 call_render_line(p, focus, top, NULL);
1456 measure_line(p, focus, top);
1458 if (top && top->mdata) {
1459 /* We didn't fall off the end, so it is OK to remove
1460 * everything before 'top'
1463 while ((old = vmark_first(focus, rl->typenum, p)) != NULL &&
1468 rl->repositioned = 1;
1469 pane_damaged(ci->home, DAMAGED_VIEW);
1470 top = vmark_first(focus, rl->typenum, p);
1471 if (top && mark_same(top, old_top)) {
1479 static char *get_action_tag(const char *tag safe, const char *a)
1481 int taglen = strlen(tag);
1488 t = strstr(a, ",action-");
1492 } while (!(strncmp(t+8, tag, taglen) == 0 &&
1493 t[8+taglen] == ':'));
1495 t += 8 + taglen + 1;
1497 return strndup(t, c?c-t: (int)strlen(t));
1500 DEF_CMD(render_lines_set_cursor)
1502 /* ->str gives a context specific action to perform
1503 * If the attributes at the location include
1504 * action-$str then the value of that attribute
1505 * is send as a command
1507 struct pane *p = ci->home;
1508 struct pane *focus = ci->focus;
1509 struct rl_data *rl = p->data;
1510 const char *action = ci->str;
1511 const char *xyattr = NULL;
1513 struct mark *m2 = NULL;
1517 cih = pane_mapxy(ci->focus, ci->home,
1518 ci->x == INT_MAX ? p->cx : ci->x,
1519 ci->y == INT_MAX ? p->cy : ci->y,
1522 m = vmark_first(p, rl->typenum, p);
1524 while (m && m->mdata && m->mdata->y + m->mdata->h <= cih.y &&
1529 /* There is nothing rendered? */
1532 /* chi is after the last visible content, and m is the end
1533 * of that content (possible EOF) so move there
1536 if (cih.y < m->mdata->y) {
1537 /* action only permitted in precise match */
1539 cih.y = m->mdata->y;
1541 xypos = find_xy_line(p, focus, m, cih.x, cih.y, &xyattr);
1543 m2 = call_render_line_offset(focus, m, xypos);
1545 wint_t c = doc_following(focus, m2);
1546 if (c == WEOF || is_eol(c))
1547 /* after last char on line - no action. */
1555 if (action && xyattr) {
1556 tag = get_action_tag(action, xyattr);
1559 /* This is a hack to get the start of these
1560 * attrs so menu can be placed correctly.
1561 * Only works for menus below the line.
1563 if (sscanf(xyattr, "%dx%d,", &x, &y) == 2) {
1565 cih.y = m->mdata->y + y +
1566 attr_find_int(m->mdata->attrs,
1570 call(tag, focus, 0, m2, xyattr,
1577 /* m is the closest we'll get */
1581 mark_to_mark(ci->mark, m);
1583 call("Move-to", focus, 0, m);
1589 DEF_CMD(render_lines_action)
1591 /* If there is an action-$str: at '->mark', send the command
1594 struct mark *m = ci->mark;
1595 struct pane *p = ci->home;
1596 struct rl_data *rl = p->data;
1597 struct pane *focus = ci->focus;
1600 char *attr = NULL, *tag;
1604 v = vmark_first(p, rl->typenum, p);
1606 while (v && v->mdata && (n = vmark_next(v)) &&
1607 mark_ordered_or_same(n, m))
1610 if (!v || !v->mdata || !mark_ordered_or_same(v, m))
1611 return Efallthrough;
1612 offset = call_render_line_to_point(focus, m, v);
1613 measure_line(p, focus, v, offset, &attr);
1615 return Efallthrough;
1616 tag = get_action_tag(ci->str, attr);
1618 return Efallthrough;
1619 call(tag, focus, 0, m, attr);
1623 DEF_CMD(render_lines_move_pos)
1625 struct pane *p = ci->home;
1626 struct pane *focus = ci->focus;
1627 struct rl_data *rl = p->data;
1628 struct mark *pm = ci->mark;
1629 struct mark *top, *bot;
1633 rl->ignore_point = 1;
1634 top = vmark_first(focus, rl->typenum, p);
1635 bot = vmark_last(focus, rl->typenum, p);
1636 if (top && rl->skip_height)
1637 /* top line not fully displayed, being in that line is
1639 top = vmark_next(top);
1641 /* last line might not be fully displayed, so don't assume */
1642 bot = vmark_prev(bot);
1644 !mark_ordered_or_same(top, pm) ||
1645 !mark_ordered_not_same(pm, bot))
1646 /* pos not displayed */
1647 find_lines(pm, p, focus, NO_NUMERIC);
1648 pane_damaged(p, DAMAGED_REFRESH);
1652 DEF_CMD(render_lines_view_line)
1654 struct pane *p = ci->home;
1655 struct pane *focus = ci->focus;
1656 struct rl_data *rl = p->data;
1657 struct mark *pm = ci->mark;
1662 if (line == NO_NUMERIC)
1665 rl->ignore_point = 1;
1666 find_lines(pm, p, focus, line);
1667 pane_damaged(p, DAMAGED_REFRESH);
1671 DEF_CMD(render_lines_move_line)
1673 /* FIXME should be able to select between display lines
1674 * and content lines - different when a line wraps.
1675 * For now just content lines.
1676 * target_x and target_y are the target location in a line
1677 * relative to the start of line.
1678 * We use doc:EOL to find a suitable start of line, then
1679 * render that line and find the last location not after x,y
1681 struct pane *p = ci->home;
1682 struct pane *focus = ci->focus;
1683 struct rl_data *rl = p->data;
1686 struct mark *m = ci->mark;
1687 struct mark *start, *m2;
1690 m = call_ret(mark, "doc:point", focus);
1694 if (rl->target_x < 0) {
1695 rl->target_x = p->cx;
1696 rl->target_y = p->cy - rl->cursor_line;
1698 if (rl->target_x < 0)
1699 /* maybe not displayed yet */
1700 rl->target_x = rl->target_y = 0;
1704 if (call("doc:EOL", ci->focus, num, m, NULL, 1) <= 0) {
1708 if (RPT_NUM(ci) < 0) {
1709 /* at end of target line, move to start */
1710 if (call("doc:EOL", ci->focus, -1, m) <= 0) {
1716 /* We are at the start of the target line. We might
1717 * like to find the target_x column, but if anything
1718 * goes wrong it isn't a failure.
1719 * Need to ensure there is a vmark here. call_render_line_prev()
1720 * wil only move the mark if it is in a multi-line rendering,
1721 * such as an image which acts as though it is multiple lines.
1722 * It will check if there is already a mark at the target location.
1723 * It will free the mark passed in unless it returns it.
1725 start = vmark_new(focus, rl->typenum, p);
1728 mark_to_mark(start, m);
1729 start = call_render_line_prev(focus, start, 0, NULL);
1733 pane_damaged(p, DAMAGED_VIEW);
1736 if (vmark_first(focus, rl->typenum, p) == start &&
1737 !vmark_is_valid(start))
1738 /* New first mark, so view will have changed */
1739 rl->repositioned = 1;
1741 if (rl->target_x == 0 && rl->target_y == 0)
1742 /* No need to move to target column - already there.
1743 * This simplifies life for render-complete which is
1744 * always at col 0, and messes with markup a bit.
1748 /* FIXME only do this if point is active/volatile, or
1749 * if start->mdata is NULL
1751 vmark_invalidate(start);
1752 call_render_line(p, focus, start, NULL);
1756 xypos = find_xy_line(p, focus, start, rl->target_x,
1757 rl->target_y + start->mdata->y, NULL);
1761 /* xypos is the distance from start-of-line to the target */
1763 m2 = call_render_line_offset(focus, start, xypos);
1767 if (!mark_same(start, m)) {
1768 /* This is a multi-line render and we aren't on
1769 * the first line. We might need a larger 'y'.
1770 * For now, ensure that we move in the right
1772 * FIXME this loses target_x and can move up
1773 * too far. How to fix??
1775 if (num > 0 && mark_ordered_not_same(m2, m))
1776 mark_to_mark(m2, m);
1777 if (num < 0 && mark_ordered_not_same(m, m2))
1778 mark_to_mark(m2, m);
1780 mark_to_mark(m, m2);
1789 DEF_CMD(render_lines_notify_replace)
1791 struct pane *p = ci->home;
1792 struct rl_data *rl = p->data;
1793 struct mark *start = ci->mark;
1794 struct mark *end = ci->mark2;
1797 if (strcmp(ci->key, "doc:replaced") == 0) {
1798 struct mark *pt = call_ret(mark, "doc:point", ci->home);
1800 /* If anyone changes the doc, reset the target. This might
1801 * be too harsh, but I mainly want target tracking for
1802 * close-in-time movement, so it probably doesn't matter.
1806 /* If the replacement happened at 'point', then stop
1807 * ignoring it, and handle the fact that point moved.
1809 if (ci->mark2 == pt)
1810 pane_call(p, "mark:moving", ci->focus, 0, pt);
1813 if (strcmp(ci->key, "view:changed") == 0)
1814 /* Cursor possibly moved, so need to refresh */
1815 pane_damaged(ci->home, DAMAGED_REFRESH);
1817 if (!start && !end) {
1818 /* No marks given - assume everything changed */
1820 for (m = vmark_first(p, rl->typenum, p);
1823 vmark_invalidate(m);
1825 pane_damaged(p, DAMAGED_VIEW);
1826 return Efallthrough;
1829 if (start && end && start->seq > end->seq) {
1834 if (strcmp(ci->key, "doc:replaced") == 0) {
1835 first = vmark_first(ci->home, rl->typenum, p);
1836 if (first && start && end && mark_same(first, end))
1837 /* Insert just before visible region */
1838 mark_to_mark(first, start);
1842 start = vmark_at_or_before(ci->home, start, rl->typenum, p);
1844 start = vmark_first(ci->home, rl->typenum, p);
1846 start = vmark_at_or_before(ci->home, end, rl->typenum, p);
1848 /* change is before visible region */
1849 return Efallthrough;
1850 /* FIXME check 'start' is at least 'num' before end */
1853 end = vmark_at_or_before(ci->home, end, rl->typenum, p);
1855 end = vmark_last(ci->home, rl->typenum, p);
1856 } else if (start) { /* smatch needs to know start in not NULL */
1857 end = vmark_at_or_before(ci->home, start, rl->typenum, p);
1859 end = vmark_first(ci->home, rl->typenum, p);
1861 return Efallthrough;
1862 if (vmark_next(end))
1863 end = vmark_next(end);
1864 /* FIXME check that 'end' is at least 'num' after start */
1868 /* Change outside visible region */
1869 return Efallthrough;
1871 while (end && mark_ordered_or_same(start, end)) {
1872 vmark_invalidate(end);
1873 end = vmark_prev(end);
1875 /* Must be sure to invalidate the line *before* the change */
1877 vmark_invalidate(end);
1879 pane_damaged(p, DAMAGED_VIEW);
1881 return Efallthrough;
1884 DEF_CMD(render_lines_clip)
1886 struct rl_data *rl = ci->home->data;
1888 marks_clip(ci->home, ci->mark, ci->mark2, rl->typenum, ci->home,
1891 mark_clip(rl->header, ci->mark, ci->mark2, !!ci->num);
1892 return Efallthrough;
1895 DEF_CMD(render_lines_attach);
1896 DEF_CMD(render_lines_clone)
1898 struct pane *parent = ci->focus;
1900 render_lines_attach.func(ci);
1901 pane_clone_children(ci->home, parent->focus);
1905 DEF_CMD(render_lines_resize)
1907 struct pane *p = ci->home;
1908 struct rl_data *rl = p->data;
1911 for (m = vmark_first(p, rl->typenum, p);
1913 m = vmark_next(m)) {
1914 vmark_invalidate(m);
1915 pane_damaged(m->mdata, DAMAGED_REFRESH);
1917 rl->background_drawn = False;
1918 pane_damaged(p, DAMAGED_VIEW | DAMAGED_REFRESH);
1920 /* Allow propagation to children */
1924 DEF_CMD(render_send_reposition)
1926 /* Some (probably new) pane wants to know the extend of the
1927 * view, so resent render:resposition.
1929 struct pane *p = ci->home;
1930 struct rl_data *rl = p->data;
1932 rl->repositioned = 1;
1933 return Efallthrough;
1936 static struct map *rl_map;
1938 DEF_LOOKUP_CMD(render_lines_handle, rl_map);
1940 static void render_lines_register_map(void)
1942 rl_map = key_alloc();
1944 key_add(rl_map, "Move-View", &render_lines_move_view);
1945 key_add(rl_map, "Move-View-Pos", &render_lines_move_pos);
1946 key_add(rl_map, "Move-View-Line", &render_lines_view_line);
1947 key_add(rl_map, "Move-CursorXY", &render_lines_set_cursor);
1948 key_add(rl_map, "Move-Line", &render_lines_move_line);
1950 /* Make it easy to stop ignoring point */
1951 key_add(rl_map, "Abort", &render_lines_abort);
1953 key_add(rl_map, "Action", &render_lines_action);
1955 key_add(rl_map, "Close", &render_lines_close);
1956 key_add(rl_map, "Close:mark", &render_lines_close_mark);
1957 key_add(rl_map, "Free", &edlib_do_free);
1958 key_add(rl_map, "Clone", &render_lines_clone);
1959 key_add(rl_map, "Refresh", &render_lines_refresh);
1960 key_add(rl_map, "Refresh:view", &render_lines_revise);
1961 key_add(rl_map, "Refresh:size", &render_lines_resize);
1962 key_add(rl_map, "Notify:clip", &render_lines_clip);
1963 key_add(rl_map, "get-attr", &render_lines_get_attr);
1964 key_add(rl_map, "mark:moving", &render_lines_point_moving);
1966 key_add(rl_map, "doc:replaced", &render_lines_notify_replace);
1967 key_add(rl_map, "doc:replaced-attr", &render_lines_notify_replace);
1968 /* view:changed is sent to a tile when the display might need
1969 * to change, even though the doc may not have*/
1970 key_add(rl_map, "view:changed", &render_lines_notify_replace);
1971 key_add(rl_map, "render:request:reposition", &render_send_reposition);
1974 REDEF_CMD(render_lines_attach)
1980 render_lines_register_map();
1987 if (strcmp(ci->key, "attach-render-text") == 0) {
1988 p = call_ret(pane, "attach-markup", p);
1992 p = pane_register(p, 0, &render_lines_handle.c, rl);
1997 rl->typenum = home_call(ci->focus, "doc:add-view", p) - 1;
1998 call("doc:request:doc:replaced", p);
1999 call("doc:request:doc:replaced-attr", p);
2000 call("doc:request:mark:moving", p);
2002 return comm_call(ci->comm2, "callback:attach", p);
2005 void edlib_init(struct pane *ed safe)
2007 call_comm("global-set-command", ed, &render_lines_attach, 0, NULL,
2008 "attach-render-lines");
2009 call_comm("global-set-command", ed, &render_lines_attach, 0, NULL,
2010 "attach-render-text");