Mercurial > vim
comparison src/libvterm/src/termscreen.c @ 14734:2c72fa16aa70 v8.1.0379
patch 8.1.0379: build dependencies are incomplete
commit https://github.com/vim/vim/commit/78dcd4f002c41fff9c15434336f57210edc384f2
Author: Bram Moolenaar <Bram@vim.org>
Date: Thu Sep 13 17:23:28 2018 +0200
patch 8.1.0379: build dependencies are incomplete
Problem: Build dependencies are incomplete.
Solution: Update the build dependencies, mainly for xdiff. Adjust object
directory for libvterm and xdiff.
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 13 Sep 2018 17:30:05 +0200 |
parents | |
children | 544490b69e1d |
comparison
equal
deleted
inserted
replaced
14733:15d60eacdbc8 | 14734:2c72fa16aa70 |
---|---|
1 #include "vterm_internal.h" | |
2 | |
3 #include <stdio.h> | |
4 #include <string.h> | |
5 | |
6 #include "rect.h" | |
7 #include "utf8.h" | |
8 | |
9 #define UNICODE_SPACE 0x20 | |
10 #define UNICODE_LINEFEED 0x0a | |
11 | |
12 /* State of the pen at some moment in time, also used in a cell */ | |
13 typedef struct | |
14 { | |
15 /* After the bitfield */ | |
16 VTermColor fg, bg; | |
17 | |
18 unsigned int bold : 1; | |
19 unsigned int underline : 2; | |
20 unsigned int italic : 1; | |
21 unsigned int blink : 1; | |
22 unsigned int reverse : 1; | |
23 unsigned int strike : 1; | |
24 unsigned int font : 4; /* 0 to 9 */ | |
25 | |
26 /* Extra state storage that isn't strictly pen-related */ | |
27 unsigned int protected_cell : 1; | |
28 unsigned int dwl : 1; /* on a DECDWL or DECDHL line */ | |
29 unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */ | |
30 } ScreenPen; | |
31 | |
32 /* Internal representation of a screen cell */ | |
33 typedef struct | |
34 { | |
35 uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; | |
36 ScreenPen pen; | |
37 } ScreenCell; | |
38 | |
39 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell); | |
40 | |
41 struct VTermScreen | |
42 { | |
43 VTerm *vt; | |
44 VTermState *state; | |
45 | |
46 const VTermScreenCallbacks *callbacks; | |
47 void *cbdata; | |
48 | |
49 VTermDamageSize damage_merge; | |
50 /* start_row == -1 => no damage */ | |
51 VTermRect damaged; | |
52 VTermRect pending_scrollrect; | |
53 int pending_scroll_downward, pending_scroll_rightward; | |
54 | |
55 int rows; | |
56 int cols; | |
57 int global_reverse; | |
58 | |
59 /* Primary and Altscreen. buffers[1] is lazily allocated as needed */ | |
60 ScreenCell *buffers[2]; | |
61 | |
62 /* buffer will == buffers[0] or buffers[1], depending on altscreen */ | |
63 ScreenCell *buffer; | |
64 | |
65 /* buffer for a single screen row used in scrollback storage callbacks */ | |
66 VTermScreenCell *sb_buffer; | |
67 | |
68 ScreenPen pen; | |
69 }; | |
70 | |
71 static ScreenCell *getcell(const VTermScreen *screen, int row, int col) | |
72 { | |
73 if(row < 0 || row >= screen->rows) | |
74 return NULL; | |
75 if(col < 0 || col >= screen->cols) | |
76 return NULL; | |
77 return screen->buffer + (screen->cols * row) + col; | |
78 } | |
79 | |
80 static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) | |
81 { | |
82 ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); | |
83 int row, col; | |
84 | |
85 for(row = 0; row < new_rows; row++) { | |
86 for(col = 0; col < new_cols; col++) { | |
87 ScreenCell *new_cell = new_buffer + row*new_cols + col; | |
88 | |
89 if(buffer && row < screen->rows && col < screen->cols) | |
90 *new_cell = buffer[row * screen->cols + col]; | |
91 else { | |
92 new_cell->chars[0] = 0; | |
93 new_cell->pen = screen->pen; | |
94 } | |
95 } | |
96 } | |
97 | |
98 if(buffer) | |
99 vterm_allocator_free(screen->vt, buffer); | |
100 | |
101 return new_buffer; | |
102 } | |
103 | |
104 static void damagerect(VTermScreen *screen, VTermRect rect) | |
105 { | |
106 VTermRect emit; | |
107 | |
108 switch(screen->damage_merge) { | |
109 case VTERM_DAMAGE_CELL: | |
110 /* Always emit damage event */ | |
111 emit = rect; | |
112 break; | |
113 | |
114 case VTERM_DAMAGE_ROW: | |
115 /* Emit damage longer than one row. Try to merge with existing damage in | |
116 * the same row */ | |
117 if(rect.end_row > rect.start_row + 1) { | |
118 // Bigger than 1 line - flush existing, emit this | |
119 vterm_screen_flush_damage(screen); | |
120 emit = rect; | |
121 } | |
122 else if(screen->damaged.start_row == -1) { | |
123 // None stored yet | |
124 screen->damaged = rect; | |
125 return; | |
126 } | |
127 else if(rect.start_row == screen->damaged.start_row) { | |
128 // Merge with the stored line | |
129 if(screen->damaged.start_col > rect.start_col) | |
130 screen->damaged.start_col = rect.start_col; | |
131 if(screen->damaged.end_col < rect.end_col) | |
132 screen->damaged.end_col = rect.end_col; | |
133 return; | |
134 } | |
135 else { | |
136 // Emit the currently stored line, store a new one | |
137 emit = screen->damaged; | |
138 screen->damaged = rect; | |
139 } | |
140 break; | |
141 | |
142 case VTERM_DAMAGE_SCREEN: | |
143 case VTERM_DAMAGE_SCROLL: | |
144 /* Never emit damage event */ | |
145 if(screen->damaged.start_row == -1) | |
146 screen->damaged = rect; | |
147 else { | |
148 rect_expand(&screen->damaged, &rect); | |
149 } | |
150 return; | |
151 | |
152 default: | |
153 DEBUG_LOG1("TODO: Maybe merge damage for level %d\n", screen->damage_merge); | |
154 return; | |
155 } | |
156 | |
157 if(screen->callbacks && screen->callbacks->damage) | |
158 (*screen->callbacks->damage)(emit, screen->cbdata); | |
159 } | |
160 | |
161 static void damagescreen(VTermScreen *screen) | |
162 { | |
163 VTermRect rect = {0,0,0,0}; | |
164 rect.end_row = screen->rows; | |
165 rect.end_col = screen->cols; | |
166 | |
167 damagerect(screen, rect); | |
168 } | |
169 | |
170 static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) | |
171 { | |
172 int i; | |
173 int col; | |
174 VTermRect rect; | |
175 | |
176 VTermScreen *screen = user; | |
177 ScreenCell *cell = getcell(screen, pos.row, pos.col); | |
178 | |
179 if(!cell) | |
180 return 0; | |
181 | |
182 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { | |
183 cell->chars[i] = info->chars[i]; | |
184 cell->pen = screen->pen; | |
185 } | |
186 if(i < VTERM_MAX_CHARS_PER_CELL) | |
187 cell->chars[i] = 0; | |
188 | |
189 for(col = 1; col < info->width; col++) | |
190 getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; | |
191 | |
192 rect.start_row = pos.row; | |
193 rect.end_row = pos.row+1; | |
194 rect.start_col = pos.col; | |
195 rect.end_col = pos.col+info->width; | |
196 | |
197 cell->pen.protected_cell = info->protected_cell; | |
198 cell->pen.dwl = info->dwl; | |
199 cell->pen.dhl = info->dhl; | |
200 | |
201 damagerect(screen, rect); | |
202 | |
203 return 1; | |
204 } | |
205 | |
206 static int moverect_internal(VTermRect dest, VTermRect src, void *user) | |
207 { | |
208 VTermScreen *screen = user; | |
209 | |
210 if(screen->callbacks && screen->callbacks->sb_pushline && | |
211 dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner | |
212 dest.end_col == screen->cols && // full width | |
213 screen->buffer == screen->buffers[0]) { // not altscreen | |
214 VTermPos pos; | |
215 for(pos.row = 0; pos.row < src.start_row; pos.row++) { | |
216 for(pos.col = 0; pos.col < screen->cols; pos.col++) | |
217 (void)vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col); | |
218 | |
219 (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); | |
220 } | |
221 } | |
222 | |
223 { | |
224 int cols = src.end_col - src.start_col; | |
225 int downward = src.start_row - dest.start_row; | |
226 int init_row, test_row, inc_row; | |
227 int row; | |
228 | |
229 if(downward < 0) { | |
230 init_row = dest.end_row - 1; | |
231 test_row = dest.start_row - 1; | |
232 inc_row = -1; | |
233 } | |
234 else { | |
235 init_row = dest.start_row; | |
236 test_row = dest.end_row; | |
237 inc_row = +1; | |
238 } | |
239 | |
240 for(row = init_row; row != test_row; row += inc_row) | |
241 memmove(getcell(screen, row, dest.start_col), | |
242 getcell(screen, row + downward, src.start_col), | |
243 cols * sizeof(ScreenCell)); | |
244 } | |
245 | |
246 return 1; | |
247 } | |
248 | |
249 static int moverect_user(VTermRect dest, VTermRect src, void *user) | |
250 { | |
251 VTermScreen *screen = user; | |
252 | |
253 if(screen->callbacks && screen->callbacks->moverect) { | |
254 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) | |
255 // Avoid an infinite loop | |
256 vterm_screen_flush_damage(screen); | |
257 | |
258 if((*screen->callbacks->moverect)(dest, src, screen->cbdata)) | |
259 return 1; | |
260 } | |
261 | |
262 damagerect(screen, dest); | |
263 | |
264 return 1; | |
265 } | |
266 | |
267 static int erase_internal(VTermRect rect, int selective, void *user) | |
268 { | |
269 VTermScreen *screen = user; | |
270 int row, col; | |
271 | |
272 for(row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) { | |
273 const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row); | |
274 | |
275 for(col = rect.start_col; col < rect.end_col; col++) { | |
276 ScreenCell *cell = getcell(screen, row, col); | |
277 | |
278 if(selective && cell->pen.protected_cell) | |
279 continue; | |
280 | |
281 cell->chars[0] = 0; | |
282 cell->pen = screen->pen; | |
283 cell->pen.dwl = info->doublewidth; | |
284 cell->pen.dhl = info->doubleheight; | |
285 } | |
286 } | |
287 | |
288 return 1; | |
289 } | |
290 | |
291 static int erase_user(VTermRect rect, int selective UNUSED, void *user) | |
292 { | |
293 VTermScreen *screen = user; | |
294 | |
295 damagerect(screen, rect); | |
296 | |
297 return 1; | |
298 } | |
299 | |
300 static int erase(VTermRect rect, int selective, void *user) | |
301 { | |
302 erase_internal(rect, selective, user); | |
303 return erase_user(rect, 0, user); | |
304 } | |
305 | |
306 static int scrollrect(VTermRect rect, int downward, int rightward, void *user) | |
307 { | |
308 VTermScreen *screen = user; | |
309 | |
310 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) { | |
311 vterm_scroll_rect(rect, downward, rightward, | |
312 moverect_internal, erase_internal, screen); | |
313 | |
314 vterm_screen_flush_damage(screen); | |
315 | |
316 vterm_scroll_rect(rect, downward, rightward, | |
317 moverect_user, erase_user, screen); | |
318 | |
319 return 1; | |
320 } | |
321 | |
322 if(screen->damaged.start_row != -1 && | |
323 !rect_intersects(&rect, &screen->damaged)) { | |
324 vterm_screen_flush_damage(screen); | |
325 } | |
326 | |
327 if(screen->pending_scrollrect.start_row == -1) { | |
328 screen->pending_scrollrect = rect; | |
329 screen->pending_scroll_downward = downward; | |
330 screen->pending_scroll_rightward = rightward; | |
331 } | |
332 else if(rect_equal(&screen->pending_scrollrect, &rect) && | |
333 ((screen->pending_scroll_downward == 0 && downward == 0) || | |
334 (screen->pending_scroll_rightward == 0 && rightward == 0))) { | |
335 screen->pending_scroll_downward += downward; | |
336 screen->pending_scroll_rightward += rightward; | |
337 } | |
338 else { | |
339 vterm_screen_flush_damage(screen); | |
340 | |
341 screen->pending_scrollrect = rect; | |
342 screen->pending_scroll_downward = downward; | |
343 screen->pending_scroll_rightward = rightward; | |
344 } | |
345 | |
346 vterm_scroll_rect(rect, downward, rightward, | |
347 moverect_internal, erase_internal, screen); | |
348 | |
349 if(screen->damaged.start_row == -1) | |
350 return 1; | |
351 | |
352 if(rect_contains(&rect, &screen->damaged)) { | |
353 /* Scroll region entirely contains the damage; just move it */ | |
354 vterm_rect_move(&screen->damaged, -downward, -rightward); | |
355 rect_clip(&screen->damaged, &rect); | |
356 } | |
357 /* There are a number of possible cases here, but lets restrict this to only | |
358 * the common case where we might actually gain some performance by | |
359 * optimising it. Namely, a vertical scroll that neatly cuts the damage | |
360 * region in half. | |
361 */ | |
362 else if(rect.start_col <= screen->damaged.start_col && | |
363 rect.end_col >= screen->damaged.end_col && | |
364 rightward == 0) { | |
365 if(screen->damaged.start_row >= rect.start_row && | |
366 screen->damaged.start_row < rect.end_row) { | |
367 screen->damaged.start_row -= downward; | |
368 if(screen->damaged.start_row < rect.start_row) | |
369 screen->damaged.start_row = rect.start_row; | |
370 if(screen->damaged.start_row > rect.end_row) | |
371 screen->damaged.start_row = rect.end_row; | |
372 } | |
373 if(screen->damaged.end_row >= rect.start_row && | |
374 screen->damaged.end_row < rect.end_row) { | |
375 screen->damaged.end_row -= downward; | |
376 if(screen->damaged.end_row < rect.start_row) | |
377 screen->damaged.end_row = rect.start_row; | |
378 if(screen->damaged.end_row > rect.end_row) | |
379 screen->damaged.end_row = rect.end_row; | |
380 } | |
381 } | |
382 else { | |
383 DEBUG_LOG2("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n", | |
384 ARGSrect(screen->damaged), ARGSrect(rect)); | |
385 } | |
386 | |
387 return 1; | |
388 } | |
389 | |
390 static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) | |
391 { | |
392 VTermScreen *screen = user; | |
393 | |
394 if(screen->callbacks && screen->callbacks->movecursor) | |
395 return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata); | |
396 | |
397 return 0; | |
398 } | |
399 | |
400 static int setpenattr(VTermAttr attr, VTermValue *val, void *user) | |
401 { | |
402 VTermScreen *screen = user; | |
403 | |
404 switch(attr) { | |
405 case VTERM_ATTR_BOLD: | |
406 screen->pen.bold = val->boolean; | |
407 return 1; | |
408 case VTERM_ATTR_UNDERLINE: | |
409 screen->pen.underline = val->number; | |
410 return 1; | |
411 case VTERM_ATTR_ITALIC: | |
412 screen->pen.italic = val->boolean; | |
413 return 1; | |
414 case VTERM_ATTR_BLINK: | |
415 screen->pen.blink = val->boolean; | |
416 return 1; | |
417 case VTERM_ATTR_REVERSE: | |
418 screen->pen.reverse = val->boolean; | |
419 return 1; | |
420 case VTERM_ATTR_STRIKE: | |
421 screen->pen.strike = val->boolean; | |
422 return 1; | |
423 case VTERM_ATTR_FONT: | |
424 screen->pen.font = val->number; | |
425 return 1; | |
426 case VTERM_ATTR_FOREGROUND: | |
427 screen->pen.fg = val->color; | |
428 return 1; | |
429 case VTERM_ATTR_BACKGROUND: | |
430 screen->pen.bg = val->color; | |
431 return 1; | |
432 | |
433 case VTERM_N_ATTRS: | |
434 return 0; | |
435 } | |
436 | |
437 return 0; | |
438 } | |
439 | |
440 static int settermprop(VTermProp prop, VTermValue *val, void *user) | |
441 { | |
442 VTermScreen *screen = user; | |
443 | |
444 switch(prop) { | |
445 case VTERM_PROP_ALTSCREEN: | |
446 if(val->boolean && !screen->buffers[1]) | |
447 return 0; | |
448 | |
449 screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0]; | |
450 /* only send a damage event on disable; because during enable there's an | |
451 * erase that sends a damage anyway | |
452 */ | |
453 if(!val->boolean) | |
454 damagescreen(screen); | |
455 break; | |
456 case VTERM_PROP_REVERSE: | |
457 screen->global_reverse = val->boolean; | |
458 damagescreen(screen); | |
459 break; | |
460 default: | |
461 ; /* ignore */ | |
462 } | |
463 | |
464 if(screen->callbacks && screen->callbacks->settermprop) | |
465 return (*screen->callbacks->settermprop)(prop, val, screen->cbdata); | |
466 | |
467 return 1; | |
468 } | |
469 | |
470 static int bell(void *user) | |
471 { | |
472 VTermScreen *screen = user; | |
473 | |
474 if(screen->callbacks && screen->callbacks->bell) | |
475 return (*screen->callbacks->bell)(screen->cbdata); | |
476 | |
477 return 0; | |
478 } | |
479 | |
480 static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) | |
481 { | |
482 VTermScreen *screen = user; | |
483 | |
484 int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]); | |
485 | |
486 int old_rows = screen->rows; | |
487 int old_cols = screen->cols; | |
488 int first_blank_row; | |
489 | |
490 if(!is_altscreen && new_rows < old_rows) { | |
491 // Fewer rows - determine if we're going to scroll at all, and if so, push | |
492 // those lines to scrollback | |
493 VTermPos pos = { 0, 0 }; | |
494 VTermPos cursor = screen->state->pos; | |
495 // Find the first blank row after the cursor. | |
496 for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--) | |
497 if(!vterm_screen_is_eol(screen, pos) || cursor.row == pos.row) | |
498 break; | |
499 | |
500 first_blank_row = pos.row + 1; | |
501 if(first_blank_row > new_rows) { | |
502 VTermRect rect = {0,0,0,0}; | |
503 rect.end_row = old_rows; | |
504 rect.end_col = old_cols; | |
505 scrollrect(rect, first_blank_row - new_rows, 0, user); | |
506 vterm_screen_flush_damage(screen); | |
507 | |
508 delta->row -= first_blank_row - new_rows; | |
509 } | |
510 } | |
511 | |
512 screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols); | |
513 if(screen->buffers[1]) | |
514 screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols); | |
515 | |
516 screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0]; | |
517 | |
518 screen->rows = new_rows; | |
519 screen->cols = new_cols; | |
520 | |
521 if(screen->sb_buffer) | |
522 vterm_allocator_free(screen->vt, screen->sb_buffer); | |
523 | |
524 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); | |
525 | |
526 if(new_cols > old_cols) { | |
527 VTermRect rect; | |
528 rect.start_row = 0; | |
529 rect.end_row = old_rows; | |
530 rect.start_col = old_cols; | |
531 rect.end_col = new_cols; | |
532 damagerect(screen, rect); | |
533 } | |
534 | |
535 if(new_rows > old_rows) { | |
536 if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) { | |
537 int rows = new_rows - old_rows; | |
538 while(rows) { | |
539 VTermRect rect = {0,0,0,0}; | |
540 VTermPos pos = { 0, 0 }; | |
541 if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata))) | |
542 break; | |
543 | |
544 rect.end_row = screen->rows; | |
545 rect.end_col = screen->cols; | |
546 scrollrect(rect, -1, 0, user); | |
547 | |
548 for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width) | |
549 vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col); | |
550 | |
551 rect.end_row = 1; | |
552 damagerect(screen, rect); | |
553 | |
554 vterm_screen_flush_damage(screen); | |
555 | |
556 rows--; | |
557 delta->row++; | |
558 } | |
559 } | |
560 | |
561 { | |
562 VTermRect rect; | |
563 rect.start_row = old_rows; | |
564 rect.end_row = new_rows; | |
565 rect.start_col = 0; | |
566 rect.end_col = new_cols; | |
567 damagerect(screen, rect); | |
568 } | |
569 } | |
570 | |
571 if(screen->callbacks && screen->callbacks->resize) | |
572 return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata); | |
573 | |
574 return 1; | |
575 } | |
576 | |
577 static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) | |
578 { | |
579 VTermScreen *screen = user; | |
580 int col; | |
581 VTermRect rect; | |
582 | |
583 if(newinfo->doublewidth != oldinfo->doublewidth || | |
584 newinfo->doubleheight != oldinfo->doubleheight) { | |
585 for(col = 0; col < screen->cols; col++) { | |
586 ScreenCell *cell = getcell(screen, row, col); | |
587 cell->pen.dwl = newinfo->doublewidth; | |
588 cell->pen.dhl = newinfo->doubleheight; | |
589 } | |
590 | |
591 rect.start_row = row; | |
592 rect.end_row = row + 1; | |
593 rect.start_col = 0; | |
594 rect.end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols; | |
595 damagerect(screen, rect); | |
596 | |
597 if(newinfo->doublewidth) { | |
598 rect.start_col = screen->cols / 2; | |
599 rect.end_col = screen->cols; | |
600 | |
601 erase_internal(rect, 0, user); | |
602 } | |
603 } | |
604 | |
605 return 1; | |
606 } | |
607 | |
608 static VTermStateCallbacks state_cbs = { | |
609 &putglyph, /* putglyph */ | |
610 &movecursor, /* movecursor */ | |
611 &scrollrect, /* scrollrect */ | |
612 NULL, /* moverect */ | |
613 &erase, /* erase */ | |
614 NULL, /* initpen */ | |
615 &setpenattr, /* setpenattr */ | |
616 &settermprop, /* settermprop */ | |
617 &bell, /* bell */ | |
618 &resize, /* resize */ | |
619 &setlineinfo /* setlineinfo */ | |
620 }; | |
621 | |
622 static VTermScreen *screen_new(VTerm *vt) | |
623 { | |
624 VTermState *state = vterm_obtain_state(vt); | |
625 VTermScreen *screen; | |
626 int rows, cols; | |
627 | |
628 if(!state) | |
629 return NULL; | |
630 | |
631 screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); | |
632 | |
633 vterm_get_size(vt, &rows, &cols); | |
634 | |
635 screen->vt = vt; | |
636 screen->state = state; | |
637 | |
638 screen->damage_merge = VTERM_DAMAGE_CELL; | |
639 screen->damaged.start_row = -1; | |
640 screen->pending_scrollrect.start_row = -1; | |
641 | |
642 screen->rows = rows; | |
643 screen->cols = cols; | |
644 | |
645 screen->callbacks = NULL; | |
646 screen->cbdata = NULL; | |
647 | |
648 screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols); | |
649 | |
650 screen->buffer = screen->buffers[0]; | |
651 | |
652 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols); | |
653 | |
654 vterm_state_set_callbacks(screen->state, &state_cbs, screen); | |
655 | |
656 return screen; | |
657 } | |
658 | |
659 INTERNAL void vterm_screen_free(VTermScreen *screen) | |
660 { | |
661 vterm_allocator_free(screen->vt, screen->buffers[0]); | |
662 if(screen->buffers[1]) | |
663 vterm_allocator_free(screen->vt, screen->buffers[1]); | |
664 | |
665 vterm_allocator_free(screen->vt, screen->sb_buffer); | |
666 | |
667 vterm_allocator_free(screen->vt, screen); | |
668 } | |
669 | |
670 void vterm_screen_reset(VTermScreen *screen, int hard) | |
671 { | |
672 screen->damaged.start_row = -1; | |
673 screen->pending_scrollrect.start_row = -1; | |
674 vterm_state_reset(screen->state, hard); | |
675 vterm_screen_flush_damage(screen); | |
676 } | |
677 | |
678 static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) | |
679 { | |
680 size_t outpos = 0; | |
681 int padding = 0; | |
682 int row, col; | |
683 | |
684 #define PUT(c) \ | |
685 if(utf8) { \ | |
686 size_t thislen = utf8_seqlen(c); \ | |
687 if(buffer && outpos + thislen <= len) \ | |
688 outpos += fill_utf8((c), (char *)buffer + outpos); \ | |
689 else \ | |
690 outpos += thislen; \ | |
691 } \ | |
692 else { \ | |
693 if(buffer && outpos + 1 <= len) \ | |
694 ((uint32_t*)buffer)[outpos++] = (c); \ | |
695 else \ | |
696 outpos++; \ | |
697 } | |
698 | |
699 for(row = rect.start_row; row < rect.end_row; row++) { | |
700 for(col = rect.start_col; col < rect.end_col; col++) { | |
701 ScreenCell *cell = getcell(screen, row, col); | |
702 int i; | |
703 | |
704 if(cell->chars[0] == 0) | |
705 // Erased cell, might need a space | |
706 padding++; | |
707 else if(cell->chars[0] == (uint32_t)-1) | |
708 // Gap behind a double-width char, do nothing | |
709 ; | |
710 else { | |
711 while(padding) { | |
712 PUT(UNICODE_SPACE); | |
713 padding--; | |
714 } | |
715 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { | |
716 PUT(cell->chars[i]); | |
717 } | |
718 } | |
719 } | |
720 | |
721 if(row < rect.end_row - 1) { | |
722 PUT(UNICODE_LINEFEED); | |
723 padding = 0; | |
724 } | |
725 } | |
726 | |
727 return outpos; | |
728 } | |
729 | |
730 size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) | |
731 { | |
732 return _get_chars(screen, 0, chars, len, rect); | |
733 } | |
734 | |
735 size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect) | |
736 { | |
737 return _get_chars(screen, 1, str, len, rect); | |
738 } | |
739 | |
740 /* Copy internal to external representation of a screen cell */ | |
741 int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) | |
742 { | |
743 ScreenCell *intcell = getcell(screen, pos.row, pos.col); | |
744 int i; | |
745 | |
746 if(!intcell) | |
747 return 0; | |
748 | |
749 for(i = 0; ; i++) { | |
750 cell->chars[i] = intcell->chars[i]; | |
751 if(!intcell->chars[i]) | |
752 break; | |
753 } | |
754 | |
755 cell->attrs.bold = intcell->pen.bold; | |
756 cell->attrs.underline = intcell->pen.underline; | |
757 cell->attrs.italic = intcell->pen.italic; | |
758 cell->attrs.blink = intcell->pen.blink; | |
759 cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse; | |
760 cell->attrs.strike = intcell->pen.strike; | |
761 cell->attrs.font = intcell->pen.font; | |
762 | |
763 cell->attrs.dwl = intcell->pen.dwl; | |
764 cell->attrs.dhl = intcell->pen.dhl; | |
765 | |
766 cell->fg = intcell->pen.fg; | |
767 cell->bg = intcell->pen.bg; | |
768 | |
769 if(pos.col < (screen->cols - 1) && | |
770 getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1) | |
771 cell->width = 2; | |
772 else | |
773 cell->width = 1; | |
774 | |
775 return 1; | |
776 } | |
777 | |
778 /* Copy external to internal representation of a screen cell */ | |
779 /* static because it's only used internally for sb_popline during resize */ | |
780 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell) | |
781 { | |
782 ScreenCell *intcell = getcell(screen, pos.row, pos.col); | |
783 int i; | |
784 | |
785 if(!intcell) | |
786 return 0; | |
787 | |
788 for(i = 0; ; i++) { | |
789 intcell->chars[i] = cell->chars[i]; | |
790 if(!cell->chars[i]) | |
791 break; | |
792 } | |
793 | |
794 intcell->pen.bold = cell->attrs.bold; | |
795 intcell->pen.underline = cell->attrs.underline; | |
796 intcell->pen.italic = cell->attrs.italic; | |
797 intcell->pen.blink = cell->attrs.blink; | |
798 intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse; | |
799 intcell->pen.strike = cell->attrs.strike; | |
800 intcell->pen.font = cell->attrs.font; | |
801 | |
802 intcell->pen.fg = cell->fg; | |
803 intcell->pen.bg = cell->bg; | |
804 | |
805 if(cell->width == 2) | |
806 getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1; | |
807 | |
808 return 1; | |
809 } | |
810 | |
811 int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) | |
812 { | |
813 /* This cell is EOL if this and every cell to the right is black */ | |
814 for(; pos.col < screen->cols; pos.col++) { | |
815 ScreenCell *cell = getcell(screen, pos.row, pos.col); | |
816 if(cell->chars[0] != 0) | |
817 return 0; | |
818 } | |
819 | |
820 return 1; | |
821 } | |
822 | |
823 VTermScreen *vterm_obtain_screen(VTerm *vt) | |
824 { | |
825 if(!vt->screen) | |
826 vt->screen = screen_new(vt); | |
827 return vt->screen; | |
828 } | |
829 | |
830 void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) | |
831 { | |
832 | |
833 if(!screen->buffers[1] && altscreen) { | |
834 int rows, cols; | |
835 vterm_get_size(screen->vt, &rows, &cols); | |
836 | |
837 screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols); | |
838 } | |
839 } | |
840 | |
841 void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user) | |
842 { | |
843 screen->callbacks = callbacks; | |
844 screen->cbdata = user; | |
845 } | |
846 | |
847 void *vterm_screen_get_cbdata(VTermScreen *screen) | |
848 { | |
849 return screen->cbdata; | |
850 } | |
851 | |
852 void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermParserCallbacks *fallbacks, void *user) | |
853 { | |
854 vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user); | |
855 } | |
856 | |
857 void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen) | |
858 { | |
859 return vterm_state_get_unrecognised_fbdata(screen->state); | |
860 } | |
861 | |
862 void vterm_screen_flush_damage(VTermScreen *screen) | |
863 { | |
864 if(screen->pending_scrollrect.start_row != -1) { | |
865 vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward, | |
866 moverect_user, erase_user, screen); | |
867 | |
868 screen->pending_scrollrect.start_row = -1; | |
869 } | |
870 | |
871 if(screen->damaged.start_row != -1) { | |
872 if(screen->callbacks && screen->callbacks->damage) | |
873 (*screen->callbacks->damage)(screen->damaged, screen->cbdata); | |
874 | |
875 screen->damaged.start_row = -1; | |
876 } | |
877 } | |
878 | |
879 void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size) | |
880 { | |
881 vterm_screen_flush_damage(screen); | |
882 screen->damage_merge = size; | |
883 } | |
884 | |
885 static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) | |
886 { | |
887 if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) | |
888 return 1; | |
889 if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) | |
890 return 1; | |
891 if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) | |
892 return 1; | |
893 if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) | |
894 return 1; | |
895 if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) | |
896 return 1; | |
897 if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) | |
898 return 1; | |
899 if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) | |
900 return 1; | |
901 if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg)) | |
902 return 1; | |
903 if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg)) | |
904 return 1; | |
905 | |
906 return 0; | |
907 } | |
908 | |
909 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs) | |
910 { | |
911 int col; | |
912 | |
913 ScreenCell *target = getcell(screen, pos.row, pos.col); | |
914 | |
915 // TODO: bounds check | |
916 extent->start_row = pos.row; | |
917 extent->end_row = pos.row + 1; | |
918 | |
919 if(extent->start_col < 0) | |
920 extent->start_col = 0; | |
921 if(extent->end_col < 0) | |
922 extent->end_col = screen->cols; | |
923 | |
924 for(col = pos.col - 1; col >= extent->start_col; col--) | |
925 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) | |
926 break; | |
927 extent->start_col = col + 1; | |
928 | |
929 for(col = pos.col + 1; col < extent->end_col; col++) | |
930 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) | |
931 break; | |
932 extent->end_col = col - 1; | |
933 | |
934 return 1; | |
935 } |