Line | Count | Source |
1 | | /* $OpenBSD: screen.c,v 1.108 2026/10/12 10:50:06 nicm Exp $ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <sys/types.h> |
20 | | |
21 | | #include <stdlib.h> |
22 | | #include <string.h> |
23 | | #include <unistd.h> |
24 | | |
25 | | #include "tmux.h" |
26 | | |
27 | | /* Selected area in screen. */ |
28 | | struct screen_sel { |
29 | | int hidden; |
30 | | int rectangle; |
31 | | int modekeys; |
32 | | |
33 | | u_int sx; |
34 | | u_int sy; |
35 | | |
36 | | u_int ex; |
37 | | u_int ey; |
38 | | |
39 | | u_int clipx; |
40 | | |
41 | | struct grid_cell cell; |
42 | | }; |
43 | | |
44 | | /* Entry on title stack. */ |
45 | | struct screen_title_entry { |
46 | | char *text; |
47 | | |
48 | | TAILQ_ENTRY(screen_title_entry) entry; |
49 | | }; |
50 | | TAILQ_HEAD(screen_titles, screen_title_entry); |
51 | | |
52 | | static void screen_resize_y(struct screen *, u_int, int, u_int *); |
53 | | static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int); |
54 | | |
55 | | /* Free titles stack. */ |
56 | | static void |
57 | | screen_free_titles(struct screen *s) |
58 | 0 | { |
59 | 0 | struct screen_title_entry *title_entry; |
60 | |
|
61 | 0 | if (s->titles == NULL) |
62 | 0 | return; |
63 | | |
64 | 0 | while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) { |
65 | 0 | TAILQ_REMOVE(s->titles, title_entry, entry); |
66 | 0 | free(title_entry->text); |
67 | 0 | free(title_entry); |
68 | 0 | } |
69 | |
|
70 | 0 | free(s->titles); |
71 | 0 | s->titles = NULL; |
72 | 0 | s->ntitles = 0; |
73 | 0 | } |
74 | | |
75 | | /* Create a new screen. */ |
76 | | void |
77 | | screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) |
78 | 0 | { |
79 | 0 | s->grid = grid_create(sx, sy, hlimit); |
80 | 0 | s->saved_grid = NULL; |
81 | |
|
82 | 0 | s->title = xstrdup(""); |
83 | 0 | s->titles = NULL; |
84 | 0 | s->ntitles = 0; |
85 | 0 | s->path = NULL; |
86 | |
|
87 | 0 | s->cstyle = SCREEN_CURSOR_DEFAULT; |
88 | 0 | s->default_cstyle = SCREEN_CURSOR_DEFAULT; |
89 | 0 | s->mode = MODE_CURSOR; |
90 | 0 | s->default_mode = 0; |
91 | 0 | s->ccolour = -1; |
92 | 0 | s->default_ccolour = -1; |
93 | 0 | s->tabs = NULL; |
94 | 0 | s->sel = NULL; |
95 | |
|
96 | 0 | s->write_list = NULL; |
97 | 0 | s->hyperlinks = NULL; |
98 | |
|
99 | 0 | screen_reinit(s); |
100 | 0 | } |
101 | | |
102 | | /* Reinitialise screen. */ |
103 | | void |
104 | | screen_reinit(struct screen *s) |
105 | 0 | { |
106 | 0 | s->cx = 0; |
107 | 0 | s->cy = 0; |
108 | |
|
109 | 0 | s->rupper = 0; |
110 | 0 | s->rlower = screen_size_y(s) - 1; |
111 | |
|
112 | 0 | s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF); |
113 | |
|
114 | 0 | if (options_get_number(global_options, "extended-keys") == 2) |
115 | 0 | s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED; |
116 | |
|
117 | 0 | if (SCREEN_IS_ALTERNATE(s)) |
118 | 0 | screen_alternate_off(s, NULL, 0); |
119 | 0 | s->saved_cx = UINT_MAX; |
120 | 0 | s->saved_cy = UINT_MAX; |
121 | |
|
122 | 0 | screen_reset_tabs(s); |
123 | 0 | grid_free_lines(s->grid, s->grid->hsize, s->grid->sy); |
124 | 0 | grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); |
125 | |
|
126 | 0 | screen_clear_selection(s); |
127 | 0 | screen_free_titles(s); |
128 | 0 | screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0); |
129 | 0 | screen_reset_hyperlinks(s); |
130 | 0 | } |
131 | | |
132 | | /* Reset hyperlinks of a screen. */ |
133 | | void |
134 | | screen_reset_hyperlinks(struct screen *s) |
135 | 0 | { |
136 | 0 | if (s->hyperlinks == NULL) |
137 | 0 | s->hyperlinks = hyperlinks_init(); |
138 | 0 | else |
139 | 0 | hyperlinks_reset(s->hyperlinks); |
140 | 0 | } |
141 | | |
142 | | /* Destroy a screen. */ |
143 | | void |
144 | | screen_free(struct screen *s) |
145 | 0 | { |
146 | 0 | free(s->sel); |
147 | 0 | free(s->tabs); |
148 | 0 | free(s->path); |
149 | 0 | free(s->title); |
150 | |
|
151 | 0 | if (s->write_list != NULL) |
152 | 0 | screen_write_free_list(s); |
153 | |
|
154 | 0 | if (SCREEN_IS_ALTERNATE(s)) |
155 | 0 | grid_destroy(s->saved_grid); |
156 | 0 | grid_destroy(s->grid); |
157 | |
|
158 | 0 | if (s->hyperlinks != NULL) |
159 | 0 | hyperlinks_free(s->hyperlinks); |
160 | 0 | screen_free_titles(s); |
161 | |
|
162 | 0 | } |
163 | | |
164 | | /* Reset tabs to default, eight spaces apart. */ |
165 | | void |
166 | | screen_reset_tabs(struct screen *s) |
167 | 0 | { |
168 | 0 | u_int i; |
169 | |
|
170 | 0 | free(s->tabs); |
171 | |
|
172 | 0 | if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) |
173 | 0 | fatal("bit_alloc failed"); |
174 | 0 | for (i = 8; i < screen_size_x(s); i += 8) |
175 | 0 | bit_set(s->tabs, i); |
176 | 0 | } |
177 | | |
178 | | /* Set default cursor style and colour from options. */ |
179 | | void |
180 | | screen_set_default_cursor(struct screen *s, struct options *oo) |
181 | 0 | { |
182 | 0 | struct grid_cell gc; |
183 | 0 | int c; |
184 | |
|
185 | 0 | style_apply(&gc, oo, "cursor-colour", NULL); |
186 | 0 | s->default_ccolour = gc.fg; |
187 | |
|
188 | 0 | c = options_get_number(oo, "cursor-style"); |
189 | 0 | s->default_mode = 0; |
190 | 0 | screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode); |
191 | 0 | } |
192 | | |
193 | | /* Set screen cursor style and mode. */ |
194 | | void |
195 | | screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle, |
196 | | int *mode) |
197 | 0 | { |
198 | 0 | switch (style) { |
199 | 0 | case 0: |
200 | 0 | *cstyle = SCREEN_CURSOR_DEFAULT; |
201 | 0 | break; |
202 | 0 | case 1: |
203 | 0 | *cstyle = SCREEN_CURSOR_BLOCK; |
204 | 0 | *mode |= MODE_CURSOR_BLINKING; |
205 | 0 | break; |
206 | 0 | case 2: |
207 | 0 | *cstyle = SCREEN_CURSOR_BLOCK; |
208 | 0 | *mode &= ~MODE_CURSOR_BLINKING; |
209 | 0 | break; |
210 | 0 | case 3: |
211 | 0 | *cstyle = SCREEN_CURSOR_UNDERLINE; |
212 | 0 | *mode |= MODE_CURSOR_BLINKING; |
213 | 0 | break; |
214 | 0 | case 4: |
215 | 0 | *cstyle = SCREEN_CURSOR_UNDERLINE; |
216 | 0 | *mode &= ~MODE_CURSOR_BLINKING; |
217 | 0 | break; |
218 | 0 | case 5: |
219 | 0 | *cstyle = SCREEN_CURSOR_BAR; |
220 | 0 | *mode |= MODE_CURSOR_BLINKING; |
221 | 0 | break; |
222 | 0 | case 6: |
223 | 0 | *cstyle = SCREEN_CURSOR_BAR; |
224 | 0 | *mode &= ~MODE_CURSOR_BLINKING; |
225 | 0 | break; |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | /* Set screen cursor colour. */ |
230 | | void |
231 | | screen_set_cursor_colour(struct screen *s, int colour) |
232 | 0 | { |
233 | 0 | s->ccolour = colour; |
234 | 0 | } |
235 | | |
236 | | /* Set screen title. */ |
237 | | int |
238 | | screen_set_title(struct screen *s, const char *title, int untrusted) |
239 | 0 | { |
240 | 0 | char *new_title; |
241 | |
|
242 | 0 | new_title = clean_name(title, untrusted); |
243 | 0 | if (new_title == NULL) |
244 | 0 | return (0); |
245 | 0 | free(s->title); |
246 | 0 | s->title = new_title; |
247 | 0 | return (1); |
248 | 0 | } |
249 | | |
250 | | /* Set screen path. */ |
251 | | int |
252 | | screen_set_path(struct screen *s, const char *path, int untrusted) |
253 | 0 | { |
254 | 0 | char *new_path; |
255 | |
|
256 | 0 | new_path = clean_name(path, untrusted); |
257 | 0 | if (new_path == NULL) |
258 | 0 | return (0); |
259 | 0 | free(s->path); |
260 | 0 | s->path = new_path; |
261 | 0 | return (1); |
262 | 0 | } |
263 | | |
264 | | /* Push the current title onto the stack. */ |
265 | | void |
266 | | screen_push_title(struct screen *s) |
267 | 0 | { |
268 | 0 | struct screen_title_entry *title_entry; |
269 | |
|
270 | 0 | log_debug("%s: %u", __func__, s->ntitles); |
271 | |
|
272 | 0 | while (s->ntitles >= 10) { |
273 | 0 | title_entry = TAILQ_LAST(s->titles, screen_titles); |
274 | 0 | free(title_entry->text); |
275 | 0 | TAILQ_REMOVE(s->titles, title_entry, entry); |
276 | 0 | free(title_entry); |
277 | 0 | s->ntitles--; |
278 | 0 | } |
279 | |
|
280 | 0 | if (s->titles == NULL) { |
281 | 0 | s->titles = xmalloc(sizeof *s->titles); |
282 | 0 | TAILQ_INIT(s->titles); |
283 | 0 | } |
284 | 0 | title_entry = xmalloc(sizeof *title_entry); |
285 | 0 | title_entry->text = xstrdup(s->title); |
286 | 0 | TAILQ_INSERT_HEAD(s->titles, title_entry, entry); |
287 | 0 | s->ntitles++; |
288 | 0 | } |
289 | | |
290 | | /* |
291 | | * Pop a title from the stack and set it as the screen title. If the stack is |
292 | | * empty, do nothing. |
293 | | */ |
294 | | void |
295 | | screen_pop_title(struct screen *s) |
296 | 0 | { |
297 | 0 | struct screen_title_entry *title_entry; |
298 | |
|
299 | 0 | if (s->titles == NULL) |
300 | 0 | return; |
301 | 0 | log_debug("%s: %u", __func__, s->ntitles); |
302 | |
|
303 | 0 | title_entry = TAILQ_FIRST(s->titles); |
304 | 0 | if (title_entry != NULL) { |
305 | 0 | free(s->title); |
306 | 0 | s->title = title_entry->text; |
307 | 0 | TAILQ_REMOVE(s->titles, title_entry, entry); |
308 | 0 | free(title_entry); |
309 | 0 | s->ntitles--; |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | | /* |
314 | | * Set the progress bar state and progress. The progress will not be updated |
315 | | * if p is negative. |
316 | | */ |
317 | | void |
318 | | screen_set_progress_bar(struct screen *s, enum progress_bar_state pbs, int p) |
319 | 0 | { |
320 | 0 | s->progress_bar.state = pbs; |
321 | 0 | if (p >= 0 && pbs != PROGRESS_BAR_INDETERMINATE) |
322 | 0 | s->progress_bar.progress = p; |
323 | 0 | } |
324 | | |
325 | | |
326 | | /* Resize screen with options. */ |
327 | | void |
328 | | screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, |
329 | | int eat_empty, int cursor) |
330 | 0 | { |
331 | 0 | u_int cx = s->cx, cy = s->grid->hsize + s->cy; |
332 | |
|
333 | 0 | if (s->write_list != NULL) |
334 | 0 | screen_write_free_list(s); |
335 | |
|
336 | 0 | log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", |
337 | 0 | __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, |
338 | 0 | cx, cy); |
339 | |
|
340 | 0 | if (sx < 1) |
341 | 0 | sx = 1; |
342 | 0 | if (sy < 1) |
343 | 0 | sy = 1; |
344 | |
|
345 | 0 | if (sx != screen_size_x(s)) { |
346 | 0 | s->grid->sx = sx; |
347 | 0 | screen_reset_tabs(s); |
348 | 0 | } else |
349 | 0 | reflow = 0; |
350 | |
|
351 | 0 | if (sy != screen_size_y(s)) |
352 | 0 | screen_resize_y(s, sy, eat_empty, &cy); |
353 | |
|
354 | 0 | if (reflow) |
355 | 0 | screen_reflow(s, sx, &cx, &cy, cursor); |
356 | |
|
357 | 0 | if (cy >= s->grid->hsize) { |
358 | 0 | s->cx = cx; |
359 | 0 | s->cy = cy - s->grid->hsize; |
360 | 0 | } else { |
361 | 0 | s->cx = 0; |
362 | 0 | s->cy = 0; |
363 | 0 | } |
364 | |
|
365 | 0 | log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, |
366 | 0 | s->cy, cx, cy); |
367 | |
|
368 | 0 | if (s->write_list != NULL) |
369 | 0 | screen_write_make_list(s); |
370 | 0 | } |
371 | | |
372 | | /* Resize screen. */ |
373 | | void |
374 | | screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) |
375 | 0 | { |
376 | 0 | screen_resize_cursor(s, sx, sy, reflow, 1, 1); |
377 | 0 | } |
378 | | |
379 | | static void |
380 | | screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) |
381 | 0 | { |
382 | 0 | struct grid *gd = s->grid; |
383 | 0 | u_int needed, available, oldy, i; |
384 | |
|
385 | 0 | if (sy == 0) |
386 | 0 | fatalx("zero size"); |
387 | 0 | oldy = screen_size_y(s); |
388 | | |
389 | | /* |
390 | | * When resizing: |
391 | | * |
392 | | * If the height is decreasing, delete lines from the bottom until |
393 | | * hitting the cursor, then push lines from the top into the history. |
394 | | * |
395 | | * When increasing, pull as many lines as possible from scrolled |
396 | | * history (not explicitly cleared from view) to the top, then fill the |
397 | | * remaining with blanks at the bottom. |
398 | | */ |
399 | | |
400 | | /* Size decreasing. */ |
401 | 0 | if (sy < oldy) { |
402 | 0 | needed = oldy - sy; |
403 | | |
404 | | /* Delete as many lines as possible from the bottom. */ |
405 | 0 | if (eat_empty) { |
406 | 0 | available = oldy - 1 - s->cy; |
407 | 0 | if (available > 0) { |
408 | 0 | if (available > needed) |
409 | 0 | available = needed; |
410 | | #ifdef ENABLE_IMAGES |
411 | | for (i = oldy - available; i < oldy; i++) { |
412 | | image_grid_free_line(gd, |
413 | | &gd->linedata[gd->hsize + i]); |
414 | | } |
415 | | #endif |
416 | 0 | grid_view_delete_lines(gd, oldy - available, |
417 | 0 | available, 8); |
418 | 0 | } |
419 | 0 | needed -= available; |
420 | 0 | } |
421 | | |
422 | | /* |
423 | | * Now just increase the history size, if possible, to take |
424 | | * over the lines which are left. If history is off, delete |
425 | | * lines from the top. |
426 | | */ |
427 | 0 | available = s->cy; |
428 | 0 | if (gd->flags & GRID_HISTORY) { |
429 | 0 | gd->hscrolled += needed; |
430 | 0 | gd->hsize += needed; |
431 | 0 | } else if (needed > 0 && available > 0) { |
432 | 0 | if (available > needed) |
433 | 0 | available = needed; |
434 | 0 | grid_view_delete_lines(gd, 0, available, 8); |
435 | 0 | (*cy) -= available; |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | | /* Resize line array. */ |
440 | 0 | grid_adjust_lines(gd, gd->hsize + sy); |
441 | | |
442 | | /* Size increasing. */ |
443 | 0 | if (sy > oldy) { |
444 | 0 | needed = sy - oldy; |
445 | | |
446 | | /* |
447 | | * Try to pull as much as possible out of scrolled history, if |
448 | | * it is enabled. |
449 | | */ |
450 | 0 | available = gd->hscrolled; |
451 | 0 | if (gd->flags & GRID_HISTORY && available > 0) { |
452 | 0 | if (available > needed) |
453 | 0 | available = needed; |
454 | 0 | gd->hscrolled -= available; |
455 | 0 | gd->hsize -= available; |
456 | 0 | } else |
457 | 0 | available = 0; |
458 | 0 | needed -= available; |
459 | | |
460 | | /* Then fill the rest in with blanks. */ |
461 | 0 | for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) |
462 | 0 | grid_empty_line(gd, i, 8); |
463 | 0 | } |
464 | | |
465 | | /* Set the new size, and reset the scroll region. */ |
466 | 0 | gd->sy = sy; |
467 | 0 | s->rupper = 0; |
468 | 0 | s->rlower = screen_size_y(s) - 1; |
469 | 0 | } |
470 | | |
471 | | /* Set selection. */ |
472 | | void |
473 | | screen_set_selection(struct screen *s, u_int sx, u_int sy, |
474 | | u_int ex, u_int ey, u_int rectangle, u_int clipx, int modekeys, |
475 | | struct grid_cell *gc) |
476 | 0 | { |
477 | 0 | if (s->sel == NULL) |
478 | 0 | s->sel = xcalloc(1, sizeof *s->sel); |
479 | |
|
480 | 0 | memcpy(&s->sel->cell, gc, sizeof s->sel->cell); |
481 | 0 | s->sel->hidden = 0; |
482 | 0 | s->sel->rectangle = rectangle; |
483 | 0 | s->sel->modekeys = modekeys; |
484 | |
|
485 | 0 | s->sel->sx = sx; |
486 | 0 | s->sel->sy = sy; |
487 | 0 | s->sel->ex = ex; |
488 | 0 | s->sel->ey = ey; |
489 | 0 | s->sel->clipx = clipx; |
490 | 0 | } |
491 | | |
492 | | /* Clear selection. */ |
493 | | void |
494 | | screen_clear_selection(struct screen *s) |
495 | 0 | { |
496 | 0 | free(s->sel); |
497 | 0 | s->sel = NULL; |
498 | 0 | } |
499 | | |
500 | | /* Hide selection. */ |
501 | | void |
502 | | screen_hide_selection(struct screen *s) |
503 | 0 | { |
504 | 0 | if (s->sel != NULL) |
505 | 0 | s->sel->hidden = 1; |
506 | 0 | } |
507 | | |
508 | | /* Check if cell in selection. */ |
509 | | int |
510 | | screen_check_selection(struct screen *s, u_int px, u_int py) |
511 | 0 | { |
512 | 0 | struct screen_sel *sel = s->sel; |
513 | 0 | u_int xx; |
514 | |
|
515 | 0 | if (sel == NULL || sel->hidden) |
516 | 0 | return (0); |
517 | 0 | if (px < sel->clipx) |
518 | 0 | return (0); |
519 | | |
520 | 0 | if (sel->rectangle) { |
521 | 0 | if (sel->sy < sel->ey) { |
522 | | /* start line < end line -- downward selection. */ |
523 | 0 | if (py < sel->sy || py > sel->ey) |
524 | 0 | return (0); |
525 | 0 | } else if (sel->sy > sel->ey) { |
526 | | /* start line > end line -- upward selection. */ |
527 | 0 | if (py > sel->sy || py < sel->ey) |
528 | 0 | return (0); |
529 | 0 | } else { |
530 | | /* starting line == ending line. */ |
531 | 0 | if (py != sel->sy) |
532 | 0 | return (0); |
533 | 0 | } |
534 | | |
535 | | /* |
536 | | * Need to include the selection start row, but not the cursor |
537 | | * row, which means the selection changes depending on which |
538 | | * one is on the left. |
539 | | */ |
540 | 0 | if (sel->ex < sel->sx) { |
541 | | /* Cursor (ex) is on the left. */ |
542 | 0 | if (px < sel->ex) |
543 | 0 | return (0); |
544 | | |
545 | 0 | if (px > sel->sx) |
546 | 0 | return (0); |
547 | 0 | } else { |
548 | | /* Selection start (sx) is on the left. */ |
549 | 0 | if (px < sel->sx) |
550 | 0 | return (0); |
551 | | |
552 | 0 | if (px > sel->ex) |
553 | 0 | return (0); |
554 | 0 | } |
555 | 0 | } else { |
556 | | /* |
557 | | * Like emacs, keep the top-left-most character, and drop the |
558 | | * bottom-right-most, regardless of copy direction. |
559 | | */ |
560 | 0 | if (sel->sy < sel->ey) { |
561 | | /* starting line < ending line -- downward selection. */ |
562 | 0 | if (py < sel->sy || py > sel->ey) |
563 | 0 | return (0); |
564 | | |
565 | 0 | if (py == sel->sy && px < sel->sx) |
566 | 0 | return (0); |
567 | | |
568 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
569 | 0 | xx = (sel->ex == 0 ? 0 : sel->ex - 1); |
570 | 0 | else |
571 | 0 | xx = sel->ex; |
572 | 0 | if (py == sel->ey && px > xx) |
573 | 0 | return (0); |
574 | 0 | } else if (sel->sy > sel->ey) { |
575 | | /* starting line > ending line -- upward selection. */ |
576 | 0 | if (py > sel->sy || py < sel->ey) |
577 | 0 | return (0); |
578 | | |
579 | 0 | if (py == sel->ey && px < sel->ex) |
580 | 0 | return (0); |
581 | | |
582 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
583 | 0 | xx = sel->sx - 1; |
584 | 0 | else |
585 | 0 | xx = sel->sx; |
586 | 0 | if (py == sel->sy && (sel->sx == 0 || px > xx)) |
587 | 0 | return (0); |
588 | 0 | } else { |
589 | | /* starting line == ending line. */ |
590 | 0 | if (py != sel->sy) |
591 | 0 | return (0); |
592 | | |
593 | 0 | if (sel->ex < sel->sx) { |
594 | | /* cursor (ex) is on the left */ |
595 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
596 | 0 | xx = sel->sx - 1; |
597 | 0 | else |
598 | 0 | xx = sel->sx; |
599 | 0 | if (px > xx || px < sel->ex) |
600 | 0 | return (0); |
601 | 0 | } else { |
602 | | /* selection start (sx) is on the left */ |
603 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
604 | 0 | xx = (sel->ex == 0 ? 0 : sel->ex - 1); |
605 | 0 | else |
606 | 0 | xx = sel->ex; |
607 | 0 | if (px < sel->sx || px > xx) |
608 | 0 | return (0); |
609 | 0 | } |
610 | 0 | } |
611 | 0 | } |
612 | | |
613 | 0 | return (1); |
614 | 0 | } |
615 | | |
616 | | /* Get selected grid cell. */ |
617 | | int |
618 | | screen_select_cell(struct screen *s, struct grid_cell *dst, |
619 | | const struct grid_cell *src) |
620 | 0 | { |
621 | 0 | if (s->sel == NULL || s->sel->hidden) |
622 | 0 | return (0); |
623 | | |
624 | 0 | memcpy(dst, &s->sel->cell, sizeof *dst); |
625 | 0 | if (COLOUR_DEFAULT(dst->fg)) |
626 | 0 | dst->fg = src->fg; |
627 | 0 | if (COLOUR_DEFAULT(dst->bg)) |
628 | 0 | dst->bg = src->bg; |
629 | 0 | utf8_copy(&dst->data, &src->data); |
630 | 0 | dst->flags = src->flags; |
631 | |
|
632 | 0 | if (dst->attr & GRID_ATTR_NOATTR) |
633 | 0 | dst->attr |= (src->attr & GRID_ATTR_CHARSET); |
634 | 0 | else |
635 | 0 | dst->attr |= src->attr; |
636 | 0 | return (1); |
637 | 0 | } |
638 | | |
639 | | /* Reflow wrapped lines. */ |
640 | | static void |
641 | | screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) |
642 | 0 | { |
643 | 0 | u_int wx, wy; |
644 | |
|
645 | 0 | if (cursor) { |
646 | 0 | grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); |
647 | 0 | log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, |
648 | 0 | wy); |
649 | 0 | } |
650 | |
|
651 | 0 | grid_reflow(s->grid, new_x); |
652 | |
|
653 | 0 | if (cursor) { |
654 | 0 | grid_unwrap_position(s->grid, cx, cy, wx, wy); |
655 | 0 | log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy); |
656 | 0 | } |
657 | 0 | else { |
658 | 0 | *cx = 0; |
659 | 0 | *cy = s->grid->hsize; |
660 | 0 | } |
661 | 0 | } |
662 | | |
663 | | /* |
664 | | * Enter alternative screen mode. A copy of the visible screen is saved and the |
665 | | * history is not updated. |
666 | | */ |
667 | | int |
668 | | screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) |
669 | 0 | { |
670 | 0 | u_int sx, sy; |
671 | 0 | if (SCREEN_IS_ALTERNATE(s)) |
672 | 0 | return 0; |
673 | 0 | sx = screen_size_x(s); |
674 | 0 | sy = screen_size_y(s); |
675 | |
|
676 | 0 | s->saved_grid = grid_create(sx, sy, 0); |
677 | 0 | grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy); |
678 | 0 | if (cursor) { |
679 | 0 | s->saved_cx = s->cx; |
680 | 0 | s->saved_cy = s->cy; |
681 | 0 | } |
682 | 0 | memcpy(&s->saved_cell, gc, sizeof s->saved_cell); |
683 | |
|
684 | 0 | grid_free_lines(s->grid, screen_hsize(s), sy); |
685 | 0 | grid_view_clear(s->grid, 0, 0, sx, sy, 8); |
686 | |
|
687 | 0 | s->saved_flags = s->grid->flags; |
688 | 0 | s->grid->flags &= ~GRID_HISTORY; |
689 | |
|
690 | 0 | return 1; |
691 | 0 | } |
692 | | |
693 | | /* Exit alternate screen mode and restore the copied grid. */ |
694 | | int |
695 | | screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) |
696 | 0 | { |
697 | 0 | u_int sx = screen_size_x(s), sy = screen_size_y(s); |
698 | | /* |
699 | | * If the current size is different, temporarily resize to the old size |
700 | | * before copying back. |
701 | | */ |
702 | 0 | if (SCREEN_IS_ALTERNATE(s)) |
703 | 0 | screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0); |
704 | | |
705 | | /* |
706 | | * Restore the cursor position and cell. This happens even if not |
707 | | * currently in the alternate screen. |
708 | | */ |
709 | 0 | if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { |
710 | 0 | s->cx = s->saved_cx; |
711 | 0 | s->cy = s->saved_cy; |
712 | 0 | if (gc != NULL) |
713 | 0 | memcpy(gc, &s->saved_cell, sizeof *gc); |
714 | 0 | } |
715 | | |
716 | | /* If not in the alternate screen, do nothing more. */ |
717 | 0 | if (!SCREEN_IS_ALTERNATE(s)) { |
718 | 0 | if (s->cx > screen_size_x(s) - 1) |
719 | 0 | s->cx = screen_size_x(s) - 1; |
720 | 0 | if (s->cy > screen_size_y(s) - 1) |
721 | 0 | s->cy = screen_size_y(s) - 1; |
722 | 0 | return 0; |
723 | 0 | } |
724 | | |
725 | | /* Clamp cursor to the current screen size. */ |
726 | 0 | if (s->cy > screen_size_y(s) - 1) |
727 | 0 | s->cy = screen_size_y(s) - 1; |
728 | | |
729 | | /* Restore the saved grid. */ |
730 | 0 | grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, |
731 | 0 | s->saved_grid->sy); |
732 | | |
733 | | /* |
734 | | * Turn history back on (so resize can use it) and then resize back to |
735 | | * the current size. |
736 | | */ |
737 | 0 | if (s->saved_flags & GRID_HISTORY) |
738 | 0 | s->grid->flags |= GRID_HISTORY; |
739 | 0 | screen_resize(s, sx, sy, 1); |
740 | |
|
741 | 0 | grid_destroy(s->saved_grid); |
742 | 0 | s->saved_grid = NULL; |
743 | |
|
744 | 0 | if (s->cx > screen_size_x(s) - 1) |
745 | 0 | s->cx = screen_size_x(s) - 1; |
746 | 0 | if (s->cy > screen_size_y(s) - 1) |
747 | 0 | s->cy = screen_size_y(s) - 1; |
748 | |
|
749 | 0 | return 1; |
750 | 0 | } |
751 | | |
752 | | /* Get mode as a string. */ |
753 | | const char * |
754 | | screen_mode_to_string(int mode) |
755 | 0 | { |
756 | 0 | static char tmp[1024]; |
757 | |
|
758 | 0 | if (mode == 0) |
759 | 0 | return ("NONE"); |
760 | 0 | if (mode == ALL_MODES) |
761 | 0 | return ("ALL"); |
762 | | |
763 | 0 | *tmp = '\0'; |
764 | 0 | if (mode & MODE_CURSOR) |
765 | 0 | strlcat(tmp, "CURSOR,", sizeof tmp); |
766 | 0 | if (mode & MODE_INSERT) |
767 | 0 | strlcat(tmp, "INSERT,", sizeof tmp); |
768 | 0 | if (mode & MODE_KCURSOR) |
769 | 0 | strlcat(tmp, "KCURSOR,", sizeof tmp); |
770 | 0 | if (mode & MODE_KKEYPAD) |
771 | 0 | strlcat(tmp, "KKEYPAD,", sizeof tmp); |
772 | 0 | if (mode & MODE_WRAP) |
773 | 0 | strlcat(tmp, "WRAP,", sizeof tmp); |
774 | 0 | if (mode & MODE_MOUSE_STANDARD) |
775 | 0 | strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp); |
776 | 0 | if (mode & MODE_MOUSE_BUTTON) |
777 | 0 | strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp); |
778 | 0 | if (mode & MODE_CURSOR_BLINKING) |
779 | 0 | strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp); |
780 | 0 | if (mode & MODE_CURSOR_VERY_VISIBLE) |
781 | 0 | strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); |
782 | 0 | if (mode & MODE_CURSOR_BLINKING_SET) |
783 | 0 | strlcat(tmp, "CURSOR_BLINKING_SET,", sizeof tmp); |
784 | 0 | if (mode & MODE_MOUSE_UTF8) |
785 | 0 | strlcat(tmp, "MOUSE_UTF8,", sizeof tmp); |
786 | 0 | if (mode & MODE_MOUSE_SGR) |
787 | 0 | strlcat(tmp, "MOUSE_SGR,", sizeof tmp); |
788 | 0 | if (mode & MODE_BRACKETPASTE) |
789 | 0 | strlcat(tmp, "BRACKETPASTE,", sizeof tmp); |
790 | 0 | if (mode & MODE_FOCUSON) |
791 | 0 | strlcat(tmp, "FOCUSON,", sizeof tmp); |
792 | 0 | if (mode & MODE_MOUSE_ALL) |
793 | 0 | strlcat(tmp, "MOUSE_ALL,", sizeof tmp); |
794 | 0 | if (mode & MODE_ORIGIN) |
795 | 0 | strlcat(tmp, "ORIGIN,", sizeof tmp); |
796 | 0 | if (mode & MODE_CRLF) |
797 | 0 | strlcat(tmp, "CRLF,", sizeof tmp); |
798 | 0 | if (mode & MODE_KEYS_EXTENDED) |
799 | 0 | strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp); |
800 | 0 | if (mode & MODE_KEYS_EXTENDED_2) |
801 | 0 | strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp); |
802 | 0 | if (mode & MODE_THEME_UPDATES) |
803 | 0 | strlcat(tmp, "THEME_UPDATES,", sizeof tmp); |
804 | 0 | if (mode & MODE_SYNC) |
805 | 0 | strlcat(tmp, "SYNC,", sizeof tmp); |
806 | 0 | if (*tmp != '\0') |
807 | 0 | tmp[strlen(tmp) - 1] = '\0'; |
808 | 0 | return (tmp); |
809 | 0 | } |
810 | | |
811 | | /* Convert screen to a string. */ |
812 | | const char * |
813 | | screen_print(struct screen *s, int line) |
814 | 0 | { |
815 | 0 | static char *buf; |
816 | 0 | static size_t len = 16384; |
817 | 0 | const char *acs; |
818 | 0 | u_int x, y; |
819 | 0 | int n; |
820 | 0 | size_t last = 0; |
821 | 0 | struct utf8_data ud; |
822 | 0 | struct grid_line *gl; |
823 | 0 | struct grid_cell_entry *gce; |
824 | |
|
825 | 0 | if (buf == NULL) |
826 | 0 | buf = xmalloc(len); |
827 | |
|
828 | 0 | for (y = 0; y < screen_hsize(s) + s->grid->sy; y++) { |
829 | 0 | if (line >= 0 && y != (u_int)line) |
830 | 0 | continue; |
831 | 0 | n = snprintf(buf + last, len - last, "%.4d \"", y); |
832 | 0 | if (n <= 0 || (u_int)n >= len - last) |
833 | 0 | goto out; |
834 | 0 | last += n; |
835 | |
|
836 | 0 | gl = &s->grid->linedata[y]; |
837 | 0 | for (x = 0; x < gl->cellused; x++) { |
838 | 0 | gce = &gl->celldata[x]; |
839 | 0 | if (gce->flags & GRID_FLAG_PADDING) |
840 | 0 | continue; |
841 | | |
842 | 0 | if (~gce->flags & GRID_FLAG_EXTENDED) { |
843 | 0 | if (last + 2 >= len) |
844 | 0 | goto out; |
845 | 0 | buf[last++] = gce->data.data; |
846 | 0 | } else if (gce->flags & GRID_FLAG_TAB) { |
847 | 0 | if (last + 2 >= len) |
848 | 0 | goto out; |
849 | 0 | buf[last++] = '\t'; |
850 | 0 | } else if (gce->flags & GRID_ATTR_CHARSET) { |
851 | 0 | acs = tty_acs_get(NULL, gce->data.data); |
852 | 0 | if (acs != NULL) |
853 | 0 | n = strlen(acs); |
854 | 0 | else { |
855 | 0 | acs = &gce->data.data; |
856 | 0 | n = 1; |
857 | 0 | } |
858 | 0 | if (last + n + 1 >= len) |
859 | 0 | goto out; |
860 | 0 | memcpy(buf + last, acs, n); |
861 | 0 | last += n; |
862 | 0 | } else { |
863 | 0 | utf8_to_data(gl->extddata[gce->offset].data, |
864 | 0 | &ud); |
865 | 0 | if (ud.size > 0) { |
866 | 0 | if (last + ud.size + 1 >= len) |
867 | 0 | goto out; |
868 | 0 | memcpy(buf + last, ud.data, ud.size); |
869 | 0 | last += ud.size; |
870 | 0 | } |
871 | 0 | } |
872 | 0 | } |
873 | | |
874 | 0 | if (last + 3 >= len) |
875 | 0 | goto out; |
876 | 0 | buf[last++] = '"'; |
877 | 0 | buf[last++] = '\n'; |
878 | 0 | } |
879 | | |
880 | 0 | out: |
881 | 0 | buf[last] = '\0'; |
882 | 0 | return (buf); |
883 | 0 | } |