tmux  3.2a
About: tmux is a terminal multiplexer that lets you switch easily between several programs in one terminal.
  Fossies Dox: tmux-3.2a.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

format-draw.c
Go to the documentation of this file.
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 /* Format range. */
27 struct format_range {
28  u_int index;
29  struct screen *s;
30 
31  u_int start;
32  u_int end;
33 
35  u_int argument;
36 
37  TAILQ_ENTRY(format_range) entry;
38 };
39 TAILQ_HEAD(format_ranges, format_range);
40 
41 /* Does this range match this style? */
42 static int
43 format_is_type(struct format_range *fr, struct style *sy)
44 {
45  if (fr->type != sy->range_type)
46  return (0);
47  if (fr->type == STYLE_RANGE_WINDOW &&
48  fr->argument != sy->range_argument)
49  return (0);
50  return (1);
51 }
52 
53 /* Free a range. */
54 static void
55 format_free_range(struct format_ranges *frs, struct format_range *fr)
56 {
57  TAILQ_REMOVE(frs, fr, entry);
58  free(fr);
59 }
60 
61 /* Fix range positions. */
62 static void
63 format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset,
64  u_int start, u_int width)
65 {
66  struct format_range *fr, *fr1;
67 
68  if (frs == NULL)
69  return;
70 
71  TAILQ_FOREACH_SAFE(fr, frs, entry, fr1) {
72  if (fr->s != s)
73  continue;
74 
75  if (fr->end <= start || fr->start >= start + width) {
76  format_free_range(frs, fr);
77  continue;
78  }
79 
80  if (fr->start < start)
81  fr->start = start;
82  if (fr->end > start + width)
83  fr->end = start + width;
84  if (fr->start == fr->end) {
85  format_free_range(frs, fr);
86  continue;
87  }
88 
89  fr->start -= start;
90  fr->end -= start;
91 
92  fr->start += offset;
93  fr->end += offset;
94  }
95 }
96 
97 /* Draw a part of the format. */
98 static void
99 format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy,
100  struct screen *s, struct format_ranges *frs, u_int offset, u_int start,
101  u_int width)
102 {
103  /*
104  * The offset is how far from the cursor on the target screen; start
105  * and width how much to copy from the source screen.
106  */
107  screen_write_cursormove(octx, ocx + offset, ocy, 0);
108  screen_write_fast_copy(octx, s, start, 0, width, 1);
109  format_update_ranges(frs, s, offset, start, width);
110 }
111 
112 /* Draw list part of format. */
113 static void
115  u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list,
116  struct screen *list_left, struct screen *list_right, int focus_start,
117  int focus_end, struct format_ranges *frs)
118 {
119  u_int start, focus_centre;
120 
121  /* If there is enough space for the list, draw it entirely. */
122  if (width >= list->cx) {
123  format_draw_put(octx, ocx, ocy, list, frs, offset, 0, width);
124  return;
125  }
126 
127  /* The list needs to be trimmed. Try to keep the focus visible. */
128  focus_centre = focus_start + (focus_end - focus_start) / 2;
129  if (focus_centre < width / 2)
130  start = 0;
131  else
132  start = focus_centre - width / 2;
133  if (start + width > list->cx)
134  start = list->cx - width;
135 
136  /* Draw <> markers at either side if needed. */
137  if (start != 0 && width > list_left->cx) {
138  screen_write_cursormove(octx, ocx + offset, ocy, 0);
139  screen_write_fast_copy(octx, list_left, 0, 0, list_left->cx, 1);
140  offset += list_left->cx;
141  start += list_left->cx;
142  width -= list_left->cx;
143  }
144  if (start + width < list->cx && width > list_right->cx) {
145  screen_write_cursormove(octx, ocx + offset + width -
146  list_right->cx, ocy, 0);
147  screen_write_fast_copy(octx, list_right, 0, 0, list_right->cx,
148  1);
149  width -= list_right->cx;
150  }
151 
152  /* Draw the list screen itself. */
153  format_draw_put(octx, ocx, ocy, list, frs, offset, start, width);
154 }
155 
156 /* Draw format with no list. */
157 static void
158 format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx,
159  u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
160  struct screen *abs_centre, struct format_ranges *frs)
161 {
162  u_int width_left, width_centre, width_right, width_abs_centre;
163 
164  width_left = left->cx;
165  width_centre = centre->cx;
166  width_right = right->cx;
167  width_abs_centre = abs_centre->cx;
168 
169  /*
170  * Try to keep as much of the left and right as possible at the expense
171  * of the centre.
172  */
173  while (width_left + width_centre + width_right > available) {
174  if (width_centre > 0)
175  width_centre--;
176  else if (width_right > 0)
177  width_right--;
178  else
179  width_left--;
180  }
181 
182  /* Write left. */
183  format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
184 
185  /* Write right at available - width_right. */
186  format_draw_put(octx, ocx, ocy, right, frs,
187  available - width_right,
188  right->cx - width_right,
189  width_right);
190 
191  /*
192  * Write centre halfway between
193  * width_left
194  * and
195  * available - width_right.
196  */
197  format_draw_put(octx, ocx, ocy, centre, frs,
198  width_left
199  + ((available - width_right) - width_left) / 2
200  - width_centre / 2,
201  centre->cx / 2 - width_centre / 2,
202  width_centre);
203 
204  /*
205  * Write abs_centre in the perfect centre of all horizontal space.
206  */
207  if (width_abs_centre > available)
208  width_abs_centre = available;
209  format_draw_put(octx, ocx, ocy, abs_centre, frs,
210  (available - width_abs_centre) / 2,
211  0,
212  width_abs_centre);
213 }
214 
215 /* Draw format with list on the left. */
216 static void
217 format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx,
218  u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
219  struct screen *abs_centre, struct screen *list, struct screen *list_left,
220  struct screen *list_right, struct screen *after, int focus_start,
221  int focus_end, struct format_ranges *frs)
222 {
223  u_int width_left, width_centre, width_right;
224  u_int width_list, width_after, width_abs_centre;
225  struct screen_write_ctx ctx;
226 
227  width_left = left->cx;
228  width_centre = centre->cx;
229  width_right = right->cx;
230  width_abs_centre = abs_centre->cx;
231  width_list = list->cx;
232  width_after = after->cx;
233 
234  /*
235  * Trim first the centre, then the list, then the right, then after the
236  * list, then the left.
237  */
238  while (width_left +
239  width_centre +
240  width_right +
241  width_list +
242  width_after > available) {
243  if (width_centre > 0)
244  width_centre--;
245  else if (width_list > 0)
246  width_list--;
247  else if (width_right > 0)
248  width_right--;
249  else if (width_after > 0)
250  width_after--;
251  else
252  width_left--;
253  }
254 
255  /* If there is no list left, pass off to the no list function. */
256  if (width_list == 0) {
257  screen_write_start(&ctx, left);
258  screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
259  screen_write_stop(&ctx);
260 
261  format_draw_none(octx, available, ocx, ocy, left, centre,
262  right, abs_centre, frs);
263  return;
264  }
265 
266  /* Write left at 0. */
267  format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
268 
269  /* Write right at available - width_right. */
270  format_draw_put(octx, ocx, ocy, right, frs,
271  available - width_right,
272  right->cx - width_right,
273  width_right);
274 
275  /* Write after at width_left + width_list. */
276  format_draw_put(octx, ocx, ocy, after, frs,
277  width_left + width_list,
278  0,
279  width_after);
280 
281  /*
282  * Write centre halfway between
283  * width_left + width_list + width_after
284  * and
285  * available - width_right.
286  */
287  format_draw_put(octx, ocx, ocy, centre, frs,
288  (width_left + width_list + width_after)
289  + ((available - width_right)
290  - (width_left + width_list + width_after)) / 2
291  - width_centre / 2,
292  centre->cx / 2 - width_centre / 2,
293  width_centre);
294 
295  /*
296  * The list now goes from
297  * width_left
298  * to
299  * width_left + width_list.
300  * If there is no focus given, keep the left in focus.
301  */
302  if (focus_start == -1 || focus_end == -1)
303  focus_start = focus_end = 0;
304  format_draw_put_list(octx, ocx, ocy, width_left, width_list, list,
305  list_left, list_right, focus_start, focus_end, frs);
306 
307  /*
308  * Write abs_centre in the perfect centre of all horizontal space.
309  */
310  if (width_abs_centre > available)
311  width_abs_centre = available;
312  format_draw_put(octx, ocx, ocy, abs_centre, frs,
313  (available - width_abs_centre) / 2,
314  0,
315  width_abs_centre);
316 }
317 
318 /* Draw format with list in the centre. */
319 static void
320 format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx,
321  u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
322  struct screen *abs_centre, struct screen *list, struct screen *list_left,
323  struct screen *list_right, struct screen *after, int focus_start,
324  int focus_end, struct format_ranges *frs)
325 {
326  u_int width_left, width_centre, width_right, middle;
327  u_int width_list, width_after, width_abs_centre;
328  struct screen_write_ctx ctx;
329 
330  width_left = left->cx;
331  width_centre = centre->cx;
332  width_right = right->cx;
333  width_abs_centre = abs_centre->cx;
334  width_list = list->cx;
335  width_after = after->cx;
336 
337  /*
338  * Trim first the list, then after the list, then the centre, then the
339  * right, then the left.
340  */
341  while (width_left +
342  width_centre +
343  width_right +
344  width_list +
345  width_after > available) {
346  if (width_list > 0)
347  width_list--;
348  else if (width_after > 0)
349  width_after--;
350  else if (width_centre > 0)
351  width_centre--;
352  else if (width_right > 0)
353  width_right--;
354  else
355  width_left--;
356  }
357 
358  /* If there is no list left, pass off to the no list function. */
359  if (width_list == 0) {
360  screen_write_start(&ctx, centre);
361  screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
362  screen_write_stop(&ctx);
363 
364  format_draw_none(octx, available, ocx, ocy, left, centre,
365  right, abs_centre, frs);
366  return;
367  }
368 
369  /* Write left at 0. */
370  format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
371 
372  /* Write right at available - width_right. */
373  format_draw_put(octx, ocx, ocy, right, frs,
374  available - width_right,
375  right->cx - width_right,
376  width_right);
377 
378  /*
379  * All three centre sections are offset from the middle of the
380  * available space.
381  */
382  middle = (width_left + ((available - width_right) - width_left) / 2);
383 
384  /*
385  * Write centre at
386  * middle - width_list / 2 - width_centre.
387  */
388  format_draw_put(octx, ocx, ocy, centre, frs,
389  middle - width_list / 2 - width_centre,
390  0,
391  width_centre);
392 
393  /*
394  * Write after at
395  * middle - width_list / 2 + width_list
396  */
397  format_draw_put(octx, ocx, ocy, after, frs,
398  middle - width_list / 2 + width_list,
399  0,
400  width_after);
401 
402  /*
403  * The list now goes from
404  * middle - width_list / 2
405  * to
406  * middle + width_list / 2
407  * If there is no focus given, keep the centre in focus.
408  */
409  if (focus_start == -1 || focus_end == -1)
410  focus_start = focus_end = list->cx / 2;
411  format_draw_put_list(octx, ocx, ocy, middle - width_list / 2,
412  width_list, list, list_left, list_right, focus_start, focus_end,
413  frs);
414 
415  /*
416  * Write abs_centre in the perfect centre of all horizontal space.
417  */
418  if (width_abs_centre > available)
419  width_abs_centre = available;
420  format_draw_put(octx, ocx, ocy, abs_centre, frs,
421  (available - width_abs_centre) / 2,
422  0,
423  width_abs_centre);
424 }
425 
426 /* Draw format with list on the right. */
427 static void
428 format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx,
429  u_int ocy, struct screen *left, struct screen *centre, struct screen *right,
430  struct screen *abs_centre, struct screen *list,
431  struct screen *list_left, struct screen *list_right, struct screen *after,
432  int focus_start, int focus_end, struct format_ranges *frs)
433 {
434  u_int width_left, width_centre, width_right;
435  u_int width_list, width_after, width_abs_centre;
436  struct screen_write_ctx ctx;
437 
438  width_left = left->cx;
439  width_centre = centre->cx;
440  width_right = right->cx;
441  width_abs_centre = abs_centre->cx;
442  width_list = list->cx;
443  width_after = after->cx;
444 
445  /*
446  * Trim first the centre, then the list, then the right, then
447  * after the list, then the left.
448  */
449  while (width_left +
450  width_centre +
451  width_right +
452  width_list +
453  width_after > available) {
454  if (width_centre > 0)
455  width_centre--;
456  else if (width_list > 0)
457  width_list--;
458  else if (width_right > 0)
459  width_right--;
460  else if (width_after > 0)
461  width_after--;
462  else
463  width_left--;
464  }
465 
466  /* If there is no list left, pass off to the no list function. */
467  if (width_list == 0) {
468  screen_write_start(&ctx, right);
469  screen_write_fast_copy(&ctx, after, 0, 0, width_after, 1);
470  screen_write_stop(&ctx);
471 
472  format_draw_none(octx, available, ocx, ocy, left, centre,
473  right, abs_centre, frs);
474  return;
475  }
476 
477  /* Write left at 0. */
478  format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
479 
480  /* Write after at available - width_after. */
481  format_draw_put(octx, ocx, ocy, after, frs,
482  available - width_after,
483  after->cx - width_after,
484  width_after);
485 
486  /*
487  * Write right at
488  * available - width_right - width_list - width_after.
489  */
490  format_draw_put(octx, ocx, ocy, right, frs,
491  available - width_right - width_list - width_after,
492  0,
493  width_right);
494 
495  /*
496  * Write centre halfway between
497  * width_left
498  * and
499  * available - width_right - width_list - width_after.
500  */
501  format_draw_put(octx, ocx, ocy, centre, frs,
502  width_left
503  + ((available - width_right - width_list - width_after)
504  - width_left) / 2
505  - width_centre / 2,
506  centre->cx / 2 - width_centre / 2,
507  width_centre);
508 
509  /*
510  * The list now goes from
511  * available - width_list - width_after
512  * to
513  * available - width_after
514  * If there is no focus given, keep the right in focus.
515  */
516  if (focus_start == -1 || focus_end == -1)
517  focus_start = focus_end = 0;
518  format_draw_put_list(octx, ocx, ocy, available - width_list -
519  width_after, width_list, list, list_left, list_right, focus_start,
520  focus_end, frs);
521 
522  /*
523  * Write abs_centre in the perfect centre of all horizontal space.
524  */
525  if (width_abs_centre > available)
526  width_abs_centre = available;
527  format_draw_put(octx, ocx, ocy, abs_centre, frs,
528  (available - width_abs_centre) / 2,
529  0,
530  width_abs_centre);
531 }
532 
533 static void
534 format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available,
535  u_int ocx, u_int ocy, struct screen *left, struct screen *centre,
536  struct screen *right, struct screen *abs_centre, struct screen *list,
537  struct screen *list_left, struct screen *list_right, struct screen *after,
538  int focus_start, int focus_end, struct format_ranges *frs)
539 {
540  u_int width_left, width_centre, width_right, width_abs_centre;
541  u_int width_list, width_after, middle, abs_centre_offset;
542 
543  width_left = left->cx;
544  width_centre = centre->cx;
545  width_right = right->cx;
546  width_abs_centre = abs_centre->cx;
547  width_list = list->cx;
548  width_after = after->cx;
549 
550  /*
551  * Trim first centre, then the right, then the left.
552  */
553  while (width_left +
554  width_centre +
555  width_right > available) {
556  if (width_centre > 0)
557  width_centre--;
558  else if (width_right > 0)
559  width_right--;
560  else
561  width_left--;
562  }
563 
564  /*
565  * We trim list after and abs_centre independently, as we are drawing
566  * them over the rest. Trim first the list, then after the list, then
567  * abs_centre.
568  */
569  while (width_list + width_after + width_abs_centre > available) {
570  if (width_list > 0)
571  width_list--;
572  else if (width_after > 0)
573  width_after--;
574  else
575  width_abs_centre--;
576  }
577 
578  /* Write left at 0. */
579  format_draw_put(octx, ocx, ocy, left, frs, 0, 0, width_left);
580 
581  /* Write right at available - width_right. */
582  format_draw_put(octx, ocx, ocy, right, frs,
583  available - width_right,
584  right->cx - width_right,
585  width_right);
586 
587  /*
588  * Keep writing centre at the relative centre. Only the list is written
589  * in the absolute centre of the horizontal space.
590  */
591  middle = (width_left + ((available - width_right) - width_left) / 2);
592 
593  /*
594  * Write centre at
595  * middle - width_centre.
596  */
597  format_draw_put(octx, ocx, ocy, centre, frs,
598  middle - width_centre,
599  0,
600  width_centre);
601 
602  /*
603  * If there is no focus given, keep the centre in focus.
604  */
605  if (focus_start == -1 || focus_end == -1)
606  focus_start = focus_end = list->cx / 2;
607 
608  /*
609  * We centre abs_centre and the list together, so their shared centre is
610  * in the perfect centre of horizontal space.
611  */
612  abs_centre_offset = (available - width_list - width_abs_centre) / 2;
613 
614  /*
615  * Write abs_centre before the list.
616  */
617  format_draw_put(octx, ocx, ocy, abs_centre, frs, abs_centre_offset,
618  0, width_abs_centre);
619  abs_centre_offset += width_abs_centre;
620 
621  /*
622  * Draw the list in the absolute centre
623  */
624  format_draw_put_list(octx, ocx, ocy, abs_centre_offset, width_list,
625  list, list_left, list_right, focus_start, focus_end, frs);
626  abs_centre_offset += width_list;
627 
628  /*
629  * Write after at the end of the centre
630  */
631  format_draw_put(octx, ocx, ocy, after, frs, abs_centre_offset, 0,
632  width_after);
633 }
634 
635 /* Draw multiple characters. */
636 static void
637 format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch,
638  u_int n)
639 {
640  u_int i;
641 
642  utf8_set(&sy->gc.data, ch);
643  for (i = 0; i < n; i++)
644  screen_write_cell(ctx, &sy->gc);
645 }
646 
647 /* Draw a format to a screen. */
648 void
649 format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
650  u_int available, const char *expanded, struct style_ranges *srs)
651 {
652  enum { LEFT,
653  CENTRE,
654  RIGHT,
655  ABSOLUTE_CENTRE,
656  LIST,
657  LIST_LEFT,
658  LIST_RIGHT,
659  AFTER,
660  TOTAL } current = LEFT, last = LEFT;
661  const char *names[] = { "LEFT",
662  "CENTRE",
663  "RIGHT",
664  "ABSOLUTE_CENTRE",
665  "LIST",
666  "LIST_LEFT",
667  "LIST_RIGHT",
668  "AFTER" };
669  size_t size = strlen(expanded);
670  struct screen *os = octx->s, s[TOTAL];
671  struct screen_write_ctx ctx[TOTAL];
672  u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
673  u_int map[] = { LEFT,
674  LEFT,
675  CENTRE,
676  RIGHT,
677  ABSOLUTE_CENTRE };
678  int focus_start = -1, focus_end = -1;
679  int list_state = -1, fill = -1, even;
680  enum style_align list_align = STYLE_ALIGN_DEFAULT;
681  struct grid_cell gc, current_default;
682  struct style sy, saved_sy;
683  struct utf8_data *ud = &sy.gc.data;
684  const char *cp, *end;
685  enum utf8_state more;
686  char *tmp;
687  struct format_range *fr = NULL, *fr1;
688  struct format_ranges frs;
689  struct style_range *sr;
690 
691  memcpy(&current_default, base, sizeof current_default);
692  style_set(&sy, &current_default);
693  TAILQ_INIT(&frs);
694  log_debug("%s: %s", __func__, expanded);
695 
696  /*
697  * We build three screens for left, right, centre alignment, one for
698  * the list, one for anything after the list and two for the list left
699  * and right markers.
700  */
701  for (i = 0; i < TOTAL; i++) {
702  screen_init(&s[i], size, 1, 0);
703  screen_write_start(&ctx[i], &s[i]);
704  screen_write_clearendofline(&ctx[i], current_default.bg);
705  width[i] = 0;
706  }
707 
708  /*
709  * Walk the string and add to the corresponding screens,
710  * parsing styles as we go.
711  */
712  cp = expanded;
713  while (*cp != '\0') {
714  /* Handle sequences of #. */
715  if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') {
716  for (n = 1; cp[n] == '#'; n++)
717  /* nothing */;
718  even = ((n % 2) == 0);
719  if (cp[n] != '[') {
720  cp += n;
721  if (even)
722  n = (n / 2);
723  else
724  n = (n / 2) + 1;
725  width[current] += n;
726  format_draw_many(&ctx[current], &sy, '#', n);
727  continue;
728  }
729  if (even)
730  cp += (n + 1);
731  else
732  cp += (n - 1);
733  if (sy.ignore)
734  continue;
735  format_draw_many(&ctx[current], &sy, '#', n / 2);
736  width[current] += (n / 2);
737  if (even) {
738  utf8_set(ud, '[');
739  screen_write_cell(&ctx[current], &sy.gc);
740  width[current]++;
741  }
742  continue;
743  }
744 
745  /* Is this not a style? */
746  if (cp[0] != '#' || cp[1] != '[' || sy.ignore) {
747  /* See if this is a UTF-8 character. */
748  if ((more = utf8_open(ud, *cp)) == UTF8_MORE) {
749  while (*++cp != '\0' && more == UTF8_MORE)
750  more = utf8_append(ud, *cp);
751  if (more != UTF8_DONE)
752  cp -= ud->have;
753  }
754 
755  /* Not a UTF-8 character - ASCII or not valid. */
756  if (more != UTF8_DONE) {
757  if (*cp < 0x20 || *cp > 0x7e) {
758  /* Ignore nonprintable characters. */
759  cp++;
760  continue;
761  }
762  utf8_set(ud, *cp);
763  cp++;
764  }
765 
766  /* Draw the cell to the current screen. */
767  screen_write_cell(&ctx[current], &sy.gc);
768  width[current] += ud->width;
769  continue;
770  }
771 
772  /* This is a style. Work out where the end is and parse it. */
773  end = format_skip(cp + 2, "]");
774  if (end == NULL) {
775  log_debug("%s: no terminating ] at '%s'", __func__,
776  cp + 2);
777  TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1)
778  format_free_range(&frs, fr);
779  goto out;
780  }
781  tmp = xstrndup(cp + 2, end - (cp + 2));
782  style_copy(&saved_sy, &sy);
783  if (style_parse(&sy, &current_default, tmp) != 0) {
784  log_debug("%s: invalid style '%s'", __func__, tmp);
785  free(tmp);
786  cp = end + 1;
787  continue;
788  }
789  log_debug("%s: style '%s' -> '%s'", __func__, tmp,
790  style_tostring(&sy));
791  free(tmp);
792 
793  /* If this style has a fill colour, store it for later. */
794  if (sy.fill != 8)
795  fill = sy.fill;
796 
797  /* If this style pushed or popped the default, update it. */
798  if (sy.default_type == STYLE_DEFAULT_PUSH) {
799  memcpy(&current_default, &saved_sy.gc,
800  sizeof current_default);
802  } else if (sy.default_type == STYLE_DEFAULT_POP) {
803  memcpy(&current_default, base, sizeof current_default);
805  }
806 
807  /* Check the list state. */
808  switch (sy.list) {
809  case STYLE_LIST_ON:
810  /*
811  * Entering the list, exiting a marker, or exiting the
812  * focus.
813  */
814  if (list_state != 0) {
815  if (fr != NULL) { /* abort any region */
816  free(fr);
817  fr = NULL;
818  }
819  list_state = 0;
820  list_align = sy.align;
821  }
822 
823  /* End the focus if started. */
824  if (focus_start != -1 && focus_end == -1)
825  focus_end = s[LIST].cx;
826 
827  current = LIST;
828  break;
829  case STYLE_LIST_FOCUS:
830  /* Entering the focus. */
831  if (list_state != 0) /* not inside the list */
832  break;
833  if (focus_start == -1) /* focus already started */
834  focus_start = s[LIST].cx;
835  break;
836  case STYLE_LIST_OFF:
837  /* Exiting or outside the list. */
838  if (list_state == 0) {
839  if (fr != NULL) { /* abort any region */
840  free(fr);
841  fr = NULL;
842  }
843  if (focus_start != -1 && focus_end == -1)
844  focus_end = s[LIST].cx;
845 
846  map[list_align] = AFTER;
847  if (list_align == STYLE_ALIGN_LEFT)
848  map[STYLE_ALIGN_DEFAULT] = AFTER;
849  list_state = 1;
850  }
851  current = map[sy.align];
852  break;
854  /* Entering left marker. */
855  if (list_state != 0) /* not inside the list */
856  break;
857  if (s[LIST_LEFT].cx != 0) /* already have marker */
858  break;
859  if (fr != NULL) { /* abort any region */
860  free(fr);
861  fr = NULL;
862  }
863  if (focus_start != -1 && focus_end == -1)
864  focus_start = focus_end = -1;
865  current = LIST_LEFT;
866  break;
868  /* Entering right marker. */
869  if (list_state != 0) /* not inside the list */
870  break;
871  if (s[LIST_RIGHT].cx != 0) /* already have marker */
872  break;
873  if (fr != NULL) { /* abort any region */
874  free(fr);
875  fr = NULL;
876  }
877  if (focus_start != -1 && focus_end == -1)
878  focus_start = focus_end = -1;
879  current = LIST_RIGHT;
880  break;
881  }
882  if (current != last) {
883  log_debug("%s: change %s -> %s", __func__,
884  names[last], names[current]);
885  last = current;
886  }
887 
888  /*
889  * Check if the range style has changed and if so end the
890  * current range and start a new one if needed.
891  */
892  if (srs != NULL) {
893  if (fr != NULL && !format_is_type(fr, &sy)) {
894  if (s[current].cx != fr->start) {
895  fr->end = s[current].cx + 1;
896  TAILQ_INSERT_TAIL(&frs, fr, entry);
897  } else
898  free(fr);
899  fr = NULL;
900  }
901  if (fr == NULL && sy.range_type != STYLE_RANGE_NONE) {
902  fr = xcalloc(1, sizeof *fr);
903  fr->index = current;
904 
905  fr->s = &s[current];
906  fr->start = s[current].cx;
907 
908  fr->type = sy.range_type;
909  fr->argument = sy.range_argument;
910  }
911  }
912 
913  cp = end + 1;
914  }
915  free(fr);
916 
917  for (i = 0; i < TOTAL; i++) {
918  screen_write_stop(&ctx[i]);
919  log_debug("%s: width %s is %u", __func__, names[i], width[i]);
920  }
921  if (focus_start != -1 && focus_end != -1)
922  log_debug("%s: focus %d-%d", __func__, focus_start, focus_end);
923  TAILQ_FOREACH(fr, &frs, entry) {
924  log_debug("%s: range %d|%u is %s %u-%u", __func__, fr->type,
925  fr->argument, names[fr->index], fr->start, fr->end);
926  }
927 
928  /* Clear the available area. */
929  if (fill != -1) {
930  memcpy(&gc, &grid_default_cell, sizeof gc);
931  gc.bg = fill;
932  for (i = 0; i < available; i++)
933  screen_write_putc(octx, &gc, ' ');
934  }
935 
936  /*
937  * Draw the screens. How they are arranged depends on where the list
938  * appears.
939  */
940  switch (list_align) {
941  case STYLE_ALIGN_DEFAULT:
942  /* No list. */
943  format_draw_none(octx, available, ocx, ocy, &s[LEFT],
944  &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &frs);
945  break;
946  case STYLE_ALIGN_LEFT:
947  /* List is part of the left. */
948  format_draw_left(octx, available, ocx, ocy, &s[LEFT],
949  &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
950  &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
951  focus_start, focus_end, &frs);
952  break;
953  case STYLE_ALIGN_CENTRE:
954  /* List is part of the centre. */
955  format_draw_centre(octx, available, ocx, ocy, &s[LEFT],
956  &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
957  &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
958  focus_start, focus_end, &frs);
959  break;
960  case STYLE_ALIGN_RIGHT:
961  /* List is part of the right. */
962  format_draw_right(octx, available, ocx, ocy, &s[LEFT],
963  &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
964  &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
965  focus_start, focus_end, &frs);
966  break;
968  /* List is in the centre of the entire horizontal space. */
969  format_draw_absolute_centre(octx, available, ocx, ocy, &s[LEFT],
970  &s[CENTRE], &s[RIGHT], &s[ABSOLUTE_CENTRE], &s[LIST],
971  &s[LIST_LEFT], &s[LIST_RIGHT], &s[AFTER],
972  focus_start, focus_end, &frs);
973  break;
974  }
975 
976  /* Create ranges to return. */
977  TAILQ_FOREACH_SAFE(fr, &frs, entry, fr1) {
978  sr = xcalloc(1, sizeof *sr);
979  sr->type = fr->type;
980  sr->argument = fr->argument;
981  sr->start = fr->start;
982  sr->end = fr->end;
983  TAILQ_INSERT_TAIL(srs, sr, entry);
984 
985  log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
986  sr->argument, sr->start, sr->end);
987 
988  format_free_range(&frs, fr);
989  }
990 
991 out:
992  /* Free the screens. */
993  for (i = 0; i < TOTAL; i++)
994  screen_free(&s[i]);
995 
996  /* Restore the original cursor position. */
997  screen_write_cursormove(octx, ocx, ocy, 0);
998 }
999 
1000 /* Get width, taking #[] into account. */
1001 u_int
1002 format_width(const char *expanded)
1003 {
1004  const char *cp, *end;
1005  u_int n, width = 0;
1006  struct utf8_data ud;
1007  enum utf8_state more;
1008 
1009  cp = expanded;
1010  while (*cp != '\0') {
1011  if (*cp == '#') {
1012  for (n = 1; cp[n] == '#'; n++)
1013  /* nothing */;
1014  if (cp[n] != '[') {
1015  width += n;
1016  cp += n;
1017  continue;
1018  }
1019  width += (n / 2); /* one for each ## */
1020 
1021  if ((n % 2) == 0) {
1022  /*
1023  * An even number of #s means that all #s are
1024  * escaped, so not a style.
1025  */
1026  width++; /* one for the [ */
1027  cp += (n + 1);
1028  continue;
1029  }
1030  cp += (n - 1); /* point to the [ */
1031 
1032  end = format_skip(cp + 2, "]");
1033  if (end == NULL)
1034  return (0);
1035  cp = end + 1;
1036  } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1037  while (*++cp != '\0' && more == UTF8_MORE)
1038  more = utf8_append(&ud, *cp);
1039  if (more == UTF8_DONE)
1040  width += ud.width;
1041  else
1042  cp -= ud.have;
1043  } else if (*cp > 0x1f && *cp < 0x7f) {
1044  width++;
1045  cp++;
1046  } else
1047  cp++;
1048  }
1049  return (width);
1050 }
1051 
1052 /*
1053  * Trim on the left, taking #[] into account. Note, we copy the whole set of
1054  * unescaped #s, but only add their escaped size to width. This is because the
1055  * format_draw function will actually do the escaping when it runs
1056  */
1057 char *
1058 format_trim_left(const char *expanded, u_int limit)
1059 {
1060  char *copy, *out;
1061  const char *cp = expanded, *end;
1062  u_int even, n, width = 0;
1063  struct utf8_data ud;
1064  enum utf8_state more;
1065 
1066  out = copy = xcalloc(1, strlen(expanded) + 1);
1067  while (*cp != '\0') {
1068  if (width >= limit)
1069  break;
1070  if (*cp == '#') {
1071  for (end = cp + 1; *end == '#'; end++)
1072  /* nothing */;
1073  n = end - cp;
1074  if (*end != '[') {
1075  if (n > limit - width)
1076  n = limit - width;
1077  memcpy(out, cp, n);
1078  out += n;
1079  width += n;
1080  cp = end;
1081  continue;
1082  }
1083  even = ((n % 2) == 0);
1084 
1085  n /= 2;
1086  if (n > limit - width)
1087  n = limit - width;
1088  width += n;
1089  n *= 2;
1090  memcpy(out, cp, n);
1091  out += n;
1092 
1093  if (even) {
1094  if (width + 1 <= limit) {
1095  *out++ = '[';
1096  width++;
1097  }
1098  cp = end + 1;
1099  continue;
1100  }
1101  cp = end - 1;
1102 
1103  end = format_skip(cp + 2, "]");
1104  if (end == NULL)
1105  break;
1106  memcpy(out, cp, end + 1 - cp);
1107  out += (end + 1 - cp);
1108  cp = end + 1;
1109  } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1110  while (*++cp != '\0' && more == UTF8_MORE)
1111  more = utf8_append(&ud, *cp);
1112  if (more == UTF8_DONE) {
1113  if (width + ud.width <= limit) {
1114  memcpy(out, ud.data, ud.size);
1115  out += ud.size;
1116  }
1117  width += ud.width;
1118  } else {
1119  cp -= ud.have;
1120  cp++;
1121  }
1122  } else if (*cp > 0x1f && *cp < 0x7f) {
1123  if (width + 1 <= limit)
1124  *out++ = *cp;
1125  width++;
1126  cp++;
1127  } else
1128  cp++;
1129  }
1130  *out = '\0';
1131  return (copy);
1132 }
1133 
1134 /* Trim on the right, taking #[] into account. */
1135 char *
1136 format_trim_right(const char *expanded, u_int limit)
1137 {
1138  char *copy, *out;
1139  const char *cp = expanded, *end;
1140  u_int width = 0, total_width, skip, old_n, even, n;
1141  struct utf8_data ud;
1142  enum utf8_state more;
1143 
1144  total_width = format_width(expanded);
1145  if (total_width <= limit)
1146  return (xstrdup(expanded));
1147  skip = total_width - limit;
1148 
1149  out = copy = xcalloc(1, strlen(expanded) + 1);
1150  while (*cp != '\0') {
1151  if (*cp == '#') {
1152  for (end = cp + 1; *end == '#'; end++)
1153  /* nothing */;
1154  old_n = n = end - cp;
1155  if (*end != '[') {
1156  if (width <= skip) {
1157  if (skip - width >= n)
1158  n = 0;
1159  else
1160  n -= (skip - width);
1161  }
1162  if (n != 0) {
1163  memcpy(out, cp, n);
1164  out += n;
1165  }
1166 
1167  /*
1168  * The width always increases by the full
1169  * amount even if we can't copy anything yet.
1170  */
1171  width += old_n;
1172  cp = end;
1173  continue;
1174  }
1175  even = ((n % 2) == 0);
1176 
1177  n /= 2;
1178  if (width <= skip) {
1179  if (skip - width >= n)
1180  n = 0;
1181  else
1182  n -= (skip - width);
1183  }
1184  if (n != 0) {
1185  /*
1186  * Copy the full amount because it hasn't been
1187  * escaped yet.
1188  */
1189  memcpy(out, cp, old_n);
1190  out += old_n;
1191  }
1192  cp += old_n;
1193  width += (old_n / 2) - even;
1194 
1195  if (even) {
1196  if (width > skip)
1197  *out++ = '[';
1198  width++;
1199  continue;
1200  }
1201  cp = end - 1;
1202 
1203  end = format_skip(cp + 2, "]");
1204  if (end == NULL) {
1205  break;
1206  }
1207  memcpy(out, cp, end + 1 - cp);
1208  out += (end + 1 - cp);
1209  cp = end + 1;
1210  } else if ((more = utf8_open(&ud, *cp)) == UTF8_MORE) {
1211  while (*++cp != '\0' && more == UTF8_MORE)
1212  more = utf8_append(&ud, *cp);
1213  if (more == UTF8_DONE) {
1214  if (width >= skip) {
1215  memcpy(out, ud.data, ud.size);
1216  out += ud.size;
1217  }
1218  width += ud.width;
1219  } else {
1220  cp -= ud.have;
1221  cp++;
1222  }
1223  } else if (*cp > 0x1f && *cp < 0x7f) {
1224  if (width >= skip)
1225  *out++ = *cp;
1226  width++;
1227  cp++;
1228  } else
1229  cp++;
1230  }
1231  *out = '\0';
1232  return (copy);
1233 }
static void format_draw_absolute_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *abs_centre, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs)
Definition: format-draw.c:534
static void format_draw_put(struct screen_write_ctx *octx, u_int ocx, u_int ocy, struct screen *s, struct format_ranges *frs, u_int offset, u_int start, u_int width)
Definition: format-draw.c:99
static void format_draw_centre(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *abs_centre, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs)
Definition: format-draw.c:320
static void format_update_ranges(struct format_ranges *frs, struct screen *s, u_int offset, u_int start, u_int width)
Definition: format-draw.c:63
static void format_draw_left(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *abs_centre, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs)
Definition: format-draw.c:217
static void format_free_range(struct format_ranges *frs, struct format_range *fr)
Definition: format-draw.c:55
char * format_trim_right(const char *expanded, u_int limit)
Definition: format-draw.c:1136
static void format_draw_none(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *abs_centre, struct format_ranges *frs)
Definition: format-draw.c:158
u_int format_width(const char *expanded)
Definition: format-draw.c:1002
static void format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, u_int ocy, struct screen *left, struct screen *centre, struct screen *right, struct screen *abs_centre, struct screen *list, struct screen *list_left, struct screen *list_right, struct screen *after, int focus_start, int focus_end, struct format_ranges *frs)
Definition: format-draw.c:428
void format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, u_int available, const char *expanded, struct style_ranges *srs)
Definition: format-draw.c:649
char * format_trim_left(const char *expanded, u_int limit)
Definition: format-draw.c:1058
static int format_is_type(struct format_range *fr, struct style *sy)
Definition: format-draw.c:43
static void format_draw_put_list(struct screen_write_ctx *octx, u_int ocx, u_int ocy, u_int offset, u_int width, struct screen *list, struct screen *list_left, struct screen *list_right, int focus_start, int focus_end, struct format_ranges *frs)
Definition: format-draw.c:114
TAILQ_HEAD(format_ranges, format_range)
static void format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, u_int n)
Definition: format-draw.c:637
const char * format_skip(const char *s, const char *end)
Definition: format.c:3422
const struct grid_cell grid_default_cell
Definition: grid.c:39
void log_debug(const char *msg,...)
Definition: log.c:130
void screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, u_int py, u_int nx, u_int ny)
Definition: screen-write.c:548
void screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
void screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
Definition: screen-write.c:284
void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
void screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, u_char ch)
Definition: screen-write.c:321
void screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, int origin)
void screen_write_stop(struct screen_write_ctx *ctx)
Definition: screen-write.c:296
void screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
Definition: screen.c:74
void screen_free(struct screen *s)
Definition: screen.c:122
u_int argument
Definition: format-draw.c:35
enum style_range_type type
Definition: format-draw.c:34
struct screen * s
Definition: format-draw.c:29
int bg
Definition: tmux.h:693
struct utf8_data data
Definition: tmux.h:689
struct screen * s
Definition: tmux.h:851
Definition: tmux.h:816
u_int cy
Definition: tmux.h:824
u_int cx
Definition: tmux.h:823
u_int end
Definition: tmux.h:785
u_int argument
Definition: tmux.h:782
enum style_range_type type
Definition: tmux.h:781
u_int start
Definition: tmux.h:784
Definition: tmux.h:799
enum style_align align
Definition: tmux.h:804
u_int range_argument
Definition: tmux.h:808
struct grid_cell gc
Definition: tmux.h:800
int fill
Definition: tmux.h:803
int ignore
Definition: tmux.h:801
enum style_default_type default_type
Definition: tmux.h:810
enum style_range_type range_type
Definition: tmux.h:807
enum style_list list
Definition: tmux.h:805
u_char data[21]
Definition: tmux.h:629
u_char size
Definition: tmux.h:632
u_char have
Definition: tmux.h:631
u_char width
Definition: tmux.h:634
void style_copy(struct style *dst, struct style *src)
Definition: style.c:315
const char * style_tostring(struct style *sy)
Definition: style.c:189
void style_set(struct style *sy, const struct grid_cell *gc)
Definition: style.c:307
int style_parse(struct style *sy, const struct grid_cell *base, const char *in)
Definition: style.c:51
@ STYLE_LIST_ON
Definition: tmux.h:767
@ STYLE_LIST_FOCUS
Definition: tmux.h:768
@ STYLE_LIST_OFF
Definition: tmux.h:766
@ STYLE_LIST_RIGHT_MARKER
Definition: tmux.h:770
@ STYLE_LIST_LEFT_MARKER
Definition: tmux.h:769
enum utf8_state utf8_open(struct utf8_data *, u_char)
Definition: utf8.c:266
void utf8_set(struct utf8_data *, u_char)
Definition: utf8.c:193
@ STYLE_DEFAULT_BASE
Definition: tmux.h:793
@ STYLE_DEFAULT_POP
Definition: tmux.h:795
@ STYLE_DEFAULT_PUSH
Definition: tmux.h:794
style_align
Definition: tmux.h:756
@ STYLE_ALIGN_ABSOLUTE_CENTRE
Definition: tmux.h:761
@ STYLE_ALIGN_RIGHT
Definition: tmux.h:760
@ STYLE_ALIGN_CENTRE
Definition: tmux.h:759
@ STYLE_ALIGN_DEFAULT
Definition: tmux.h:757
@ STYLE_ALIGN_LEFT
Definition: tmux.h:758
utf8_state
Definition: tmux.h:636
@ UTF8_DONE
Definition: tmux.h:638
@ UTF8_MORE
Definition: tmux.h:637
style_range_type
Definition: tmux.h:774
@ STYLE_RANGE_NONE
Definition: tmux.h:775
@ STYLE_RANGE_WINDOW
Definition: tmux.h:778
enum utf8_state utf8_append(struct utf8_data *, u_char)
Definition: utf8.c:283
char * xstrndup(const char *str, size_t maxlen)
Definition: xmalloc.c:99
void * xcalloc(size_t nmemb, size_t size)
Definition: xmalloc.c:41
char * xstrdup(const char *str)
Definition: xmalloc.c:89