1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "vt_screen.h"
4
5 #include <stdlib.h> /* abs */
6 #include <unistd.h> /* write */
7 #include <sys/time.h>
8 #include <pobl/bl_debug.h>
9 #include <pobl/bl_mem.h> /* malloc/free */
10 #include <pobl/bl_str.h> /* strdup */
11 #include <pobl/bl_util.h> /* BL_MIN */
12
13 #include "vt_char_encoding.h"
14 #include "vt_str_parser.h"
15
16 #define ROW_IN_LOGS(screen, row) (vt_get_num_logged_lines(&(screen)->logs) + row)
17
18 #if 1
19 #define EXIT_BS_AT_BOTTOM
20 #endif
21
22 /* --- static variables --- */
23
24 static char *word_separators = " .,:;/|@()[]{}";
25 static int regard_uri_as_word = 0;
26
27 /* --- static functions --- */
28
get_n_prev_char_pos(vt_screen_t * screen,int * char_index,int * row,int n)29 static int get_n_prev_char_pos(vt_screen_t *screen, int *char_index, int *row, int n) {
30 int count;
31
32 *char_index = vt_cursor_char_index(screen->edit);
33 *row = vt_cursor_row(screen->edit);
34
35 for (count = 0; count < n; count++) {
36 if (*char_index == 0) {
37 return 0;
38 }
39
40 (*char_index)--;
41 }
42
43 return 1;
44 }
45
is_word_separator(vt_char_t * ch)46 static int is_word_separator(vt_char_t *ch) {
47 char *p;
48 char c;
49
50 if (vt_char_cs(ch) != US_ASCII) {
51 return 0;
52 }
53
54 p = word_separators;
55 c = vt_char_code(ch);
56
57 while (*p) {
58 if (c == *p) {
59 return 1;
60 }
61
62 p++;
63 }
64
65 return 0;
66 }
67
68 /*
69 * callbacks of vt_edit_scroll_event_listener_t.
70 */
71
72 /*
73 * Don't operate vt_model_t in this function because vt_model_t is not scrolled
74 * yet.
75 * Operate vt_model_t in scrolled_out_lines_finished().
76 */
receive_scrolled_out_line(void * p,vt_line_t * line)77 static void receive_scrolled_out_line(void *p, vt_line_t *line) {
78 vt_screen_t *screen;
79
80 screen = p;
81
82 if (vt_status_line_is_focused(screen)) {
83 vt_line_set_size_attr(line, 0);
84 line->mark = 0;
85
86 return;
87 }
88
89 if (screen->screen_listener && screen->screen_listener->line_scrolled_out) {
90 (*screen->screen_listener->line_scrolled_out)(screen->screen_listener->self);
91 }
92
93 if (screen->logvis) {
94 (*screen->logvis->visual_line)(screen->logvis, line);
95 } else {
96 line->num_filled_chars =
97 vt_line_get_num_filled_chars_except_sp_with_func(line, vt_char_equal);
98 }
99
100 vt_log_add(&screen->logs, line);
101
102 /* XXX see vt_line_iscii_visual() */
103 #if 1
104 if (line->num_chars > vt_screen_get_logical_cols(screen)) {
105 /*
106 * line->num_filled_chars can be more than line->num_chars
107 * without vt_line_reset() here because line is visualized.
108 */
109 vt_line_reset(line);
110 vt_line_set_updated(line);
111
112 vt_str_final(line->chars + vt_screen_get_logical_cols(screen),
113 line->num_chars - vt_screen_get_logical_cols(screen));
114 line->num_chars = vt_screen_get_logical_cols(screen);
115 } else
116 #endif
117 {
118 vt_line_set_size_attr(line, 0);
119 line->mark = 0;
120 }
121
122 if (vt_screen_is_backscrolling(screen) == BSM_STATIC) {
123 screen->backscroll_rows++;
124 }
125
126 if (screen->search) {
127 screen->search->row--;
128 }
129 }
130
scrolled_out_lines_finished(void * p)131 static void scrolled_out_lines_finished(void *p) {
132 vt_screen_t *screen;
133
134 screen = p;
135
136 if (vt_screen_is_backscrolling(screen) == BSM_DEFAULT) {
137 vt_screen_set_modified_all(screen);
138 }
139 }
140
window_scroll_upward_region(void * p,int beg_row,int end_row,u_int size)141 static int window_scroll_upward_region(void *p, int beg_row, int end_row, u_int size) {
142 vt_screen_t *screen;
143
144 screen = p;
145
146 if (screen->is_backscrolling) {
147 /*
148 * Not necessary to scrolling window. If backscroll_mode is BSM_DEFAULT,
149 * vt_screen_set_modified_all() in scrolled_out_lines_finished() later.
150 */
151 return 1;
152 }
153
154 if (!screen->screen_listener || !screen->screen_listener->window_scroll_upward_region) {
155 return 0;
156 }
157
158 return (*screen->screen_listener->window_scroll_upward_region)(screen->screen_listener->self,
159 beg_row, end_row, size);
160 }
161
window_scroll_downward_region(void * p,int beg_row,int end_row,u_int size)162 static int window_scroll_downward_region(void *p, int beg_row, int end_row, u_int size) {
163 vt_screen_t *screen;
164
165 screen = p;
166
167 if (screen->is_backscrolling) {
168 /*
169 * Not necessary to scrolling window. If backscroll_mode is BSM_DEFAULT,
170 * vt_screen_set_modified_all() in scrolled_out_lines_finished() later.
171 */
172 return 1;
173 }
174
175 if (!screen->screen_listener || !screen->screen_listener->window_scroll_downward_region) {
176 return 0;
177 }
178
179 return (*screen->screen_listener->window_scroll_downward_region)(screen->screen_listener->self,
180 beg_row, end_row, size);
181 }
182
top_line_is_wrapped(void * p)183 static int top_line_is_wrapped(void *p) {
184 vt_screen_t *screen;
185 u_int num;
186
187 screen = p;
188
189 if ((num = vt_get_num_logged_lines(&screen->logs)) > 0) {
190 if (vt_line_is_continued_to_next(vt_log_get(&screen->logs, num - 1))) {
191 return 1;
192 }
193 }
194
195 return 0;
196 }
197
modify_region(vt_screen_t * screen,int * end_char_index,int * end_row)198 static int modify_region(vt_screen_t *screen, /* visual */
199 int *end_char_index, int *end_row) {
200 int row;
201 vt_line_t *line;
202
203 /* Removing empty lines of the end. */
204
205 row = *end_row;
206
207 while ((line = vt_screen_get_line(screen, row)) == NULL || vt_line_is_empty(line)) {
208 if (--row < 0 && abs(row) > vt_get_num_logged_lines(&screen->logs)) {
209 return 0;
210 }
211 }
212
213 if (row < *end_row) {
214 if (vt_line_is_rtl(line)) {
215 *end_char_index = vt_line_beg_char_index_regarding_rtl(line);
216 } else {
217 if ((*end_char_index = vt_line_get_num_filled_chars_except_sp(line)) > 0) {
218 (*end_char_index)--;
219 }
220 }
221
222 *end_row = row;
223 }
224
225 return 1;
226 }
227
convert_col_to_char_index(vt_screen_t * screen,vt_line_t * line,int * beg_char_index,int * end_char_index,int beg_col,int end_col)228 static void convert_col_to_char_index(vt_screen_t *screen, /* visual */
229 vt_line_t *line, int *beg_char_index,
230 int *end_char_index, /* end + 1 */
231 int beg_col, int end_col) {
232 int padding;
233 int beg;
234 int end;
235 u_int rest;
236
237 if (vt_line_is_rtl(line) &&
238 (padding = vt_screen_get_cols(screen) - vt_line_get_num_filled_cols(line)) > 0) {
239 beg_col -= padding;
240 end_col -= padding;
241 }
242
243 *beg_char_index = vt_convert_col_to_char_index(line, &rest, beg_col, 0);
244
245 if ((end = vt_line_get_num_filled_chars_except_sp(line)) <= *beg_char_index ||
246 (end == *beg_char_index + 1 && rest > 0)) {
247 *beg_char_index = end;
248 } else if ((beg = vt_line_beg_char_index_regarding_rtl(line)) > *beg_char_index) {
249 *beg_char_index = beg;
250 }
251
252 *end_char_index = vt_convert_col_to_char_index(line, NULL, end_col, 0) + 1;
253 if (end < *end_char_index) {
254 *end_char_index = end;
255 }
256 }
257
reverse_or_restore_color_rect(vt_screen_t * screen,int beg_col,int beg_row,int end_col,int end_row,int (* func)(vt_line_t *,int))258 static int reverse_or_restore_color_rect(vt_screen_t *screen, /* visual */
259 int beg_col, int beg_row, int end_col, int end_row,
260 int (*func)(vt_line_t *, int)) {
261 int row;
262 int beg_char_index;
263 int end_char_index;
264 vt_line_t *line;
265
266 if (beg_col > end_col) {
267 int col;
268
269 col = beg_col;
270 beg_col = end_col;
271 end_col = col;
272 }
273
274 for (row = beg_row; row <= end_row; row++) {
275 if ((line = vt_screen_get_line(screen, row))) {
276 int char_index;
277
278 convert_col_to_char_index(screen, line, &beg_char_index, &end_char_index, beg_col, end_col);
279
280 for (char_index = beg_char_index; char_index < end_char_index; char_index++) {
281 (*func)(line, char_index);
282 }
283 }
284 }
285
286 return 1;
287 }
288
reverse_or_restore_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int (* func)(vt_line_t *,int))289 static int reverse_or_restore_color(vt_screen_t *screen, /* visual */
290 int beg_char_index, int beg_row, int end_char_index,
291 int end_row, int (*func)(vt_line_t *, int)) {
292 /*
293 * LTR line: a<aa bbb ccc>
294 * ^beg ^end
295 * RTL line: c<cc bbb aaa>
296 * ^end ^beg
297 * ^beg_regarding_rtl
298 * <>: selected region
299 */
300
301 int char_index;
302 int row;
303 vt_line_t *line;
304 u_int size_except_sp;
305 int beg_regarding_rtl;
306
307 #ifdef __DEBUG
308 bl_debug_printf(BL_DEBUG_TAG " reverse/restore region: %d %d %d %d\n", beg_char_index, beg_row,
309 end_char_index, end_row);
310 #endif
311
312 if (!modify_region(screen, &end_char_index, &end_row)) {
313 return 0;
314 }
315
316 /* Removing empty lines of the beginning. */
317
318 row = beg_row;
319
320 while (1) {
321 if (!(line = vt_screen_get_line(screen, row)) || vt_line_is_empty(line)) {
322 goto next_line;
323 }
324
325 size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
326 beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
327
328 if (vt_line_is_rtl(line)) {
329 if (row > beg_row || beg_char_index >= size_except_sp) {
330 beg_char_index = BL_MAX(size_except_sp, 1) - 1;
331 } else if (beg_char_index < beg_regarding_rtl) {
332 goto next_line;
333 }
334 } else {
335 if (row > beg_row || beg_char_index < beg_regarding_rtl) {
336 beg_char_index = beg_regarding_rtl;
337 } else if (beg_char_index >= size_except_sp) {
338 goto next_line;
339 }
340 }
341
342 break;
343
344 next_line:
345 if (++row > end_row) {
346 return 0;
347 }
348 }
349
350 if (row < end_row) {
351 if (vt_line_is_rtl(line)) {
352 for (char_index = beg_regarding_rtl; char_index <= beg_char_index; char_index++) {
353 (*func)(line, char_index);
354 }
355 } else {
356 for (char_index = beg_char_index; char_index < size_except_sp; char_index++) {
357 (*func)(line, char_index);
358 }
359 }
360
361 for (row++; row < end_row; row++) {
362 if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
363 continue;
364 }
365
366 size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
367
368 for (char_index = vt_line_beg_char_index_regarding_rtl(line); char_index < size_except_sp;
369 char_index++) {
370 (*func)(line, char_index);
371 }
372 }
373
374 if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
375 return 1;
376 }
377
378 size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
379 beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
380
381 if (vt_line_is_rtl(line)) {
382 beg_char_index = BL_MAX(size_except_sp, 1) - 1;
383 } else {
384 beg_char_index = beg_regarding_rtl;
385 }
386 }
387
388 /* row == end_row */
389
390 if (vt_line_is_rtl(line)) {
391 if (end_char_index < size_except_sp) {
392 for (char_index = BL_MAX(end_char_index, beg_regarding_rtl); char_index <= beg_char_index;
393 char_index++) {
394 (*func)(line, char_index);
395 }
396 }
397 } else {
398 if (end_char_index >= beg_regarding_rtl) {
399 for (char_index = beg_char_index; char_index < BL_MIN(end_char_index + 1, size_except_sp);
400 char_index++) {
401 (*func)(line, char_index);
402 }
403 }
404 }
405
406 return 1;
407 }
408
check_or_copy_region_rect(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_col,int beg_row,int end_col,int end_row)409 static u_int check_or_copy_region_rect(
410 vt_screen_t *screen, /* visual */
411 vt_char_t *chars, /* Behavior is undefined if chars is insufficient. */
412 u_int num_chars, int beg_col, int beg_row, int end_col, int end_row) {
413 int row;
414 vt_line_t *line;
415 u_int region_size;
416 int beg_char_index;
417 int end_char_index;
418
419 if (beg_col > end_col) {
420 int col;
421
422 col = beg_col;
423 beg_col = end_col;
424 end_col = col;
425 }
426
427 region_size = 0;
428
429 for (row = beg_row; row <= end_row; row++) {
430 if ((line = vt_screen_get_line(screen, row))) {
431 u_int size;
432
433 convert_col_to_char_index(screen, line, &beg_char_index, &end_char_index, beg_col, end_col);
434
435 size = end_char_index - beg_char_index;
436
437 if (chars && num_chars >= region_size + size) {
438 vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
439 }
440
441 region_size += size;
442
443 if (row < end_row) {
444 if (chars && num_chars >= region_size + 1) {
445 vt_char_copy(chars + region_size, vt_nl_ch());
446 }
447
448 region_size++;
449 }
450 }
451 }
452
453 return region_size;
454 }
455
check_or_copy_region(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_char_index,int beg_row,int end_char_index,int end_row)456 static u_int check_or_copy_region(
457 vt_screen_t *screen, /* visual */
458 vt_char_t *chars, /* Behavior is undefined if chars is insufficient. */
459 u_int num_chars, int beg_char_index, /* can be over size_except_sp */
460 int beg_row, int end_char_index, /* can be over size_except_sp */
461 int end_row) {
462 /*
463 * LTR line: a<aa bbb ccc>
464 * ^beg ^end
465 * RTL line: c<cc bbb aaa>
466 * ^end ^beg
467 * ^beg_regarding_rtl
468 * <>: selected region
469 */
470
471 vt_line_t *line;
472 u_int size;
473 u_int region_size;
474 u_int size_except_sp;
475 int beg_regarding_rtl;
476 int row;
477
478 #ifdef __DEBUG
479 bl_debug_printf(BL_DEBUG_TAG " check/copy region: %d %d %d %d\n", beg_char_index, beg_row,
480 end_char_index, end_row);
481 #endif
482
483 if (!modify_region(screen, &end_char_index, &end_row)) {
484 return 0;
485 }
486
487 row = beg_row;
488
489 /* Removing empty lines of the beginning. */
490 while (1) {
491 if (!(line = vt_screen_get_line(screen, row)) || vt_line_is_empty(line)) {
492 goto next_line;
493 }
494
495 size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
496 beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
497
498 if (vt_line_is_rtl(line)) {
499 if (row > beg_row || beg_char_index >= size_except_sp) {
500 beg_char_index = BL_MAX(size_except_sp, 1) - 1;
501 } else if (beg_char_index < beg_regarding_rtl) {
502 goto next_line;
503 }
504 } else {
505 if (row > beg_row || beg_char_index < beg_regarding_rtl) {
506 beg_char_index = beg_regarding_rtl;
507 } else if (beg_char_index >= size_except_sp) {
508 goto next_line;
509 }
510 }
511
512 break;
513
514 next_line:
515 if (++row > end_row) {
516 return 0;
517 }
518 }
519
520 region_size = 0;
521
522 if (row < end_row) {
523 if (vt_line_is_rtl(line)) {
524 size = beg_char_index - beg_regarding_rtl + 1;
525
526 if (chars && num_chars >= region_size + size) {
527 vt_line_copy_logical_str(line, chars + region_size, beg_regarding_rtl, size);
528 }
529 } else {
530 size = size_except_sp - beg_char_index;
531
532 if (chars && num_chars >= region_size + size) {
533 vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
534 }
535 }
536
537 region_size += size;
538
539 if (!vt_line_is_continued_to_next(line)) {
540 if (chars && num_chars > region_size) {
541 vt_char_copy(chars + region_size, vt_nl_ch());
542 }
543 region_size++;
544 }
545
546 for (row++; row < end_row; row++) {
547 line = vt_screen_get_line(screen, row);
548
549 beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
550 size = vt_line_get_num_filled_chars_except_sp(line) - beg_regarding_rtl;
551
552 if (chars && num_chars >= region_size + size) {
553 vt_line_copy_logical_str(line, chars + region_size, beg_regarding_rtl, size);
554 }
555
556 region_size += size;
557
558 if (!vt_line_is_continued_to_next(line)) {
559 if (chars && num_chars > region_size) {
560 vt_char_copy(chars + region_size, vt_nl_ch());
561 }
562 region_size++;
563 }
564 }
565
566 if (vt_line_is_empty((line = vt_screen_get_line(screen, row)))) {
567 return region_size;
568 }
569
570 size_except_sp = vt_line_get_num_filled_chars_except_sp(line);
571 beg_regarding_rtl = vt_line_beg_char_index_regarding_rtl(line);
572
573 if (vt_line_is_rtl(line)) {
574 beg_char_index = BL_MAX(size_except_sp, 1) - 1;
575 } else {
576 beg_char_index = beg_regarding_rtl;
577 }
578 }
579
580 /* row == end_row */
581
582 if (size_except_sp == 0) {
583 /* do nothing */
584 } else if (vt_line_is_rtl(line)) {
585 if (end_char_index < size_except_sp) {
586 if (end_char_index < beg_regarding_rtl) {
587 end_char_index = beg_regarding_rtl;
588 }
589
590 if (beg_row == end_row && beg_char_index < end_char_index) {
591 int tmp = end_char_index;
592 end_char_index = beg_char_index;
593 beg_char_index = tmp;
594 }
595
596 size = beg_char_index - end_char_index + 1;
597
598 if (chars && num_chars >= region_size + size) {
599 vt_line_copy_logical_str(line, chars + region_size, end_char_index, size);
600 }
601
602 region_size += size;
603 }
604 } else {
605 if (end_char_index >= beg_regarding_rtl) {
606 if (end_char_index >= size_except_sp) {
607 end_char_index = size_except_sp - 1;
608 }
609
610 size = end_char_index - beg_char_index + 1;
611
612 if (chars && num_chars >= region_size + size) {
613 vt_line_copy_logical_str(line, chars + region_size, beg_char_index, size);
614 }
615
616 region_size += size;
617 }
618 }
619
620 return region_size;
621 }
622
get_msec_time(void)623 static u_int32_t get_msec_time(void) {
624 #ifdef HAVE_GETTIMEOFDAY
625 struct timeval tv;
626
627 gettimeofday(&tv, NULL);
628
629 /* XXX '/ 1000' => '>> 10' and '* 1000' => '<< 10' */
630 return (tv.tv_sec << 10) + (tv.tv_usec >> 10);
631 #else
632 return time(NULL) << 10;
633 #endif
634 }
635
change_edit(vt_screen_t * screen,vt_edit_t * edit)636 static void change_edit(vt_screen_t *screen, vt_edit_t *edit) {
637 vt_screen_disable_local_echo(screen);
638
639 if (edit != screen->status_edit) {
640 if (screen->logvis) {
641 (*screen->logvis->init)(screen->logvis, &edit->model, &edit->cursor);
642 }
643
644 if (screen->main_edit) {
645 screen->main_edit = edit;
646 }
647
648 if (screen->edit != screen->status_edit) {
649 vt_edit_set_modified_all(edit);
650 }
651 } else {
652 screen->main_edit = screen->edit;
653 }
654
655 edit->bce_ch = screen->edit->bce_ch;
656
657 screen->edit = edit;
658 }
659
get_edit(vt_screen_t * screen,u_int page_id)660 static vt_edit_t *get_edit(vt_screen_t *screen, u_int page_id) {
661 if (page_id == 0) {
662 if (vt_screen_is_alternative_edit(screen)) {
663 return &screen->alt_edit;
664 } else {
665 return &screen->normal_edit;
666 }
667 } else if (page_id <= MAX_PAGE_ID) {
668 if (screen->page_edits == NULL) {
669 int count;
670
671 if (!(screen->page_edits = malloc(sizeof(vt_edit_t) * MAX_PAGE_ID))) {
672 return NULL;
673 }
674
675 for (count = 0; count < MAX_PAGE_ID; count++) {
676 vt_edit_init(screen->page_edits + count, &screen->edit_scroll_listener,
677 vt_edit_get_cols(&screen->normal_edit),
678 vt_edit_get_rows(&screen->normal_edit),
679 vt_edit_get_tab_size(&screen->normal_edit), 1,
680 screen->normal_edit.use_bce);
681 }
682 }
683
684 return screen->page_edits + (page_id - 1);
685 }
686
687 return NULL;
688 }
689
status_edit_new(vt_edit_t * main_edit)690 static vt_edit_t *status_edit_new(vt_edit_t *main_edit) {
691 vt_edit_t *status_edit;
692
693 if ((status_edit = malloc(sizeof(vt_edit_t)))) {
694 vt_edit_init(status_edit, main_edit->scroll_listener,
695 vt_edit_get_cols(main_edit), 1, vt_edit_get_tab_size(main_edit), 1,
696 main_edit->use_bce);
697 }
698
699 return status_edit;
700 }
701
get_wrap_lines_in_logs(vt_logs_t * logs,u_int * cols,u_int * rows,int beg)702 static u_int get_wrap_lines_in_logs(vt_logs_t *logs, u_int *cols, u_int *rows, int beg) {
703 vt_line_t *line;
704 u_int num_chars = 0;
705 int row;
706 u_int num;
707
708 for (row = beg; row > 0 && vt_line_is_continued_to_next(vt_log_get(logs, row - 1)); row--);
709
710 *rows = beg - row + 1;
711 *cols = 0;
712
713 for(; row < beg; row++) {
714 line = vt_log_get(logs, row);
715 vt_line_ctl_logical(line); /* logged lines are always visualized. */
716 *cols += vt_str_cols(line->chars, line->num_filled_chars);
717 num_chars += line->num_filled_chars;
718 vt_line_ctl_visual(line);
719 }
720
721 line = vt_log_get(logs, row);
722 vt_line_ctl_logical(line); /* logged lines are always visualized. */
723 if (!vt_line_is_continued_to_next(line)) {
724 num = vt_line_get_num_filled_chars_except_sp(line);
725 } else {
726 num = line->num_filled_chars;
727 }
728 *cols += vt_str_cols(line->chars, num);
729 num_chars += num;
730 vt_line_ctl_visual(line);
731
732 return num_chars;
733 }
734
get_wrap_lines_in_edit(vt_edit_t * edit,vt_char_t * chars,u_int * cols,u_int * rows,int beg)735 static u_int get_wrap_lines_in_edit(vt_edit_t *edit, vt_char_t *chars,
736 u_int *cols, u_int *rows, int beg) {
737 u_int num_chars = 0;
738 vt_line_t *line;
739 int row = beg;
740
741 if (cols) {
742 *cols = 0;
743 }
744
745 while ((line = vt_edit_get_line(edit, row++))) {
746 if (!vt_line_is_continued_to_next(line)) {
747 u_int num = vt_line_get_num_filled_chars_except_sp(line);
748
749 if (chars) {
750 vt_str_copy(chars + num_chars, line->chars, num);
751 } else {
752 *cols += vt_str_cols(line->chars, num);
753 }
754 num_chars += num;
755
756 break;
757 } else {
758 if (chars) {
759 vt_str_copy(chars + num_chars, line->chars, line->num_filled_chars);
760 } else {
761 *cols += vt_str_cols(line->chars, line->num_filled_chars);
762 }
763 num_chars += line->num_filled_chars;
764 }
765 }
766
767 if (rows) {
768 *rows = row - beg;
769 }
770
771 return num_chars;
772 }
773
774 /*
775 * Don't call this if vt_edit_is_logging(screen->edit) is false.
776 * comb_logical() is not called for lines reverted to the main screen, so
777 * dynamic combining is corrupt.
778 */
rollback(vt_screen_t * screen,u_int dst_cols,u_int dst_rows)779 static void rollback(vt_screen_t *screen, u_int dst_cols, u_int dst_rows) {
780 int src_row;
781 int src_end_row; /* end row in backlog */
782 u_int dst_row;
783 u_int n_scrollback;
784 vt_line_t *line;
785 vt_char_t *chars;
786 vt_char_t *chars_in_edit;
787 u_int src_cols_in_edit;
788 u_int src_nchars_in_edit;
789 u_int src_rows_in_edit;
790 u_int src_nchars;
791 u_int max_nchars;
792 u_int src_cols;
793 u_int count;
794 int cursor_col = vt_cursor_col(screen->edit);
795 int cursor_row = vt_cursor_row(screen->edit);
796
797 if ((src_end_row = vt_get_num_logged_lines(&screen->logs)) == 0) {
798 return;
799 }
800 src_row = (--src_end_row);
801
802 if (vt_line_is_continued_to_next(vt_log_get(&screen->logs, src_row))) {
803 src_nchars_in_edit = get_wrap_lines_in_edit(screen->edit, NULL,
804 &src_cols_in_edit, &src_rows_in_edit, 0);
805 src_cols = src_cols_in_edit;
806 dst_rows += src_rows_in_edit;
807
808 if ((chars_in_edit = vt_str_alloca(src_nchars_in_edit)) == NULL) {
809 return;
810 }
811 vt_str_init(chars_in_edit, src_nchars_in_edit);
812
813 get_wrap_lines_in_edit(screen->edit, chars_in_edit, NULL, NULL, 0);
814 } else {
815 src_nchars_in_edit = src_cols = src_cols_in_edit = src_rows_in_edit = 0;
816 }
817 max_nchars = src_nchars = 0;
818
819 count = 0;
820 while (1) {
821 u_int n;
822 u_int r;
823 u_int c;
824 u_int tmp;
825
826 n = get_wrap_lines_in_logs(&screen->logs, &c, &r, src_row);
827
828 if (src_cols + c == 0) {
829 tmp = 1;
830 } else {
831 tmp = (src_cols + c - 1) / dst_cols + 1;
832 }
833 if (tmp > dst_rows - count) {
834 break;
835 }
836
837 if (count == 0) {
838 n += src_nchars_in_edit;
839
840 if (cursor_row < src_rows_in_edit) {
841 /*
842 * XXX
843 * This calculation is incorrect if a character at the end of line
844 * is full width.
845 */
846 cursor_col += (c + cursor_row * dst_cols);
847 cursor_row = cursor_col / dst_cols;
848 cursor_col -= (cursor_row * dst_cols);
849 /*
850 * The number of rows reverted to the screen is added to cursor_row by
851 * 'cursor_row += (dst_rows - src_rows_in_edit)' below.
852 */
853 cursor_row -= (cursor_row + 1 - src_rows_in_edit);
854 }
855 }
856
857 if (n > max_nchars) {
858 max_nchars = n;
859 }
860
861 count += tmp;
862 src_cols = 0;
863 src_nchars += n;
864 if ((src_row -= r) < 0) {
865 break;
866 }
867 }
868
869 if ((dst_rows = count) == 0) {
870 return;
871 }
872
873 n_scrollback = src_end_row - src_row;
874 src_row++;
875
876 if ((chars = vt_str_alloca(max_nchars)) == NULL) {
877 return;
878 }
879 vt_str_init(chars, max_nchars);
880
881 vt_edit_scroll_downward(screen->edit, dst_rows - src_rows_in_edit);
882 cursor_row += (dst_rows - src_rows_in_edit);
883
884 dst_row = 0;
885 while (src_row <= src_end_row) {
886 u_int n;
887
888 line = vt_log_get(&screen->logs, src_row++);
889 src_cols = 0;
890 src_nchars = 0;
891
892 /* vt_line_ctl_visual() is not called because vt_log_rollback_index() clears it. */
893 vt_line_ctl_logical(line);
894 while (1) {
895 if (!vt_line_is_continued_to_next(line)) {
896 n = vt_line_get_num_filled_chars_except_sp(line);
897 vt_str_copy(chars + src_nchars, line->chars, n);
898 src_nchars += n;
899 src_cols += vt_str_cols(line->chars, n);
900
901 break;
902 } else {
903 vt_str_copy(chars + src_nchars, line->chars, line->num_filled_chars);
904 src_nchars += line->num_filled_chars;
905 src_cols += vt_str_cols(line->chars, line->num_filled_chars);
906
907 if (src_row > src_end_row) {
908 vt_str_copy(chars + src_nchars, chars_in_edit, src_nchars_in_edit);
909 src_nchars += src_nchars_in_edit;
910 src_cols += src_cols_in_edit;
911
912 break;
913 } else {
914 line = vt_log_get(&screen->logs, src_row++);
915 /* vt_line_ctl_visual() is not called because vt_log_rollback_index() clears it. */
916 vt_line_ctl_logical(line);
917 }
918 }
919 }
920
921 if (src_nchars > 0) {
922 dst_row += vt_edit_replace(screen->edit, dst_row, chars, src_cols, dst_cols);
923 } else {
924 dst_row++;
925 }
926 }
927
928 vt_edit_goto(screen->edit, cursor_col, cursor_row);
929
930 vt_str_final(chars, max_nchars);
931 if (src_nchars_in_edit > 0) {
932 vt_str_final(chars_in_edit, src_nchars_in_edit);
933 }
934
935 vt_log_rollback_index(&screen->logs, n_scrollback);
936 }
937
resize(vt_screen_t * screen,u_int cols,u_int rows,int pack)938 static int resize(vt_screen_t *screen, u_int cols /* > 0 */, u_int rows /* > 0 */, int pack) {
939 /* This considers status line ('rows' contains status line) */
940 u_int old_empty_rows;
941 u_int empty_rows;
942
943 old_empty_rows = vt_edit_get_rows(screen->edit) - vt_edit_get_num_filled_rows(screen->edit);
944
945 if (screen->status_edit) {
946 vt_edit_resize(screen->status_edit, cols, 1);
947
948 if (vt_screen_has_status_line(screen) && rows >= 2) {
949 rows--;
950 }
951 }
952
953 switch(vt_edit_resize(&screen->normal_edit, cols, rows)) {
954 case 2: /* RZ_WRAP */
955 screen->need_rewrap_logs = 1;
956 break;
957
958 case 3: /* RZ_SCROLL */
959 pack = 0;
960 break;
961 }
962
963 #ifdef DEBUG
964 {
965 vt_line_t *line = vt_edit_get_line(&screen->normal_edit,
966 vt_cursor_row(&screen->normal_edit));
967
968 if (line->num_filled_chars <= screen->normal_edit.cursor.char_index) {
969 bl_debug_printf("Cursor posion (%d %d) is over the end of line (%d)\n",
970 screen->normal_edit.cursor.char_index,
971 screen->normal_edit.cursor.row,
972 line->num_filled_chars);
973 abort();
974 }
975 }
976 #endif
977
978 vt_edit_resize(&screen->alt_edit, cols, rows);
979
980 if (screen->stored_edit) {
981 vt_edit_resize(&screen->stored_edit->edit, cols, rows);
982 }
983
984 if (screen->page_edits) {
985 u_int count;
986
987 for (count = 0; count < MAX_PAGE_ID; count++) {
988 vt_edit_resize(screen->page_edits + count, cols, rows);
989 }
990 }
991
992 empty_rows = vt_edit_get_rows(screen->edit) - vt_edit_get_num_filled_rows(screen->edit);
993
994 if (pack && vt_edit_is_logging(screen->edit) && empty_rows > old_empty_rows) {
995 u_int n_scroll = vt_get_num_logged_lines(&screen->logs);
996
997 if (n_scroll > 0) {
998 if (n_scroll > empty_rows - old_empty_rows) {
999 n_scroll = empty_rows - old_empty_rows;
1000 }
1001
1002 #if 0
1003 vt_edit_scroll_downward(screen->edit, n_scroll);
1004
1005 for (count = 0; count < n_scroll; count++) {
1006 vt_line_t *src = vt_log_get(&screen->logs,
1007 vt_get_num_logged_lines(&screen->logs) - n_scroll + count);
1008 vt_line_t *dst = vt_edit_get_line(screen->edit, count);
1009 /* vt_screen_resize() is always called in logical context. */
1010 vt_line_ctl_logical(src);
1011 vt_line_copy(dst, src);
1012 vt_line_set_modified_all(dst);
1013
1014 vt_edit_go_downward(screen->edit, 0);
1015 }
1016
1017 vt_log_rollback_index(&screen->logs, n_scroll);
1018 #else
1019 rollback(screen, cols, n_scroll);
1020 #endif
1021
1022 return 2;
1023 }
1024 }
1025
1026 return 1;
1027 }
1028
rewrap_logs(vt_screen_t * screen)1029 static int rewrap_logs(vt_screen_t *screen) {
1030 vt_logs_t new_logs;
1031 u_int src_row;
1032 vt_char_t *buf;
1033 u_int max_nchars = 0;
1034 u_int num_cols;
1035 u_int num_log_lines;
1036 vt_line_t new_line;
1037 int last_line_is_wrapped;
1038
1039 if ((num_log_lines = vt_get_num_logged_lines(&screen->logs)) == 0) {
1040 return 1;
1041 }
1042
1043 vt_log_init(&new_logs, vt_get_log_size(&screen->logs));
1044 if (vt_log_size_is_unlimited(&screen->logs)) {
1045 vt_unlimit_log_size(&new_logs);
1046 }
1047
1048 num_cols = vt_edit_get_cols(screen->edit);
1049 last_line_is_wrapped = vt_line_is_continued_to_next(vt_log_get(&screen->logs, num_log_lines - 1));
1050
1051 for (src_row = 0; src_row < num_log_lines; src_row++) {
1052 vt_line_t *line = vt_log_get(&screen->logs, src_row);
1053 vt_char_t *wrap_chars;
1054 u_int wrap_ncols;
1055 u_int len;
1056 u_int cols;
1057
1058 vt_line_ctl_logical(line);
1059
1060 if (!vt_line_is_continued_to_next(line)) {
1061 wrap_ncols = vt_str_cols(line->chars, vt_line_get_num_filled_chars_except_sp(line));
1062 if (wrap_ncols <= num_cols) {
1063 vt_line_ctl_visual(line);
1064 vt_log_add(&new_logs, line);
1065
1066 continue;
1067 }
1068
1069 wrap_chars = line->chars;
1070 } else {
1071 u_int count;
1072 u_int count2;
1073 u_int nchars = line->num_filled_chars;
1074
1075 wrap_ncols = vt_str_cols(line->chars, nchars);
1076
1077 for (count = 1; src_row + count < num_log_lines;) {
1078 line = vt_log_get(&screen->logs, src_row + (count++));
1079 vt_line_ctl_logical(line);
1080 if (!vt_line_is_continued_to_next(line)) {
1081 u_int n = vt_line_get_num_filled_chars_except_sp(line);
1082 nchars += n;
1083 wrap_ncols += vt_str_cols(line->chars, n);
1084 break;
1085 } else {
1086 nchars += line->num_filled_chars;
1087 wrap_ncols += vt_str_cols(line->chars, line->num_filled_chars);
1088 }
1089 }
1090
1091 if (nchars > max_nchars) {
1092 if (max_nchars > 0) {
1093 vt_str_final(buf, max_nchars);
1094 }
1095
1096 max_nchars = nchars;
1097
1098 if ((buf = vt_str_alloca(max_nchars)) == NULL) {
1099 return 0;
1100 }
1101 vt_str_init(buf, max_nchars);
1102 }
1103
1104 for (count2 = 0, len = 0; ; count2++) {
1105 /* vt_line_ctl_logical() was applied above. */
1106 line = vt_log_get(&screen->logs, src_row + count2);
1107
1108 if (count2 == count - 1) {
1109 u_int n = vt_line_get_num_filled_chars_except_sp(line);
1110 vt_str_copy(buf + len, line->chars, n);
1111 len += n;
1112 break;
1113 } else {
1114 vt_str_copy(buf + len, line->chars, line->num_filled_chars);
1115 len += line->num_filled_chars;
1116 }
1117 }
1118
1119 wrap_chars = buf;
1120
1121 src_row += (count - 1);
1122 }
1123
1124 vt_line_init(&new_line, num_cols);
1125 cols = 0;
1126 len = 0;
1127
1128 while (1) {
1129 u_int c;
1130 u_int n;
1131
1132 if (cols + num_cols > wrap_ncols) {
1133 c = wrap_ncols - cols;
1134 } else {
1135 c = num_cols;
1136 }
1137 n = vt_str_cols_to_len(wrap_chars + len, &c);
1138
1139 vt_line_overwrite(&new_line, 0, wrap_chars + len, n, c);
1140 if (screen->logvis) {
1141 (*screen->logvis->visual_line)(screen->logvis, &new_line);
1142 }
1143
1144 len += n;
1145 cols += c;
1146
1147 if (cols < wrap_ncols) {
1148 vt_line_set_continued_to_next(&new_line, 1);
1149 vt_log_add(&new_logs, &new_line);
1150 vt_line_reset(&new_line);
1151 } else {
1152 vt_log_add(&new_logs, &new_line);
1153 vt_line_final(&new_line);
1154 break;
1155 }
1156 }
1157 }
1158
1159 vt_str_final(buf, max_nchars);
1160
1161 if (last_line_is_wrapped) {
1162 vt_line_set_continued_to_next(vt_log_get(&new_logs, vt_get_num_logged_lines(&new_logs) - 1), 1);
1163 }
1164
1165 vt_log_final(&screen->logs);
1166 screen->logs = new_logs;
1167
1168 if (num_log_lines != vt_get_num_logged_lines(&screen->logs)) {
1169 return 2;
1170 }
1171
1172 return 1;
1173 }
1174
1175 /* --- global functions --- */
1176
vt_set_word_separators(const char * seps)1177 void vt_set_word_separators(const char *seps) {
1178 static char *default_word_separators;
1179
1180 if (default_word_separators) {
1181 if (word_separators != default_word_separators) {
1182 free(word_separators);
1183 }
1184
1185 if (seps == NULL || *seps == '\0') {
1186 /* Fall back to default. */
1187 word_separators = default_word_separators;
1188
1189 return;
1190 }
1191 } else if (seps == NULL || *seps == '\0') {
1192 /* Not changed */
1193 return;
1194 } else {
1195 /* Store the default value. */
1196 default_word_separators = word_separators;
1197 }
1198
1199 word_separators = bl_str_unescape(seps);
1200 }
1201
vt_get_word_separators(void)1202 char *vt_get_word_separators(void) { return word_separators; }
1203
vt_set_regard_uri_as_word(int flag)1204 void vt_set_regard_uri_as_word(int flag) { regard_uri_as_word = flag; }
1205
vt_get_regard_uri_as_word(void)1206 int vt_get_regard_uri_as_word(void) { return regard_uri_as_word; }
1207
vt_screen_new(u_int cols,u_int rows,u_int tab_size,u_int num_log_lines,int use_bce,vt_bs_mode_t bs_mode)1208 vt_screen_t *vt_screen_new(u_int cols, u_int rows, u_int tab_size, u_int num_log_lines,
1209 int use_bce, vt_bs_mode_t bs_mode) {
1210 vt_screen_t *screen;
1211
1212 if ((screen = calloc(1, sizeof(vt_screen_t))) == NULL) {
1213 #ifdef DEBUG
1214 bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1215 #endif
1216
1217 return NULL;
1218 }
1219
1220 screen->edit_scroll_listener.self = screen;
1221 screen->edit_scroll_listener.receive_scrolled_out_line = receive_scrolled_out_line;
1222 screen->edit_scroll_listener.scrolled_out_lines_finished = scrolled_out_lines_finished;
1223 screen->edit_scroll_listener.window_scroll_upward_region = window_scroll_upward_region;
1224 screen->edit_scroll_listener.window_scroll_downward_region = window_scroll_downward_region;
1225 screen->edit_scroll_listener.top_line_is_wrapped = top_line_is_wrapped;
1226
1227 if (!vt_edit_init(&screen->normal_edit, &screen->edit_scroll_listener, cols, rows, tab_size, 1,
1228 use_bce)) {
1229 #ifdef DEBUG
1230 bl_warn_printf(BL_DEBUG_TAG " vt_edit_init(normal_edit) failed.\n");
1231 #endif
1232
1233 goto error1;
1234 }
1235
1236 if (!vt_edit_init(&screen->alt_edit, &screen->edit_scroll_listener, cols, rows, tab_size, 0,
1237 use_bce)) {
1238 #ifdef DEBUG
1239 bl_warn_printf(BL_DEBUG_TAG " vt_edit_init(alt_edit) failed.\n");
1240 #endif
1241
1242 goto error2;
1243 }
1244
1245 screen->edit = &screen->normal_edit;
1246
1247 if (!vt_log_init(&screen->logs, num_log_lines)) {
1248 #ifdef DEBUG
1249 bl_warn_printf(BL_DEBUG_TAG " vt_log_init failed.\n");
1250 #endif
1251
1252 goto error3;
1253 }
1254
1255 screen->backscroll_mode = bs_mode;
1256
1257 return screen;
1258
1259 error3:
1260 vt_edit_final(&screen->normal_edit);
1261 error2:
1262 vt_edit_final(&screen->alt_edit);
1263 error1:
1264 free(screen);
1265
1266 return NULL;
1267 }
1268
vt_screen_destroy(vt_screen_t * screen)1269 int vt_screen_destroy(vt_screen_t *screen) {
1270 /*
1271 * this should be done before vt_edit_final() since termscr->logvis refers
1272 * to vt_edit_t and may have some data structure for it.
1273 */
1274 if (screen->logvis) {
1275 (*screen->logvis->logical)(screen->logvis);
1276 (*screen->logvis->destroy)(screen->logvis);
1277 }
1278
1279 vt_edit_final(&screen->normal_edit);
1280 vt_edit_final(&screen->alt_edit);
1281
1282 if (screen->page_edits) {
1283 int count;
1284
1285 for (count = 0; count < MAX_PAGE_ID; count++) {
1286 vt_edit_final(screen->page_edits + count);
1287 }
1288
1289 free(screen->page_edits);
1290 }
1291
1292 if (screen->status_edit) {
1293 vt_edit_final(screen->status_edit);
1294 free(screen->status_edit);
1295 }
1296
1297 vt_log_final(&screen->logs);
1298
1299 vt_screen_search_final(screen);
1300
1301 free(screen);
1302
1303 return 1;
1304 }
1305
vt_screen_set_listener(vt_screen_t * screen,vt_screen_event_listener_t * screen_listener)1306 void vt_screen_set_listener(vt_screen_t *screen, vt_screen_event_listener_t *screen_listener) {
1307 screen->screen_listener = screen_listener;
1308 }
1309
vt_screen_resize(vt_screen_t * screen,u_int cols,u_int rows)1310 int vt_screen_resize(vt_screen_t *screen, u_int cols, u_int rows) {
1311 if (cols == 0) {
1312 cols = 1;
1313 }
1314 if (rows == 0) {
1315 rows = 1;
1316 }
1317
1318 return resize(screen, cols, rows, 1);
1319 }
1320
1321 /* This considers status line */
vt_screen_cursor_row(vt_screen_t * screen)1322 int vt_screen_cursor_row(vt_screen_t *screen) {
1323 int row = vt_cursor_row(screen->edit);
1324
1325 if (vt_status_line_is_focused(screen)) {
1326 row += vt_edit_get_rows(screen->main_edit);
1327 }
1328
1329 return row;
1330 }
1331
1332 /* This considers status line */
vt_screen_cursor_row_in_screen(vt_screen_t * screen)1333 int vt_screen_cursor_row_in_screen(vt_screen_t *screen) {
1334 int row = vt_screen_cursor_row(screen);
1335
1336 if (screen->backscroll_rows > 0) {
1337 if ((row += screen->backscroll_rows) >= vt_screen_get_rows(screen)) {
1338 return -1;
1339 }
1340 }
1341
1342 return row;
1343 }
1344
vt_screen_get_logical_cols(vt_screen_t * screen)1345 u_int vt_screen_get_logical_cols(vt_screen_t *screen) {
1346 if (screen->logvis && screen->logvis->is_visual) {
1347 return (*screen->logvis->logical_cols)(screen->logvis);
1348 } else {
1349 return vt_edit_get_cols(screen->edit);
1350 }
1351 }
1352
1353 /* This considers status line */
vt_screen_get_rows(vt_screen_t * screen)1354 u_int vt_screen_get_rows(vt_screen_t *screen) {
1355 u_int rows;
1356
1357 rows = vt_edit_get_rows(screen->edit);
1358
1359 if (vt_screen_has_status_line(screen)) {
1360 if (vt_status_line_is_focused(screen)) {
1361 rows += vt_edit_get_rows(screen->main_edit);
1362 } else {
1363 rows ++;
1364 }
1365 }
1366
1367 return rows;
1368 }
1369
1370 /* This ignores status line */
vt_screen_get_logical_rows(vt_screen_t * screen)1371 u_int vt_screen_get_logical_rows(vt_screen_t *screen) {
1372 if (screen->logvis && screen->logvis->is_visual) {
1373 return (*screen->logvis->logical_rows)(screen->logvis);
1374 } else {
1375 return vt_edit_get_rows(screen->edit);
1376 }
1377 }
1378
vt_screen_convert_scr_row_to_abs(vt_screen_t * screen,int row)1379 int vt_screen_convert_scr_row_to_abs(vt_screen_t *screen, int row) {
1380 return row - screen->backscroll_rows;
1381 }
1382
1383 /*
1384 * If 0 <= row && row <= vt_screen_get_rows(), this function always returns
1385 * non-NULL value.
1386 * This considers status line.
1387 */
vt_screen_get_line(vt_screen_t * screen,int row)1388 vt_line_t *vt_screen_get_line(vt_screen_t *screen, int row) {
1389 if (row < 0) {
1390 return vt_log_get(&screen->logs, ROW_IN_LOGS(screen, row));
1391 } else if (vt_screen_has_status_line(screen)) {
1392 if (row == vt_edit_get_rows(screen->main_edit)) {
1393 return vt_edit_get_line(screen->status_edit, 0);
1394 } else {
1395 return vt_edit_get_line(screen->main_edit, row);
1396 }
1397 } else {
1398 return vt_edit_get_line(screen->edit, row);
1399 }
1400 }
1401
1402 /*
1403 * If 0 <= row && row <= vt_screen_get_rows(), this function always returns
1404 * non-NULL value.
1405 * This considers status line.
1406 */
vt_screen_get_line_in_screen(vt_screen_t * screen,int row)1407 vt_line_t *vt_screen_get_line_in_screen(vt_screen_t *screen, int row) {
1408 if (screen->is_backscrolling && screen->backscroll_rows > 0) {
1409 row -= screen->backscroll_rows;
1410
1411 if (row < 0) {
1412 return vt_log_get(&screen->logs, ROW_IN_LOGS(screen, row));
1413 }
1414 }
1415
1416 if (vt_screen_has_status_line(screen)) {
1417 if (row == vt_edit_get_rows(screen->main_edit)) {
1418 return vt_edit_get_line(screen->status_edit, 0);
1419 } else {
1420 return vt_edit_get_line(screen->main_edit, row);
1421 }
1422 } else {
1423 return vt_edit_get_line(screen->edit, row);
1424 }
1425 }
1426
vt_screen_set_modified_all(vt_screen_t * screen)1427 void vt_screen_set_modified_all(vt_screen_t *screen) {
1428 int row;
1429 vt_line_t *line;
1430 u_int num_rows = vt_screen_get_rows(screen);
1431
1432 for (row = 0; row < num_rows; row++) {
1433 line = vt_screen_get_line_in_screen(screen, row); /* Always non-NULL */
1434 vt_line_set_modified_all(line);
1435 }
1436 }
1437
vt_screen_add_logical_visual(vt_screen_t * screen,vt_logical_visual_t * logvis)1438 int vt_screen_add_logical_visual(vt_screen_t *screen, vt_logical_visual_t *logvis) {
1439 (*logvis->init)(logvis, &screen->edit->model, &screen->edit->cursor);
1440
1441 if (screen->logvis) {
1442 if (screen->container_logvis == NULL &&
1443 (screen->container_logvis = vt_logvis_container_new()) == NULL) {
1444 return 0;
1445 }
1446
1447 vt_logvis_container_add(screen->container_logvis, screen->logvis);
1448 vt_logvis_container_add(screen->container_logvis, logvis);
1449
1450 screen->logvis = screen->container_logvis;
1451 } else {
1452 screen->logvis = logvis;
1453 }
1454
1455 return 1;
1456 }
1457
vt_screen_destroy_logical_visual(vt_screen_t * screen)1458 int vt_screen_destroy_logical_visual(vt_screen_t *screen) {
1459 if (screen->logvis) {
1460 (*screen->logvis->logical)(screen->logvis);
1461 (*screen->logvis->destroy)(screen->logvis);
1462 screen->logvis = NULL;
1463 screen->container_logvis = NULL;
1464
1465 return 1;
1466 } else {
1467 return 0;
1468 }
1469 }
1470
vt_screen_render(vt_screen_t * screen)1471 void vt_screen_render(vt_screen_t *screen) {
1472 if (screen->logvis) {
1473 (*screen->logvis->render)(screen->logvis);
1474 }
1475 }
1476
vt_screen_visual(vt_screen_t * screen)1477 int vt_screen_visual(vt_screen_t *screen) {
1478 if (screen->logvis) {
1479 return (*screen->logvis->visual)(screen->logvis);
1480 } else {
1481 return 1;
1482 }
1483 }
1484
vt_screen_logical(vt_screen_t * screen)1485 int vt_screen_logical(vt_screen_t *screen) {
1486 if (screen->logvis) {
1487 return (*screen->logvis->logical)(screen->logvis);
1488 } else {
1489 return 1;
1490 }
1491 }
1492
vt_screen_is_backscrolling(vt_screen_t * screen)1493 vt_bs_mode_t vt_screen_is_backscrolling(vt_screen_t *screen) {
1494 if (screen->is_backscrolling == BSM_STATIC) {
1495 if (screen->backscroll_rows < vt_get_log_size(&screen->logs)) {
1496 return BSM_STATIC;
1497 } else {
1498 return BSM_DEFAULT;
1499 }
1500 } else {
1501 return screen->is_backscrolling;
1502 }
1503 }
1504
vt_set_backscroll_mode(vt_screen_t * screen,vt_bs_mode_t mode)1505 void vt_set_backscroll_mode(vt_screen_t *screen, vt_bs_mode_t mode) {
1506 screen->backscroll_mode = mode;
1507
1508 if (screen->is_backscrolling) {
1509 screen->is_backscrolling = mode;
1510 }
1511 }
1512
vt_enter_backscroll_mode(vt_screen_t * screen)1513 int vt_enter_backscroll_mode(vt_screen_t *screen) {
1514 int ret;
1515
1516 screen->is_backscrolling = screen->backscroll_mode;
1517
1518 if (screen->need_rewrap_logs) {
1519 ret = rewrap_logs(screen);
1520 screen->need_rewrap_logs = 0;
1521 } else {
1522 ret = 1;
1523 }
1524
1525 return ret;
1526 }
1527
vt_exit_backscroll_mode(vt_screen_t * screen)1528 void vt_exit_backscroll_mode(vt_screen_t *screen) {
1529 screen->is_backscrolling = 0;
1530 screen->backscroll_rows = 0;
1531
1532 vt_screen_set_modified_all(screen);
1533 }
1534
vt_screen_backscroll_to(vt_screen_t * screen,int row)1535 int vt_screen_backscroll_to(vt_screen_t *screen, int row) {
1536 if (!screen->is_backscrolling) {
1537 return 0;
1538 }
1539
1540 if (row > 0) {
1541 screen->backscroll_rows = 0;
1542 } else {
1543 screen->backscroll_rows = abs(row);
1544 }
1545
1546 vt_screen_set_modified_all(screen);
1547
1548 #ifdef EXIT_BS_AT_BOTTOM
1549 if (screen->backscroll_rows == 0) {
1550 vt_exit_backscroll_mode(screen);
1551 }
1552 #endif
1553
1554 return 1;
1555 }
1556
vt_screen_backscroll_upward(vt_screen_t * screen,u_int size)1557 int vt_screen_backscroll_upward(vt_screen_t *screen, u_int size) {
1558 vt_line_t *line;
1559 u_int count;
1560 u_int num_rows;
1561
1562 if (!screen->is_backscrolling) {
1563 return 0;
1564 }
1565
1566 if (screen->backscroll_rows < size) {
1567 size = screen->backscroll_rows;
1568 }
1569
1570 if (size == 0) {
1571 return 0;
1572 }
1573
1574 screen->backscroll_rows -= size;
1575
1576 num_rows = vt_screen_get_rows(screen);
1577
1578 if (!screen->screen_listener || !screen->screen_listener->window_scroll_upward_region ||
1579 !(*screen->screen_listener->window_scroll_upward_region)(
1580 screen->screen_listener->self, 0, num_rows - 1, size)) {
1581 for (count = 0; count < num_rows - size; count++) {
1582 if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1583 break;
1584 }
1585
1586 vt_line_set_modified_all(line);
1587 }
1588 }
1589
1590 for (count = num_rows - size; count < num_rows; count++) {
1591 if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1592 break;
1593 }
1594
1595 vt_line_set_modified_all(line);
1596 }
1597
1598 #ifdef EXIT_BS_AT_BOTTOM
1599 if (screen->backscroll_rows == 0) {
1600 vt_exit_backscroll_mode(screen);
1601 }
1602 #endif
1603
1604 return 1;
1605 }
1606
vt_screen_backscroll_downward(vt_screen_t * screen,u_int size)1607 int vt_screen_backscroll_downward(vt_screen_t *screen, u_int size) {
1608 vt_line_t *line;
1609 u_int count;
1610 int num_rows;
1611
1612 if (!screen->is_backscrolling) {
1613 return 0;
1614 }
1615
1616 if (vt_get_num_logged_lines(&screen->logs) < screen->backscroll_rows + size) {
1617 size = vt_get_num_logged_lines(&screen->logs) - screen->backscroll_rows;
1618 }
1619
1620 if (size == 0) {
1621 return 0;
1622 }
1623
1624 screen->backscroll_rows += size;
1625
1626 num_rows = vt_screen_get_rows(screen);
1627
1628 if (!screen->screen_listener || !screen->screen_listener->window_scroll_downward_region ||
1629 !(*screen->screen_listener->window_scroll_downward_region)(
1630 screen->screen_listener->self, 0, num_rows - 1, size)) {
1631 for (count = size; count < num_rows; count++) {
1632 if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1633 break;
1634 }
1635
1636 vt_line_set_modified_all(line);
1637 }
1638 }
1639
1640 for (count = 0; count < size; count++) {
1641 if ((line = vt_screen_get_line_in_screen(screen, count)) == NULL) {
1642 break;
1643 }
1644
1645 vt_line_set_modified_all(line);
1646 }
1647
1648 return 1;
1649 }
1650
vt_screen_backscroll_upward_to_mark(vt_screen_t * screen,int * row)1651 u_int vt_screen_backscroll_upward_to_mark(vt_screen_t *screen, int *row) {
1652 vt_line_t *line;
1653 int count = 0;
1654
1655 do {
1656 if ((line = vt_screen_get_line(screen, *row + (++count))) == NULL) {
1657 return 0;
1658 }
1659 } while (!line->mark);
1660
1661 *row += count;
1662 if (screen->backscroll_rows == 0 && *row >= 0) {
1663 return 0;
1664 }
1665
1666 count = *row + screen->backscroll_rows;
1667
1668 if (vt_screen_backscroll_upward(screen, count)) {
1669 return count;
1670 } else {
1671 return 0;
1672 }
1673 }
1674
vt_screen_backscroll_downward_to_mark(vt_screen_t * screen,int * row)1675 u_int vt_screen_backscroll_downward_to_mark(vt_screen_t *screen, int *row) {
1676 vt_line_t *line;
1677 int count = 0;
1678
1679 do {
1680 if ((line = vt_screen_get_line(screen, *row - (++count))) == NULL) {
1681 return 0;
1682 }
1683 } while (!line->mark);
1684
1685 *row -= count;
1686 if (screen->backscroll_rows == 0 && *row >= 0) {
1687 return 0;
1688 }
1689
1690 count = -screen->backscroll_rows - *row;
1691
1692 if (vt_screen_backscroll_downward(screen, count)) {
1693 return count;
1694 } else {
1695 return 0;
1696 }
1697 }
1698
vt_screen_reverse_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1699 int vt_screen_reverse_color(vt_screen_t *screen, int beg_char_index, int beg_row,
1700 int end_char_index, int end_row, int is_rect) {
1701 if (is_rect) {
1702 return reverse_or_restore_color_rect(screen, beg_char_index, beg_row, end_char_index, end_row,
1703 vt_line_reverse_color);
1704 } else {
1705 return reverse_or_restore_color(screen, beg_char_index, beg_row, end_char_index, end_row,
1706 vt_line_reverse_color);
1707 }
1708 }
1709
vt_screen_restore_color(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1710 int vt_screen_restore_color(vt_screen_t *screen, int beg_char_index, int beg_row,
1711 int end_char_index, int end_row, int is_rect) {
1712 if (is_rect) {
1713 return reverse_or_restore_color_rect(screen, beg_char_index, beg_row, end_char_index, end_row,
1714 vt_line_restore_color);
1715 } else {
1716 return reverse_or_restore_color(screen, beg_char_index, beg_row, end_char_index, end_row,
1717 vt_line_restore_color);
1718 }
1719 }
1720
vt_screen_copy_region(vt_screen_t * screen,vt_char_t * chars,u_int num_chars,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1721 u_int vt_screen_copy_region(vt_screen_t *screen, vt_char_t *chars, u_int num_chars,
1722 int beg_char_index, int beg_row, int end_char_index, int end_row,
1723 int is_rect) {
1724 if (is_rect) {
1725 return check_or_copy_region_rect(screen, chars, num_chars, beg_char_index, beg_row,
1726 end_char_index, end_row);
1727 } else {
1728 return check_or_copy_region(screen, chars, num_chars, beg_char_index, beg_row,
1729 end_char_index, end_row);
1730 }
1731 }
1732
vt_screen_get_region_size(vt_screen_t * screen,int beg_char_index,int beg_row,int end_char_index,int end_row,int is_rect)1733 u_int vt_screen_get_region_size(vt_screen_t *screen, int beg_char_index, int beg_row,
1734 int end_char_index, int end_row, int is_rect) {
1735 if (is_rect) {
1736 return check_or_copy_region_rect(screen, NULL, 0, beg_char_index, beg_row, end_char_index,
1737 end_row);
1738 } else {
1739 return check_or_copy_region(screen, NULL, 0, beg_char_index, beg_row, end_char_index, end_row);
1740 }
1741 }
1742
vt_screen_get_line_region(vt_screen_t * screen,int * beg_row,int * end_char_index,int * end_row,int base_row)1743 int vt_screen_get_line_region(vt_screen_t *screen, int *beg_row, int *end_char_index, int *end_row,
1744 int base_row) {
1745 int row;
1746 vt_line_t *line;
1747 vt_line_t *next_line;
1748
1749 /*
1750 * finding the end of line.
1751 */
1752 row = base_row;
1753
1754 if ((line = vt_screen_get_line(screen, row)) == NULL || vt_line_is_empty(line)) {
1755 return 0;
1756 }
1757
1758 while (1) {
1759 if (!vt_line_is_continued_to_next(line)) {
1760 break;
1761 }
1762
1763 if ((next_line = vt_screen_get_line(screen, row + 1)) == NULL || vt_line_is_empty(next_line)) {
1764 break;
1765 }
1766
1767 line = next_line;
1768 row++;
1769 }
1770
1771 *end_char_index = line->num_filled_chars - 1;
1772 *end_row = row;
1773
1774 /*
1775 * finding the beginning of line.
1776 */
1777 row = base_row;
1778
1779 while (1) {
1780 if ((line = vt_screen_get_line(screen, row - 1)) == NULL || vt_line_is_empty(line) ||
1781 !vt_line_is_continued_to_next(line)) {
1782 break;
1783 }
1784
1785 row--;
1786 }
1787
1788 *beg_row = row;
1789
1790 return 1;
1791 }
1792
vt_screen_get_word_region(vt_screen_t * screen,int * beg_char_index,int * beg_row,int * end_char_index,int * end_row,int base_char_index,int base_row)1793 int vt_screen_get_word_region(vt_screen_t *screen, int *beg_char_index, int *beg_row,
1794 int *end_char_index, int *end_row, int base_char_index,
1795 int base_row) {
1796 int row;
1797 int char_index;
1798 vt_line_t *line;
1799 vt_line_t *base_line;
1800 vt_char_t *ch;
1801 int flag;
1802
1803 if ((base_line = vt_screen_get_line(screen, base_row)) == NULL || vt_line_is_empty(base_line)) {
1804 return 0;
1805 }
1806
1807 if (regard_uri_as_word) {
1808 char *orig;
1809 u_int len;
1810 vt_char_t *str;
1811
1812 orig = word_separators;
1813 word_separators = "\" '`<>|";
1814 regard_uri_as_word = 0;
1815
1816 /* Not return 0 */
1817 vt_screen_get_word_region(screen, beg_char_index, beg_row, end_char_index, end_row,
1818 base_char_index, base_row);
1819
1820 word_separators = orig;
1821 regard_uri_as_word = 1;
1822
1823 if ((len = check_or_copy_region(screen, NULL, 0, *beg_char_index, *beg_row, *end_char_index,
1824 *end_row)) > 0 &&
1825 (str = vt_str_alloca(len))) {
1826 u_int count;
1827 int slash_num;
1828
1829 vt_str_init(str, len);
1830 check_or_copy_region(screen, str, len, *beg_char_index, *beg_row, *end_char_index, *end_row);
1831
1832 for (slash_num = 0, count = 0; count < len; count++) {
1833 if (vt_char_cs(str + count) == US_ASCII) {
1834 switch (vt_char_code(str + count)) {
1835 case '/':
1836 if (++slash_num == 1) {
1837 continue;
1838 }
1839
1840 /* Fall through */
1841
1842 case '@':
1843 vt_str_final(str, len);
1844
1845 return 1;
1846 }
1847 }
1848 }
1849
1850 vt_str_final(str, len);
1851 }
1852 }
1853
1854 if (is_word_separator(vt_char_at(base_line, base_char_index))) {
1855 *beg_char_index = base_char_index;
1856 *end_char_index = base_char_index;
1857 *beg_row = base_row;
1858 *end_row = base_row;
1859
1860 return 1;
1861 }
1862
1863 flag = vt_char_is_fullwidth(vt_char_at(base_line, base_char_index));
1864
1865 /*
1866 * search the beg of word
1867 */
1868 row = base_row;
1869 char_index = base_char_index;
1870 line = base_line;
1871
1872 while (1) {
1873 if (char_index == 0) {
1874 if ((line = vt_screen_get_line(screen, row - 1)) == NULL || vt_line_is_empty(line) ||
1875 !vt_line_is_continued_to_next(line)) {
1876 *beg_char_index = char_index;
1877
1878 break;
1879 }
1880
1881 row--;
1882 char_index = line->num_filled_chars - 1;
1883 } else {
1884 char_index--;
1885 }
1886
1887 ch = vt_char_at(line, char_index);
1888
1889 if (is_word_separator(ch) || flag != vt_char_is_fullwidth(ch)) {
1890 *beg_char_index = char_index + 1;
1891
1892 break;
1893 }
1894 }
1895
1896 *beg_row = row;
1897
1898 /*
1899 * search the end of word.
1900 */
1901 row = base_row;
1902 char_index = base_char_index;
1903 line = base_line;
1904
1905 while (1) {
1906 if (char_index == line->num_filled_chars - 1) {
1907 if (!vt_line_is_continued_to_next(line) ||
1908 (line = vt_screen_get_line(screen, row + 1)) == NULL || vt_line_is_empty(line)) {
1909 *end_char_index = char_index;
1910
1911 break;
1912 }
1913
1914 row++;
1915 char_index = 0;
1916 } else {
1917 char_index++;
1918 }
1919
1920 ch = vt_char_at(line, char_index);
1921
1922 if (is_word_separator(ch) || flag != vt_char_is_fullwidth(ch)) {
1923 *end_char_index = char_index - 1;
1924
1925 break;
1926 }
1927 }
1928
1929 *end_row = row;
1930
1931 #ifdef __DEBUG
1932 bl_debug_printf(BL_DEBUG_TAG " selected word region: %d %d => %d %d %d %d\n", base_char_index,
1933 base_row, *beg_char_index, *beg_row, *end_char_index, *end_row);
1934 #endif
1935
1936 return 1;
1937 }
1938
vt_screen_search_init(vt_screen_t * screen,int char_index,int row,int (* match)(size_t *,size_t *,void *,u_char *,int))1939 int vt_screen_search_init(vt_screen_t *screen,
1940 int char_index, int row, /* -1: cursor position */
1941 int (*match)(size_t *, size_t *, void *, u_char *, int)) {
1942 if (screen->search) {
1943 return 0;
1944 }
1945
1946 if (!(screen->search = malloc(sizeof(*screen->search)))) {
1947 return 0;
1948 }
1949
1950 screen->search->match = match;
1951
1952 screen->search->char_index = char_index;
1953 screen->search->row = row;
1954
1955 return 1;
1956 }
1957
vt_screen_search_final(vt_screen_t * screen)1958 void vt_screen_search_final(vt_screen_t *screen) {
1959 free(screen->search);
1960 screen->search = NULL;
1961 }
1962
vt_screen_search_reset_position(vt_screen_t * screen)1963 int vt_screen_search_reset_position(vt_screen_t *screen) {
1964 if (!screen->search) {
1965 return 0;
1966 }
1967
1968 /* char_index == -1 has special meaning. */
1969 screen->search->char_index = -1;
1970 screen->search->row = -1;
1971
1972 return 1;
1973 }
1974
1975 /*
1976 * It is assumed that this function is called in *visual* context.
1977 *
1978 * XXX
1979 * It is not supported to match text in multiple lines.
1980 */
vt_screen_search_find(vt_screen_t * screen,int * beg_char_index,int * beg_row,int * end_char_index,int * end_row,void * regex,int backward)1981 int vt_screen_search_find(vt_screen_t *screen,
1982 int *beg_char_index, /* visual position is returned */
1983 int *beg_row, /* visual position is returned */
1984 int *end_char_index, /* visual position is returned */
1985 int *end_row, /* visual position is returned */
1986 void *regex, int backward) {
1987 vt_char_t *line_str;
1988 u_int cols;
1989 ef_parser_t *parser;
1990 ef_conv_t *conv;
1991 u_char *buf;
1992 size_t buf_len;
1993 vt_line_t *line;
1994 int step;
1995 int res;
1996
1997 if (!screen->search) {
1998 return 0;
1999 }
2000
2001 cols = vt_screen_get_logical_cols(screen);
2002 if (!(line_str = vt_str_alloca(cols))) {
2003 return 0;
2004 }
2005 vt_str_init(line_str, cols);
2006
2007 buf_len = vt_screen_get_logical_cols(screen) * VTCHAR_UTF_MAX_SIZE + 1;
2008 if (!(buf = alloca(buf_len))) {
2009 return 0;
2010 }
2011
2012 if (!(parser = vt_str_parser_new())) {
2013 return 0;
2014 }
2015
2016 if (!(conv = vt_char_encoding_conv_new(VT_UTF8))) {
2017 (*parser->destroy)(parser);
2018
2019 return 0;
2020 }
2021
2022 /* char_index == -1 has special meaning. */
2023 if (screen->search->char_index == -1) {
2024 screen->search->char_index = vt_screen_cursor_char_index(screen);
2025 screen->search->row = vt_screen_cursor_row(screen);
2026 }
2027
2028 *beg_char_index = screen->search->char_index;
2029 *beg_row = screen->search->row;
2030 step = (backward ? -1 : 1);
2031
2032 #ifdef DEBUG
2033 bl_debug_printf(BL_DEBUG_TAG " Search start from %d %d\n", *beg_char_index, *beg_row);
2034 #endif
2035
2036 res = 0;
2037
2038 for (; (line = vt_screen_get_line(screen, *beg_row)); (*beg_row) += step) {
2039 size_t line_len;
2040 size_t match_beg;
2041 size_t match_len;
2042
2043 if ((line_len = vt_line_get_num_filled_chars_except_sp(line)) == 0) {
2044 continue;
2045 }
2046
2047 /*
2048 * Visual => Logical
2049 */
2050 vt_line_copy_logical_str(line, line_str, 0, line_len);
2051
2052 (*parser->init)(parser);
2053 if (backward) {
2054 vt_str_parser_set_str(parser, line_str, (*beg_row != screen->search->row)
2055 ? line_len
2056 : BL_MIN(*beg_char_index + 1, line_len));
2057 *beg_char_index = 0;
2058 } else {
2059 if (*beg_row != screen->search->row) {
2060 *beg_char_index = 0;
2061 } else if (line_len <= *beg_char_index) {
2062 continue;
2063 }
2064
2065 vt_str_parser_set_str(parser, line_str + (*beg_char_index), line_len - *beg_char_index);
2066 }
2067
2068 (*conv->init)(conv);
2069 *(buf + (*conv->convert)(conv, buf, buf_len - 1, parser)) = '\0';
2070
2071 if ((*screen->search->match)(&match_beg, &match_len, regex, buf, backward)) {
2072 size_t count;
2073 u_int comb_size;
2074 int beg;
2075 int end;
2076 vt_char_t *comb;
2077
2078 comb = vt_get_combining_chars(line_str + (*beg_char_index), &comb_size);
2079
2080 for (count = 0; count < match_beg; count++) {
2081 /* Ignore 2nd and following bytes. */
2082 if ((buf[count] & 0xc0) != 0x80) {
2083 while (comb_size > 0 && vt_char_cs(comb) == PICTURE_CHARSET) {
2084 comb_size--;
2085 comb++;
2086 }
2087
2088 if (comb_size > 0) {
2089 comb_size--;
2090 comb++;
2091 } else if ((++(*beg_char_index)) >= line_len - 1) {
2092 break;
2093 } else {
2094 comb = vt_get_combining_chars(line_str + (*beg_char_index), &comb_size);
2095 }
2096 }
2097 }
2098
2099 *end_char_index = (*beg_char_index) - 1;
2100 for (; count < match_beg + match_len; count++) {
2101 /* Ignore 2nd and following bytes. */
2102 if ((buf[count] & 0xc0) != 0x80) {
2103 while (comb_size > 0 && vt_char_cs(comb) == PICTURE_CHARSET) {
2104 comb_size--;
2105 comb++;
2106 }
2107
2108 if (comb_size > 0) {
2109 comb_size--;
2110 comb++;
2111 } else if ((++(*end_char_index)) >= line_len - 1) {
2112 break;
2113 } else {
2114 comb = vt_get_combining_chars(line_str + (*end_char_index), &comb_size);
2115 }
2116 }
2117 }
2118
2119 if (*end_char_index < *beg_char_index) {
2120 continue;
2121 }
2122
2123 *end_row = *beg_row;
2124
2125 if (backward) {
2126 if (*beg_char_index > 0) {
2127 screen->search->char_index = *beg_char_index - 1;
2128 screen->search->row = *beg_row;
2129 } else {
2130 screen->search->char_index = vt_screen_get_logical_cols(screen) - 1;
2131 screen->search->row = *beg_row - 1;
2132 }
2133 } else {
2134 screen->search->char_index = *beg_char_index + 1;
2135 screen->search->row = *beg_row;
2136 }
2137
2138 #ifdef DEBUG
2139 bl_debug_printf(BL_DEBUG_TAG " Search position x %d y %d\n", screen->search->char_index,
2140 screen->search->row);
2141 #endif
2142
2143 /*
2144 * Logical => Visual
2145 *
2146 * XXX Incomplete
2147 * Current implementation have problems like this.
2148 * (logical)RRRLLLNNN => (visual)NNNLLLRRR
2149 * Searching LLLNNN => ^^^^^^ hits but only NNNL is reversed.
2150 */
2151 beg = vt_line_convert_logical_char_index_to_visual(line, *beg_char_index, NULL);
2152 end = vt_line_convert_logical_char_index_to_visual(line, *end_char_index, NULL);
2153
2154 if (beg > end) {
2155 *beg_char_index = end;
2156 *end_char_index = beg;
2157 } else {
2158 *beg_char_index = beg;
2159 *end_char_index = end;
2160 }
2161
2162 if (vt_line_is_rtl(line)) {
2163 int char_index;
2164
2165 /* XXX for x_selection */
2166 char_index = -(*beg_char_index);
2167 *beg_char_index = -(*end_char_index);
2168 *end_char_index = char_index;
2169 }
2170
2171 res = 1;
2172
2173 break;
2174 }
2175 }
2176
2177 (*parser->destroy)(parser);
2178 (*conv->destroy)(conv);
2179 vt_str_final(line_str, vt_screen_get_logical_cols(screen));
2180
2181 return res;
2182 }
2183
vt_screen_blink(vt_screen_t * screen)2184 int vt_screen_blink(vt_screen_t *screen) {
2185 int has_blinking_char;
2186
2187 has_blinking_char = 0;
2188
2189 if (screen->has_blinking_char) {
2190 int char_index;
2191 int row;
2192 vt_line_t *line;
2193 u_int num_rows = vt_screen_get_rows(screen);
2194
2195 /* '+ screen->backscroll_rows' prohibit backlog lines to blink. */
2196 for (row = 0; row + screen->backscroll_rows < num_rows; row++) {
2197 line = vt_screen_get_line(screen, row); /* Always non-NULL */
2198
2199 for (char_index = 0; char_index < line->num_filled_chars; char_index++) {
2200 if (vt_char_is_blinking(line->chars + char_index)) {
2201 vt_line_set_modified(line, char_index, char_index);
2202 has_blinking_char = 1;
2203 }
2204 }
2205 }
2206
2207 if (screen->backscroll_rows == 0) {
2208 screen->has_blinking_char = has_blinking_char;
2209 }
2210 }
2211
2212 return has_blinking_char;
2213 }
2214
2215 /*
2216 * VT100 commands
2217 *
2218 * !! Notice !!
2219 * These functions are called under logical order mode.
2220 */
2221
vt_screen_get_n_prev_char(vt_screen_t * screen,int n)2222 vt_char_t *vt_screen_get_n_prev_char(vt_screen_t *screen, int n) {
2223 int char_index;
2224 int row;
2225 vt_line_t *line;
2226
2227 if (!get_n_prev_char_pos(screen, &char_index, &row, 1)) {
2228 return NULL;
2229 }
2230
2231 if ((line = vt_edit_get_line(screen->edit, row)) == NULL) {
2232 return NULL;
2233 }
2234
2235 return vt_char_at(line, char_index);
2236 }
2237
vt_screen_combine_with_prev_char(vt_screen_t * screen,u_int32_t code,ef_charset_t cs,int is_fullwidth,int is_awidth,int is_comb,vt_color_t fg_color,vt_color_t bg_color,int is_bold,int is_italic,int line_style,int is_blinking,int is_protected)2238 int vt_screen_combine_with_prev_char(vt_screen_t *screen, u_int32_t code, ef_charset_t cs,
2239 int is_fullwidth, int is_awidth, int is_comb,
2240 vt_color_t fg_color, vt_color_t bg_color, int is_bold,
2241 int is_italic, int line_style, int is_blinking,
2242 int is_protected) {
2243 int char_index;
2244 int row;
2245 vt_char_t *ch;
2246 vt_line_t *line;
2247
2248 if (!get_n_prev_char_pos(screen, &char_index, &row, 1)) {
2249 return 0;
2250 }
2251
2252 if ((line = vt_edit_get_line(screen->edit, row)) == NULL) {
2253 return 0;
2254 }
2255
2256 if ((ch = vt_char_at(line, char_index)) == NULL) {
2257 return 0;
2258 }
2259
2260 if (!vt_char_combine(ch, code, cs, is_fullwidth, is_awidth, is_comb, fg_color, bg_color, is_bold,
2261 is_italic, line_style, is_blinking, is_protected)) {
2262 return 0;
2263 }
2264
2265 vt_line_set_modified(line, char_index, char_index);
2266
2267 return 1;
2268 }
2269
vt_screen_insert_chars(vt_screen_t * screen,vt_char_t * chars,u_int len)2270 int vt_screen_insert_chars(vt_screen_t *screen, vt_char_t *chars, u_int len) {
2271 return vt_edit_insert_chars(screen->edit, chars, len);
2272 }
2273
vt_screen_insert_new_lines(vt_screen_t * screen,u_int size)2274 int vt_screen_insert_new_lines(vt_screen_t *screen, u_int size) {
2275 u_int count;
2276
2277 if (size > vt_edit_get_rows(screen->edit)) {
2278 size = vt_edit_get_rows(screen->edit);
2279 }
2280
2281 for (count = 0; count < size; count++) {
2282 vt_edit_insert_new_line(screen->edit);
2283 }
2284
2285 return 1;
2286 }
2287
vt_screen_overwrite_chars(vt_screen_t * screen,vt_char_t * chars,u_int len)2288 int vt_screen_overwrite_chars(vt_screen_t *screen, vt_char_t *chars, u_int len) {
2289 return vt_edit_overwrite_chars(screen->edit, chars, len);
2290 }
2291
vt_screen_delete_lines(vt_screen_t * screen,u_int size)2292 int vt_screen_delete_lines(vt_screen_t *screen, u_int size) {
2293 u_int count;
2294
2295 if (size > vt_edit_get_rows(screen->edit)) {
2296 size = vt_edit_get_rows(screen->edit);
2297 }
2298
2299 for (count = 0; count < size; count++) {
2300 if (!vt_edit_delete_line(screen->edit)) {
2301 #ifdef DEBUG
2302 bl_warn_printf(BL_DEBUG_TAG " deleting nonexisting lines.\n");
2303 #endif
2304
2305 return 0;
2306 }
2307 }
2308
2309 return 1;
2310 }
2311
vt_screen_go_forward(vt_screen_t * screen,u_int size,int scroll)2312 int vt_screen_go_forward(vt_screen_t *screen, u_int size, int scroll) {
2313 u_int count;
2314
2315 for (count = 0; count < size; count++) {
2316 if (!vt_edit_go_forward(screen->edit, 0)) {
2317 if (scroll) {
2318 if (size > vt_edit_get_cols(screen->edit)) {
2319 size = vt_edit_get_cols(screen->edit);
2320 if (size <= count) {
2321 break;
2322 }
2323 }
2324
2325 vt_edit_scroll_leftward(screen->edit, size - count);
2326
2327 break;
2328 }
2329 #ifdef DEBUG
2330 else {
2331 bl_warn_printf(BL_DEBUG_TAG " cursor cannot go forward any more.\n");
2332 }
2333 #endif
2334
2335 return 0;
2336 }
2337 }
2338
2339 return 1;
2340 }
2341
vt_screen_go_back(vt_screen_t * screen,u_int size,int scroll)2342 int vt_screen_go_back(vt_screen_t *screen, u_int size, int scroll) {
2343 u_int count;
2344
2345 for (count = 0; count < size; count++) {
2346 if (!vt_edit_go_back(screen->edit, 0)) {
2347 if (scroll) {
2348 if (size > vt_edit_get_cols(screen->edit)) {
2349 size = vt_edit_get_cols(screen->edit);
2350 if (size <= count) {
2351 break;
2352 }
2353 }
2354
2355 vt_edit_scroll_rightward(screen->edit, size - count);
2356
2357 break;
2358 }
2359 #ifdef DEBUG
2360 else {
2361 bl_warn_printf(BL_DEBUG_TAG " cursor cannot go back any more.\n");
2362 }
2363 #endif
2364
2365 return 0;
2366 }
2367 }
2368
2369 return 1;
2370 }
2371
vt_screen_go_upward(vt_screen_t * screen,u_int size)2372 int vt_screen_go_upward(vt_screen_t *screen, u_int size) {
2373 u_int count;
2374
2375 for (count = 0; count < size; count++) {
2376 if (!vt_edit_go_upward(screen->edit, 0)) {
2377 #ifdef DEBUG
2378 bl_warn_printf(BL_DEBUG_TAG " cursor cannot go upward any more.\n");
2379 #endif
2380
2381 return 0;
2382 }
2383 }
2384
2385 return 1;
2386 }
2387
vt_screen_go_downward(vt_screen_t * screen,u_int size)2388 int vt_screen_go_downward(vt_screen_t *screen, u_int size) {
2389 u_int count;
2390
2391 for (count = 0; count < size; count++) {
2392 if (!vt_edit_go_downward(screen->edit, 0)) {
2393 #ifdef DEBUG
2394 bl_warn_printf(BL_DEBUG_TAG " cursor cannot go downward any more.\n");
2395 #endif
2396
2397 return 0;
2398 }
2399 }
2400
2401 return 1;
2402 }
2403
vt_screen_use_normal_edit(vt_screen_t * screen)2404 int vt_screen_use_normal_edit(vt_screen_t *screen) {
2405 if (screen->edit != &screen->normal_edit) {
2406 int col = screen->edit->cursor.col;
2407 int row = screen->edit->cursor.row;
2408
2409 change_edit(screen, &screen->normal_edit);
2410 vt_edit_goto(screen->edit, col, row);
2411
2412 return 1;
2413 } else {
2414 return 0;
2415 }
2416 }
2417
vt_screen_use_alternative_edit(vt_screen_t * screen)2418 int vt_screen_use_alternative_edit(vt_screen_t *screen) {
2419 if (screen->edit != &screen->alt_edit) {
2420 int col = screen->edit->cursor.col;
2421 int row = screen->edit->cursor.row;
2422
2423 change_edit(screen, &screen->alt_edit);
2424 vt_edit_goto(screen->edit, col, row);
2425
2426 return 1;
2427 } else {
2428 return 0;
2429 }
2430 }
2431
2432 /*
2433 * This function must be called in logical context in order to swap
2434 * stored_edit->edit and screen->edit in the same conditions.
2435 */
vt_screen_enable_local_echo(vt_screen_t * screen)2436 int vt_screen_enable_local_echo(vt_screen_t *screen) {
2437 if (!screen->stored_edit) {
2438 if (!(screen->stored_edit = malloc(sizeof(vt_stored_edit_t)))) {
2439 return 0;
2440 }
2441
2442 screen->stored_edit->edit = *screen->edit;
2443
2444 /*
2445 * New data is allocated in screen->edit, not in stored_edit->edit
2446 * because screen->edit allocated here will be deleted and
2447 * stored_edit->edit will be revived in vt_screen_disable_local_echo().
2448 */
2449 if (!vt_edit_clone(screen->edit, &screen->stored_edit->edit)) {
2450 free(screen->stored_edit);
2451 screen->stored_edit = NULL;
2452
2453 return 0;
2454 }
2455
2456 screen->edit->is_logging = 0;
2457 }
2458
2459 screen->stored_edit->time = get_msec_time();
2460
2461 return 1;
2462 }
2463
vt_screen_local_echo_wait(vt_screen_t * screen,int msec)2464 int vt_screen_local_echo_wait(vt_screen_t *screen, int msec /* 0: stored_edit->time = 0 (>=
2465 get_msec_time() is always false.) */
2466 ) {
2467 if (screen->stored_edit) {
2468 if (msec == 0) {
2469 screen->stored_edit->time = 0;
2470 } else if (screen->stored_edit->time + msec >= get_msec_time()) {
2471 return 1;
2472 }
2473 }
2474
2475 return 0;
2476 }
2477
2478 /*
2479 * This function must be called in logical context in order to swap
2480 * stored_edit->edit and screen->edit in the same conditions.
2481 */
vt_screen_disable_local_echo(vt_screen_t * screen)2482 int vt_screen_disable_local_echo(vt_screen_t *screen) {
2483 u_int row;
2484 u_int num_rows;
2485 vt_line_t *old_line;
2486 vt_line_t *new_line;
2487
2488 if (!screen->stored_edit) {
2489 return 0;
2490 }
2491
2492 num_rows = vt_edit_get_rows(screen->edit);
2493
2494 /*
2495 * Set modified flag to the difference between the current edit and the stored
2496 * one
2497 * to restore the screen before starting local echo.
2498 */
2499 for (row = 0; row < num_rows; row++) {
2500 if ((new_line = vt_edit_get_line(&screen->stored_edit->edit, row)) &&
2501 /* old_line is always non-NULL */
2502 (vt_line_is_modified((old_line = vt_edit_get_line(screen->edit, row))) ||
2503 old_line->num_filled_chars != new_line->num_filled_chars
2504 #if 1
2505 || !vt_str_bytes_equal(old_line->chars, new_line->chars, new_line->num_filled_chars)
2506 #endif
2507 )) {
2508 vt_line_set_modified_all(new_line);
2509 }
2510 }
2511
2512 vt_edit_final(screen->edit);
2513 *screen->edit = screen->stored_edit->edit;
2514
2515 free(screen->stored_edit);
2516 screen->stored_edit = NULL;
2517
2518 return 1;
2519 }
2520
vt_screen_copy_area(vt_screen_t * screen,int src_col,int src_row,u_int num_copy_cols,u_int num_copy_rows,u_int src_page,int dst_col,int dst_row,u_int dst_page)2521 void vt_screen_copy_area(vt_screen_t *screen, int src_col, int src_row, u_int num_copy_cols,
2522 u_int num_copy_rows, u_int src_page,
2523 int dst_col, int dst_row, u_int dst_page) {
2524 vt_edit_t *src_edit;
2525 vt_edit_t *dst_edit;
2526
2527 if (src_page > MAX_PAGE_ID) {
2528 src_page = MAX_PAGE_ID;
2529 }
2530 if (dst_page > MAX_PAGE_ID) {
2531 dst_page = MAX_PAGE_ID;
2532 }
2533
2534 if ((src_edit = get_edit(screen, src_page)) && (dst_edit = get_edit(screen, dst_page))) {
2535 vt_edit_copy_area(src_edit, src_col, src_row, num_copy_cols, num_copy_rows,
2536 dst_edit, dst_col, dst_row);
2537 }
2538 }
2539
vt_screen_get_checksum(vt_screen_t * screen,int col,int row,u_int num_cols,u_int num_rows,int page)2540 u_int16_t vt_screen_get_checksum(vt_screen_t *screen, int col, int row,
2541 u_int num_cols, u_int num_rows, int page) {
2542 vt_edit_t *edit;
2543
2544 if (page > MAX_PAGE_ID) {
2545 page = MAX_PAGE_ID;
2546 }
2547
2548 if ((edit = get_edit(screen, page))) {
2549 return vt_edit_get_checksum(edit, col, row, num_cols, num_rows);
2550 }
2551
2552 return 0;
2553 }
2554
vt_screen_enable_blinking(vt_screen_t * screen)2555 void vt_screen_enable_blinking(vt_screen_t *screen) { screen->has_blinking_char = 1; }
2556
vt_screen_write_content(vt_screen_t * screen,int fd,ef_conv_t * conv,int clear_at_end,vt_write_content_area_t area)2557 int vt_screen_write_content(vt_screen_t *screen, int fd, ef_conv_t *conv, int clear_at_end,
2558 vt_write_content_area_t area) {
2559 int beg;
2560 int end;
2561 vt_char_t *buf;
2562 u_int buf_len;
2563 u_int num;
2564 u_char conv_buf[512];
2565 ef_parser_t *vt_str_parser;
2566
2567 #if 1
2568 /* for sig_error in vt_term_manager.c */
2569 vt_screen_logical(screen);
2570 #endif
2571
2572 if (area == WCA_CURSOR_LINE) {
2573 beg = vt_screen_cursor_row(screen);
2574 end = beg + 1;
2575 } else {
2576 end = vt_screen_get_rows(screen);
2577 if (area == WCA_ALL) {
2578 beg = -vt_screen_get_num_logged_lines(screen);
2579 } else /* if (area == WCA_SCREEN) */ {
2580 beg = 0;
2581 }
2582 }
2583
2584 /* WCA_CURSOR_LINE may cause num == 0. */
2585 if ((buf_len = vt_screen_get_region_size(screen, 0, beg, 0, end, 0)) == 0) {
2586 return 0;
2587 }
2588
2589 /* buf_len could become huge size which gets alloca() to fail. */
2590 if ((buf = vt_str_new(buf_len)) == NULL) {
2591 return 0;
2592 }
2593
2594 vt_screen_copy_region(screen, buf, buf_len, 0, beg, 0, end, 0);
2595
2596 if (!(vt_str_parser = vt_str_parser_new())) {
2597 vt_str_destroy(buf, buf_len);
2598
2599 return 0;
2600 }
2601
2602 (*vt_str_parser->init)(vt_str_parser);
2603 vt_str_parser_set_str(vt_str_parser, buf, buf_len);
2604 (*conv->init)(conv);
2605
2606 while (!vt_str_parser->is_eos &&
2607 (num = (*conv->convert)(conv, conv_buf, sizeof(conv_buf), vt_str_parser)) > 0) {
2608 write(fd, conv_buf, num);
2609 }
2610
2611 if (clear_at_end) {
2612 write(fd, "\n\x1b[1000S\x1b[H", 11);
2613 }
2614
2615 (*vt_str_parser->destroy)(vt_str_parser);
2616 vt_str_destroy(buf, buf_len);
2617
2618 return 1;
2619 }
2620
vt_screen_get_page_id(vt_screen_t * screen)2621 int vt_screen_get_page_id(vt_screen_t *screen) {
2622 if (screen->edit == &screen->normal_edit ||
2623 screen->edit == &screen->alt_edit /* alt edit is regarded as 0 */) {
2624 return 0;
2625 } else if (screen->page_edits) {
2626 return screen->edit - screen->page_edits + 1;
2627 }
2628
2629 return -1;
2630 }
2631
vt_screen_goto_page(vt_screen_t * screen,u_int page_id)2632 int vt_screen_goto_page(vt_screen_t *screen, u_int page_id) {
2633 vt_edit_t *edit;
2634
2635 if (page_id > MAX_PAGE_ID) {
2636 page_id = MAX_PAGE_ID;
2637 }
2638
2639 if ((edit = get_edit(screen, page_id))) {
2640 vt_edit_goto(edit, screen->edit->cursor.col, screen->edit->cursor.row);
2641 if (vt_edit_get_tab_size(edit) != vt_edit_get_tab_size(screen->edit)) {
2642 vt_edit_set_tab_size(edit, vt_edit_get_tab_size(screen->edit));
2643 }
2644 edit->vmargin_beg = screen->edit->vmargin_beg;
2645 edit->vmargin_end = screen->edit->vmargin_end;
2646 edit->hmargin_beg = screen->edit->hmargin_beg;
2647 edit->hmargin_end = screen->edit->hmargin_end;
2648 edit->use_margin = screen->edit->use_margin;
2649 edit->is_logging = screen->edit->is_logging;
2650 edit->is_relative_origin = screen->edit->is_relative_origin;
2651 edit->is_auto_wrap = screen->edit->is_auto_wrap;
2652 edit->use_bce = screen->edit->use_bce;
2653 edit->use_rect_attr_select = screen->edit->use_rect_attr_select;
2654
2655 change_edit(screen, edit);
2656
2657 return 1;
2658 }
2659
2660 return 0;
2661 }
2662
vt_screen_goto_next_page(vt_screen_t * screen,u_int offset)2663 int vt_screen_goto_next_page(vt_screen_t *screen, u_int offset) {
2664 int page_id = vt_screen_get_page_id(screen);
2665
2666 if (page_id != -1) {
2667 return vt_screen_goto_page(screen, page_id + offset);
2668 }
2669
2670 return 0;
2671 }
2672
vt_screen_goto_prev_page(vt_screen_t * screen,u_int offset)2673 int vt_screen_goto_prev_page(vt_screen_t *screen, u_int offset) {
2674 int page_id = vt_screen_get_page_id(screen);
2675
2676 if (page_id != -1) {
2677 if (page_id < offset) {
2678 page_id = offset;
2679 }
2680
2681 return vt_screen_goto_page(screen, page_id - offset);
2682 }
2683
2684 return 0;
2685 }
2686
vt_screen_set_use_status_line(vt_screen_t * screen,int use)2687 void vt_screen_set_use_status_line(vt_screen_t *screen, int use) {
2688 if (use) {
2689 if (!vt_screen_has_status_line(screen)) {
2690 if ((!screen->status_edit && !(screen->status_edit = status_edit_new(screen->edit))) ||
2691 /* XXX status line is *not* supported on vertical mode */
2692 (screen->logvis && !screen->logvis->is_reversible)) {
2693 return;
2694 }
2695
2696 screen->main_edit = screen->edit;
2697 screen->has_status_line = 1;
2698 vt_edit_set_modified_all(screen->status_edit);
2699 /*
2700 * XXX
2701 * I don't know whether the cursor goes to home position or not,
2702 * but vttest 11.2.6.2.1 seems to assume it.
2703 * (https://vt100.net/docs/vt510-rm/DECSSDT.html doesn't describe it.)
2704 */
2705 #if 1
2706 vt_edit_goto(screen->status_edit, 0, 0);
2707 #endif
2708
2709 resize(screen, vt_edit_get_cols(screen->main_edit),
2710 vt_edit_get_rows(screen->main_edit), 0);
2711 }
2712 } else {
2713 if (vt_screen_has_status_line(screen)) {
2714 screen->has_status_line = 0;
2715
2716 resize(screen, vt_edit_get_cols(screen->main_edit),
2717 vt_edit_get_rows(screen->main_edit) + 1, 0);
2718 screen->main_edit = NULL;
2719 }
2720 }
2721 }
2722
vt_focus_status_line(vt_screen_t * screen)2723 void vt_focus_status_line(vt_screen_t *screen) {
2724 if (!screen->status_edit && !(screen->status_edit = status_edit_new(screen->edit))) {
2725 return;
2726 }
2727
2728 if (!vt_status_line_is_focused(screen)) {
2729 change_edit(screen, screen->status_edit);
2730 }
2731 }
2732
vt_focus_main_screen(vt_screen_t * screen)2733 void vt_focus_main_screen(vt_screen_t *screen) {
2734 if (screen->main_edit && vt_status_line_is_focused(screen)) {
2735 change_edit(screen, screen->main_edit);
2736 }
2737 }
2738