Line | Count | Source |
1 | | /* $OpenBSD$ */ |
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 | | struct grid_cell cell; |
40 | | }; |
41 | | |
42 | | /* Entry on title stack. */ |
43 | | struct screen_title_entry { |
44 | | char *text; |
45 | | |
46 | | TAILQ_ENTRY(screen_title_entry) entry; |
47 | | }; |
48 | | TAILQ_HEAD(screen_titles, screen_title_entry); |
49 | | |
50 | | static void screen_resize_y(struct screen *, u_int, int, u_int *); |
51 | | static void screen_reflow(struct screen *, u_int, u_int *, u_int *, int); |
52 | | |
53 | | /* Free titles stack. */ |
54 | | static void |
55 | | screen_free_titles(struct screen *s) |
56 | 45.7k | { |
57 | 45.7k | struct screen_title_entry *title_entry; |
58 | | |
59 | 45.7k | if (s->titles == NULL) |
60 | 45.6k | return; |
61 | | |
62 | 251 | while ((title_entry = TAILQ_FIRST(s->titles)) != NULL) { |
63 | 195 | TAILQ_REMOVE(s->titles, title_entry, entry); |
64 | 195 | free(title_entry->text); |
65 | 195 | free(title_entry); |
66 | 195 | } |
67 | | |
68 | 56 | free(s->titles); |
69 | 56 | s->titles = NULL; |
70 | 56 | } |
71 | | |
72 | | /* Create a new screen. */ |
73 | | void |
74 | | screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) |
75 | 22.8k | { |
76 | 22.8k | s->grid = grid_create(sx, sy, hlimit); |
77 | 22.8k | s->saved_grid = NULL; |
78 | | |
79 | 22.8k | s->title = xstrdup(""); |
80 | 22.8k | s->titles = NULL; |
81 | 22.8k | s->path = NULL; |
82 | | |
83 | 22.8k | s->cstyle = SCREEN_CURSOR_DEFAULT; |
84 | 22.8k | s->default_cstyle = SCREEN_CURSOR_DEFAULT; |
85 | 22.8k | s->mode = MODE_CURSOR; |
86 | 22.8k | s->default_mode = 0; |
87 | 22.8k | s->ccolour = -1; |
88 | 22.8k | s->default_ccolour = -1; |
89 | 22.8k | s->tabs = NULL; |
90 | 22.8k | s->sel = NULL; |
91 | | |
92 | | #ifdef ENABLE_SIXEL |
93 | | TAILQ_INIT(&s->images); |
94 | | #endif |
95 | | |
96 | 22.8k | s->write_list = NULL; |
97 | 22.8k | s->hyperlinks = NULL; |
98 | | |
99 | 22.8k | screen_reinit(s); |
100 | 22.8k | } |
101 | | |
102 | | /* Reinitialise screen. */ |
103 | | void |
104 | | screen_reinit(struct screen *s) |
105 | 22.8k | { |
106 | 22.8k | s->cx = 0; |
107 | 22.8k | s->cy = 0; |
108 | | |
109 | 22.8k | s->rupper = 0; |
110 | 22.8k | s->rlower = screen_size_y(s) - 1; |
111 | | |
112 | 22.8k | s->mode = MODE_CURSOR|MODE_WRAP|(s->mode & MODE_CRLF); |
113 | | |
114 | 22.8k | if (options_get_number(global_options, "extended-keys") == 2) |
115 | 0 | s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED; |
116 | | |
117 | 22.8k | if (SCREEN_IS_ALTERNATE(s)) |
118 | 0 | screen_alternate_off(s, NULL, 0); |
119 | 22.8k | s->saved_cx = UINT_MAX; |
120 | 22.8k | s->saved_cy = UINT_MAX; |
121 | | |
122 | 22.8k | screen_reset_tabs(s); |
123 | | |
124 | 22.8k | grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy, 8); |
125 | | |
126 | 22.8k | screen_clear_selection(s); |
127 | 22.8k | screen_free_titles(s); |
128 | | |
129 | | #ifdef ENABLE_SIXEL |
130 | | image_free_all(s); |
131 | | #endif |
132 | | |
133 | 22.8k | screen_reset_hyperlinks(s); |
134 | 22.8k | } |
135 | | |
136 | | /* Reset hyperlinks of a screen. */ |
137 | | void |
138 | | screen_reset_hyperlinks(struct screen *s) |
139 | 22.8k | { |
140 | 22.8k | if (s->hyperlinks == NULL) |
141 | 22.8k | s->hyperlinks = hyperlinks_init(); |
142 | 0 | else |
143 | 0 | hyperlinks_reset(s->hyperlinks); |
144 | 22.8k | } |
145 | | |
146 | | /* Destroy a screen. */ |
147 | | void |
148 | | screen_free(struct screen *s) |
149 | 22.8k | { |
150 | 22.8k | free(s->sel); |
151 | 22.8k | free(s->tabs); |
152 | 22.8k | free(s->path); |
153 | 22.8k | free(s->title); |
154 | | |
155 | 22.8k | if (s->write_list != NULL) |
156 | 11.4k | screen_write_free_list(s); |
157 | | |
158 | 22.8k | if (SCREEN_IS_ALTERNATE(s)) |
159 | 74 | grid_destroy(s->saved_grid); |
160 | 22.8k | grid_destroy(s->grid); |
161 | | |
162 | 22.8k | if (s->hyperlinks != NULL) |
163 | 22.8k | hyperlinks_free(s->hyperlinks); |
164 | 22.8k | screen_free_titles(s); |
165 | | |
166 | | #ifdef ENABLE_SIXEL |
167 | | image_free_all(s); |
168 | | #endif |
169 | 22.8k | } |
170 | | |
171 | | /* Reset tabs to default, eight spaces apart. */ |
172 | | void |
173 | | screen_reset_tabs(struct screen *s) |
174 | 23.6k | { |
175 | 23.6k | u_int i; |
176 | | |
177 | 23.6k | free(s->tabs); |
178 | | |
179 | 23.6k | if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL) |
180 | 0 | fatal("bit_alloc failed"); |
181 | 133k | for (i = 8; i < screen_size_x(s); i += 8) |
182 | 110k | bit_set(s->tabs, i); |
183 | 23.6k | } |
184 | | |
185 | | /* Set default cursor style and colour from options. */ |
186 | | void |
187 | | screen_set_default_cursor(struct screen *s, struct options *oo) |
188 | 11.4k | { |
189 | 11.4k | int c; |
190 | | |
191 | 11.4k | c = options_get_number(oo, "cursor-colour"); |
192 | 11.4k | s->default_ccolour = c; |
193 | | |
194 | 11.4k | c = options_get_number(oo, "cursor-style"); |
195 | 11.4k | s->default_mode = 0; |
196 | 11.4k | screen_set_cursor_style(c, &s->default_cstyle, &s->default_mode); |
197 | 11.4k | } |
198 | | |
199 | | /* Set screen cursor style and mode. */ |
200 | | void |
201 | | screen_set_cursor_style(u_int style, enum screen_cursor_style *cstyle, |
202 | | int *mode) |
203 | 12.0k | { |
204 | 12.0k | switch (style) { |
205 | 11.5k | case 0: |
206 | 11.5k | *cstyle = SCREEN_CURSOR_DEFAULT; |
207 | 11.5k | break; |
208 | 76 | case 1: |
209 | 76 | *cstyle = SCREEN_CURSOR_BLOCK; |
210 | 76 | *mode |= MODE_CURSOR_BLINKING; |
211 | 76 | break; |
212 | 70 | case 2: |
213 | 70 | *cstyle = SCREEN_CURSOR_BLOCK; |
214 | 70 | *mode &= ~MODE_CURSOR_BLINKING; |
215 | 70 | break; |
216 | 69 | case 3: |
217 | 69 | *cstyle = SCREEN_CURSOR_UNDERLINE; |
218 | 69 | *mode |= MODE_CURSOR_BLINKING; |
219 | 69 | break; |
220 | 75 | case 4: |
221 | 75 | *cstyle = SCREEN_CURSOR_UNDERLINE; |
222 | 75 | *mode &= ~MODE_CURSOR_BLINKING; |
223 | 75 | break; |
224 | 73 | case 5: |
225 | 73 | *cstyle = SCREEN_CURSOR_BAR; |
226 | 73 | *mode |= MODE_CURSOR_BLINKING; |
227 | 73 | break; |
228 | 71 | case 6: |
229 | 71 | *cstyle = SCREEN_CURSOR_BAR; |
230 | 71 | *mode &= ~MODE_CURSOR_BLINKING; |
231 | 71 | break; |
232 | 12.0k | } |
233 | 12.0k | } |
234 | | |
235 | | /* Set screen cursor colour. */ |
236 | | void |
237 | | screen_set_cursor_colour(struct screen *s, int colour) |
238 | 191 | { |
239 | 191 | s->ccolour = colour; |
240 | 191 | } |
241 | | |
242 | | /* Set screen title. */ |
243 | | int |
244 | | screen_set_title(struct screen *s, const char *title) |
245 | 14.5k | { |
246 | 14.5k | if (!utf8_isvalid(title)) |
247 | 612 | return (0); |
248 | 13.9k | free(s->title); |
249 | 13.9k | s->title = xstrdup(title); |
250 | 13.9k | return (1); |
251 | 14.5k | } |
252 | | |
253 | | /* Set screen path. */ |
254 | | void |
255 | | screen_set_path(struct screen *s, const char *path) |
256 | 200 | { |
257 | 200 | free(s->path); |
258 | 200 | utf8_stravis(&s->path, path, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL); |
259 | 200 | } |
260 | | |
261 | | /* Push the current title onto the stack. */ |
262 | | void |
263 | | screen_push_title(struct screen *s) |
264 | 403 | { |
265 | 403 | struct screen_title_entry *title_entry; |
266 | | |
267 | 403 | if (s->titles == NULL) { |
268 | 56 | s->titles = xmalloc(sizeof *s->titles); |
269 | 56 | TAILQ_INIT(s->titles); |
270 | 56 | } |
271 | 403 | title_entry = xmalloc(sizeof *title_entry); |
272 | 403 | title_entry->text = xstrdup(s->title); |
273 | 403 | TAILQ_INSERT_HEAD(s->titles, title_entry, entry); |
274 | 403 | } |
275 | | |
276 | | /* |
277 | | * Pop a title from the stack and set it as the screen title. If the stack is |
278 | | * empty, do nothing. |
279 | | */ |
280 | | void |
281 | | screen_pop_title(struct screen *s) |
282 | 407 | { |
283 | 407 | struct screen_title_entry *title_entry; |
284 | | |
285 | 407 | if (s->titles == NULL) |
286 | 132 | return; |
287 | | |
288 | 275 | title_entry = TAILQ_FIRST(s->titles); |
289 | 275 | if (title_entry != NULL) { |
290 | 208 | screen_set_title(s, title_entry->text); |
291 | | |
292 | 208 | TAILQ_REMOVE(s->titles, title_entry, entry); |
293 | 208 | free(title_entry->text); |
294 | 208 | free(title_entry); |
295 | 208 | } |
296 | 275 | } |
297 | | |
298 | | /* Resize screen with options. */ |
299 | | void |
300 | | screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, |
301 | | int eat_empty, int cursor) |
302 | 682 | { |
303 | 682 | u_int cx = s->cx, cy = s->grid->hsize + s->cy; |
304 | | |
305 | 682 | if (s->write_list != NULL) |
306 | 682 | screen_write_free_list(s); |
307 | | |
308 | 682 | log_debug("%s: new size %ux%u, now %ux%u (cursor %u,%u = %u,%u)", |
309 | 682 | __func__, sx, sy, screen_size_x(s), screen_size_y(s), s->cx, s->cy, |
310 | 682 | cx, cy); |
311 | | |
312 | 682 | if (sx < 1) |
313 | 0 | sx = 1; |
314 | 682 | if (sy < 1) |
315 | 0 | sy = 1; |
316 | | |
317 | 682 | if (sx != screen_size_x(s)) { |
318 | 0 | s->grid->sx = sx; |
319 | 0 | screen_reset_tabs(s); |
320 | 0 | } else |
321 | 682 | reflow = 0; |
322 | | |
323 | 682 | if (sy != screen_size_y(s)) |
324 | 0 | screen_resize_y(s, sy, eat_empty, &cy); |
325 | | |
326 | | #ifdef ENABLE_SIXEL |
327 | | image_free_all(s); |
328 | | #endif |
329 | | |
330 | 682 | if (reflow) |
331 | 0 | screen_reflow(s, sx, &cx, &cy, cursor); |
332 | | |
333 | 682 | if (cy >= s->grid->hsize) { |
334 | 682 | s->cx = cx; |
335 | 682 | s->cy = cy - s->grid->hsize; |
336 | 682 | } else { |
337 | 0 | s->cx = 0; |
338 | 0 | s->cy = 0; |
339 | 0 | } |
340 | | |
341 | 682 | log_debug("%s: cursor finished at %u,%u = %u,%u", __func__, s->cx, |
342 | 682 | s->cy, cx, cy); |
343 | | |
344 | 682 | if (s->write_list != NULL) |
345 | 682 | screen_write_make_list(s); |
346 | 682 | } |
347 | | |
348 | | /* Resize screen. */ |
349 | | void |
350 | | screen_resize(struct screen *s, u_int sx, u_int sy, int reflow) |
351 | 682 | { |
352 | 682 | screen_resize_cursor(s, sx, sy, reflow, 1, 1); |
353 | 682 | } |
354 | | |
355 | | static void |
356 | | screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy) |
357 | 0 | { |
358 | 0 | struct grid *gd = s->grid; |
359 | 0 | u_int needed, available, oldy, i; |
360 | |
|
361 | 0 | if (sy == 0) |
362 | 0 | fatalx("zero size"); |
363 | 0 | oldy = screen_size_y(s); |
364 | | |
365 | | /* |
366 | | * When resizing: |
367 | | * |
368 | | * If the height is decreasing, delete lines from the bottom until |
369 | | * hitting the cursor, then push lines from the top into the history. |
370 | | * |
371 | | * When increasing, pull as many lines as possible from scrolled |
372 | | * history (not explicitly cleared from view) to the top, then fill the |
373 | | * remaining with blanks at the bottom. |
374 | | */ |
375 | | |
376 | | /* Size decreasing. */ |
377 | 0 | if (sy < oldy) { |
378 | 0 | needed = oldy - sy; |
379 | | |
380 | | /* Delete as many lines as possible from the bottom. */ |
381 | 0 | if (eat_empty) { |
382 | 0 | available = oldy - 1 - s->cy; |
383 | 0 | if (available > 0) { |
384 | 0 | if (available > needed) |
385 | 0 | available = needed; |
386 | 0 | grid_view_delete_lines(gd, oldy - available, |
387 | 0 | available, 8); |
388 | 0 | } |
389 | 0 | needed -= available; |
390 | 0 | } |
391 | | |
392 | | /* |
393 | | * Now just increase the history size, if possible, to take |
394 | | * over the lines which are left. If history is off, delete |
395 | | * lines from the top. |
396 | | */ |
397 | 0 | available = s->cy; |
398 | 0 | if (gd->flags & GRID_HISTORY) { |
399 | 0 | gd->hscrolled += needed; |
400 | 0 | gd->hsize += needed; |
401 | 0 | } else if (needed > 0 && available > 0) { |
402 | 0 | if (available > needed) |
403 | 0 | available = needed; |
404 | 0 | grid_view_delete_lines(gd, 0, available, 8); |
405 | 0 | (*cy) -= available; |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | /* Resize line array. */ |
410 | 0 | grid_adjust_lines(gd, gd->hsize + sy); |
411 | | |
412 | | /* Size increasing. */ |
413 | 0 | if (sy > oldy) { |
414 | 0 | needed = sy - oldy; |
415 | | |
416 | | /* |
417 | | * Try to pull as much as possible out of scrolled history, if |
418 | | * it is enabled. |
419 | | */ |
420 | 0 | available = gd->hscrolled; |
421 | 0 | if (gd->flags & GRID_HISTORY && available > 0) { |
422 | 0 | if (available > needed) |
423 | 0 | available = needed; |
424 | 0 | gd->hscrolled -= available; |
425 | 0 | gd->hsize -= available; |
426 | 0 | } else |
427 | 0 | available = 0; |
428 | 0 | needed -= available; |
429 | | |
430 | | /* Then fill the rest in with blanks. */ |
431 | 0 | for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++) |
432 | 0 | grid_empty_line(gd, i, 8); |
433 | 0 | } |
434 | | |
435 | | /* Set the new size, and reset the scroll region. */ |
436 | 0 | gd->sy = sy; |
437 | 0 | s->rupper = 0; |
438 | 0 | s->rlower = screen_size_y(s) - 1; |
439 | 0 | } |
440 | | |
441 | | /* Set selection. */ |
442 | | void |
443 | | screen_set_selection(struct screen *s, u_int sx, u_int sy, |
444 | | u_int ex, u_int ey, u_int rectangle, int modekeys, struct grid_cell *gc) |
445 | 0 | { |
446 | 0 | if (s->sel == NULL) |
447 | 0 | s->sel = xcalloc(1, sizeof *s->sel); |
448 | |
|
449 | 0 | memcpy(&s->sel->cell, gc, sizeof s->sel->cell); |
450 | 0 | s->sel->hidden = 0; |
451 | 0 | s->sel->rectangle = rectangle; |
452 | 0 | s->sel->modekeys = modekeys; |
453 | |
|
454 | 0 | s->sel->sx = sx; |
455 | 0 | s->sel->sy = sy; |
456 | 0 | s->sel->ex = ex; |
457 | 0 | s->sel->ey = ey; |
458 | 0 | } |
459 | | |
460 | | /* Clear selection. */ |
461 | | void |
462 | | screen_clear_selection(struct screen *s) |
463 | 22.8k | { |
464 | 22.8k | free(s->sel); |
465 | 22.8k | s->sel = NULL; |
466 | 22.8k | } |
467 | | |
468 | | /* Hide selection. */ |
469 | | void |
470 | | screen_hide_selection(struct screen *s) |
471 | 0 | { |
472 | 0 | if (s->sel != NULL) |
473 | 0 | s->sel->hidden = 1; |
474 | 0 | } |
475 | | |
476 | | /* Check if cell in selection. */ |
477 | | int |
478 | | screen_check_selection(struct screen *s, u_int px, u_int py) |
479 | 18.9k | { |
480 | 18.9k | struct screen_sel *sel = s->sel; |
481 | 18.9k | u_int xx; |
482 | | |
483 | 18.9k | if (sel == NULL || sel->hidden) |
484 | 18.9k | return (0); |
485 | | |
486 | 0 | if (sel->rectangle) { |
487 | 0 | if (sel->sy < sel->ey) { |
488 | | /* start line < end line -- downward selection. */ |
489 | 0 | if (py < sel->sy || py > sel->ey) |
490 | 0 | return (0); |
491 | 0 | } else if (sel->sy > sel->ey) { |
492 | | /* start line > end line -- upward selection. */ |
493 | 0 | if (py > sel->sy || py < sel->ey) |
494 | 0 | return (0); |
495 | 0 | } else { |
496 | | /* starting line == ending line. */ |
497 | 0 | if (py != sel->sy) |
498 | 0 | return (0); |
499 | 0 | } |
500 | | |
501 | | /* |
502 | | * Need to include the selection start row, but not the cursor |
503 | | * row, which means the selection changes depending on which |
504 | | * one is on the left. |
505 | | */ |
506 | 0 | if (sel->ex < sel->sx) { |
507 | | /* Cursor (ex) is on the left. */ |
508 | 0 | if (px < sel->ex) |
509 | 0 | return (0); |
510 | | |
511 | 0 | if (px > sel->sx) |
512 | 0 | return (0); |
513 | 0 | } else { |
514 | | /* Selection start (sx) is on the left. */ |
515 | 0 | if (px < sel->sx) |
516 | 0 | return (0); |
517 | | |
518 | 0 | if (px > sel->ex) |
519 | 0 | return (0); |
520 | 0 | } |
521 | 0 | } else { |
522 | | /* |
523 | | * Like emacs, keep the top-left-most character, and drop the |
524 | | * bottom-right-most, regardless of copy direction. |
525 | | */ |
526 | 0 | if (sel->sy < sel->ey) { |
527 | | /* starting line < ending line -- downward selection. */ |
528 | 0 | if (py < sel->sy || py > sel->ey) |
529 | 0 | return (0); |
530 | | |
531 | 0 | if (py == sel->sy && px < sel->sx) |
532 | 0 | return (0); |
533 | | |
534 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
535 | 0 | xx = (sel->ex == 0 ? 0 : sel->ex - 1); |
536 | 0 | else |
537 | 0 | xx = sel->ex; |
538 | 0 | if (py == sel->ey && px > xx) |
539 | 0 | return (0); |
540 | 0 | } else if (sel->sy > sel->ey) { |
541 | | /* starting line > ending line -- upward selection. */ |
542 | 0 | if (py > sel->sy || py < sel->ey) |
543 | 0 | return (0); |
544 | | |
545 | 0 | if (py == sel->ey && px < sel->ex) |
546 | 0 | return (0); |
547 | | |
548 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
549 | 0 | xx = sel->sx - 1; |
550 | 0 | else |
551 | 0 | xx = sel->sx; |
552 | 0 | if (py == sel->sy && (sel->sx == 0 || px > xx)) |
553 | 0 | return (0); |
554 | 0 | } else { |
555 | | /* starting line == ending line. */ |
556 | 0 | if (py != sel->sy) |
557 | 0 | return (0); |
558 | | |
559 | 0 | if (sel->ex < sel->sx) { |
560 | | /* cursor (ex) is on the left */ |
561 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
562 | 0 | xx = sel->sx - 1; |
563 | 0 | else |
564 | 0 | xx = sel->sx; |
565 | 0 | if (px > xx || px < sel->ex) |
566 | 0 | return (0); |
567 | 0 | } else { |
568 | | /* selection start (sx) is on the left */ |
569 | 0 | if (sel->modekeys == MODEKEY_EMACS) |
570 | 0 | xx = (sel->ex == 0 ? 0 : sel->ex - 1); |
571 | 0 | else |
572 | 0 | xx = sel->ex; |
573 | 0 | if (px < sel->sx || px > xx) |
574 | 0 | return (0); |
575 | 0 | } |
576 | 0 | } |
577 | 0 | } |
578 | | |
579 | 0 | return (1); |
580 | 0 | } |
581 | | |
582 | | /* Get selected grid cell. */ |
583 | | void |
584 | | screen_select_cell(struct screen *s, struct grid_cell *dst, |
585 | | const struct grid_cell *src) |
586 | 0 | { |
587 | 0 | if (s->sel == NULL || s->sel->hidden) |
588 | 0 | return; |
589 | | |
590 | 0 | memcpy(dst, &s->sel->cell, sizeof *dst); |
591 | 0 | if (COLOUR_DEFAULT(dst->fg)) |
592 | 0 | dst->fg = src->fg; |
593 | 0 | if (COLOUR_DEFAULT(dst->bg)) |
594 | 0 | dst->bg = src->bg; |
595 | 0 | utf8_copy(&dst->data, &src->data); |
596 | 0 | dst->attr = src->attr; |
597 | 0 | dst->flags = src->flags; |
598 | 0 | } |
599 | | |
600 | | /* Reflow wrapped lines. */ |
601 | | static void |
602 | | screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) |
603 | 0 | { |
604 | 0 | u_int wx, wy; |
605 | |
|
606 | 0 | if (cursor) { |
607 | 0 | grid_wrap_position(s->grid, *cx, *cy, &wx, &wy); |
608 | 0 | log_debug("%s: cursor %u,%u is %u,%u", __func__, *cx, *cy, wx, |
609 | 0 | wy); |
610 | 0 | } |
611 | |
|
612 | 0 | grid_reflow(s->grid, new_x); |
613 | |
|
614 | 0 | if (cursor) { |
615 | 0 | grid_unwrap_position(s->grid, cx, cy, wx, wy); |
616 | 0 | log_debug("%s: new cursor is %u,%u", __func__, *cx, *cy); |
617 | 0 | } |
618 | 0 | else { |
619 | 0 | *cx = 0; |
620 | 0 | *cy = s->grid->hsize; |
621 | 0 | } |
622 | 0 | } |
623 | | |
624 | | /* |
625 | | * Enter alternative screen mode. A copy of the visible screen is saved and the |
626 | | * history is not updated. |
627 | | */ |
628 | | void |
629 | | screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) |
630 | 760 | { |
631 | 760 | u_int sx, sy; |
632 | | |
633 | 760 | if (SCREEN_IS_ALTERNATE(s)) |
634 | 345 | return; |
635 | 415 | sx = screen_size_x(s); |
636 | 415 | sy = screen_size_y(s); |
637 | | |
638 | 415 | s->saved_grid = grid_create(sx, sy, 0); |
639 | 415 | grid_duplicate_lines(s->saved_grid, 0, s->grid, screen_hsize(s), sy); |
640 | 415 | if (cursor) { |
641 | 94 | s->saved_cx = s->cx; |
642 | 94 | s->saved_cy = s->cy; |
643 | 94 | } |
644 | 415 | memcpy(&s->saved_cell, gc, sizeof s->saved_cell); |
645 | | |
646 | 415 | grid_view_clear(s->grid, 0, 0, sx, sy, 8); |
647 | | |
648 | 415 | s->saved_flags = s->grid->flags; |
649 | 415 | s->grid->flags &= ~GRID_HISTORY; |
650 | 415 | } |
651 | | |
652 | | /* Exit alternate screen mode and restore the copied grid. */ |
653 | | void |
654 | | screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) |
655 | 1.01k | { |
656 | 1.01k | u_int sx = screen_size_x(s), sy = screen_size_y(s); |
657 | | |
658 | | /* |
659 | | * If the current size is different, temporarily resize to the old size |
660 | | * before copying back. |
661 | | */ |
662 | 1.01k | if (SCREEN_IS_ALTERNATE(s)) |
663 | 341 | screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0); |
664 | | |
665 | | /* |
666 | | * Restore the cursor position and cell. This happens even if not |
667 | | * currently in the alternate screen. |
668 | | */ |
669 | 1.01k | if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { |
670 | 137 | s->cx = s->saved_cx; |
671 | 137 | s->cy = s->saved_cy; |
672 | 137 | if (gc != NULL) |
673 | 137 | memcpy(gc, &s->saved_cell, sizeof *gc); |
674 | 137 | } |
675 | | |
676 | | /* If not in the alternate screen, do nothing more. */ |
677 | 1.01k | if (!SCREEN_IS_ALTERNATE(s)) { |
678 | 671 | if (s->cx > screen_size_x(s) - 1) |
679 | 66 | s->cx = screen_size_x(s) - 1; |
680 | 671 | if (s->cy > screen_size_y(s) - 1) |
681 | 0 | s->cy = screen_size_y(s) - 1; |
682 | 671 | return; |
683 | 671 | } |
684 | | |
685 | | /* Restore the saved grid. */ |
686 | 341 | grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, |
687 | 341 | s->saved_grid->sy); |
688 | | |
689 | | /* |
690 | | * Turn history back on (so resize can use it) and then resize back to |
691 | | * the current size. |
692 | | */ |
693 | 341 | if (s->saved_flags & GRID_HISTORY) |
694 | 0 | s->grid->flags |= GRID_HISTORY; |
695 | 341 | screen_resize(s, sx, sy, 1); |
696 | | |
697 | 341 | grid_destroy(s->saved_grid); |
698 | 341 | s->saved_grid = NULL; |
699 | | |
700 | 341 | if (s->cx > screen_size_x(s) - 1) |
701 | 69 | s->cx = screen_size_x(s) - 1; |
702 | 341 | if (s->cy > screen_size_y(s) - 1) |
703 | 0 | s->cy = screen_size_y(s) - 1; |
704 | 341 | } |
705 | | |
706 | | /* Get mode as a string. */ |
707 | | const char * |
708 | | screen_mode_to_string(int mode) |
709 | 0 | { |
710 | 0 | static char tmp[1024]; |
711 | |
|
712 | 0 | if (mode == 0) |
713 | 0 | return ("NONE"); |
714 | 0 | if (mode == ALL_MODES) |
715 | 0 | return ("ALL"); |
716 | | |
717 | 0 | *tmp = '\0'; |
718 | 0 | if (mode & MODE_CURSOR) |
719 | 0 | strlcat(tmp, "CURSOR,", sizeof tmp); |
720 | 0 | if (mode & MODE_INSERT) |
721 | 0 | strlcat(tmp, "INSERT,", sizeof tmp); |
722 | 0 | if (mode & MODE_KCURSOR) |
723 | 0 | strlcat(tmp, "KCURSOR,", sizeof tmp); |
724 | 0 | if (mode & MODE_KKEYPAD) |
725 | 0 | strlcat(tmp, "KKEYPAD,", sizeof tmp); |
726 | 0 | if (mode & MODE_WRAP) |
727 | 0 | strlcat(tmp, "WRAP,", sizeof tmp); |
728 | 0 | if (mode & MODE_MOUSE_STANDARD) |
729 | 0 | strlcat(tmp, "MOUSE_STANDARD,", sizeof tmp); |
730 | 0 | if (mode & MODE_MOUSE_BUTTON) |
731 | 0 | strlcat(tmp, "MOUSE_BUTTON,", sizeof tmp); |
732 | 0 | if (mode & MODE_CURSOR_BLINKING) |
733 | 0 | strlcat(tmp, "CURSOR_BLINKING,", sizeof tmp); |
734 | 0 | if (mode & MODE_CURSOR_VERY_VISIBLE) |
735 | 0 | strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); |
736 | 0 | if (mode & MODE_MOUSE_UTF8) |
737 | 0 | strlcat(tmp, "MOUSE_UTF8,", sizeof tmp); |
738 | 0 | if (mode & MODE_MOUSE_SGR) |
739 | 0 | strlcat(tmp, "MOUSE_SGR,", sizeof tmp); |
740 | 0 | if (mode & MODE_BRACKETPASTE) |
741 | 0 | strlcat(tmp, "BRACKETPASTE,", sizeof tmp); |
742 | 0 | if (mode & MODE_FOCUSON) |
743 | 0 | strlcat(tmp, "FOCUSON,", sizeof tmp); |
744 | 0 | if (mode & MODE_MOUSE_ALL) |
745 | 0 | strlcat(tmp, "MOUSE_ALL,", sizeof tmp); |
746 | 0 | if (mode & MODE_ORIGIN) |
747 | 0 | strlcat(tmp, "ORIGIN,", sizeof tmp); |
748 | 0 | if (mode & MODE_CRLF) |
749 | 0 | strlcat(tmp, "CRLF,", sizeof tmp); |
750 | 0 | if (mode & MODE_KEYS_EXTENDED) |
751 | 0 | strlcat(tmp, "KEYS_EXTENDED,", sizeof tmp); |
752 | 0 | if (mode & MODE_KEYS_EXTENDED_2) |
753 | 0 | strlcat(tmp, "KEYS_EXTENDED_2,", sizeof tmp); |
754 | 0 | tmp[strlen(tmp) - 1] = '\0'; |
755 | 0 | return (tmp); |
756 | 0 | } |