1 |
|
|
/* $OpenBSD: tty.c,v 1.296 2017/10/16 19:30:53 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 |
|
|
#include <sys/ioctl.h> |
21 |
|
|
|
22 |
|
|
#include <netinet/in.h> |
23 |
|
|
|
24 |
|
|
#include <curses.h> |
25 |
|
|
#include <errno.h> |
26 |
|
|
#include <fcntl.h> |
27 |
|
|
#include <resolv.h> |
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <string.h> |
30 |
|
|
#include <termios.h> |
31 |
|
|
#include <unistd.h> |
32 |
|
|
|
33 |
|
|
#include "tmux.h" |
34 |
|
|
|
35 |
|
|
static int tty_log_fd = -1; |
36 |
|
|
|
37 |
|
|
static int tty_client_ready(struct client *, struct window_pane *); |
38 |
|
|
|
39 |
|
|
static void tty_set_italics(struct tty *); |
40 |
|
|
static int tty_try_colour(struct tty *, int, const char *); |
41 |
|
|
static void tty_force_cursor_colour(struct tty *, const char *); |
42 |
|
|
static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, |
43 |
|
|
u_int); |
44 |
|
|
static void tty_cursor_pane_unless_wrap(struct tty *, |
45 |
|
|
const struct tty_ctx *, u_int, u_int); |
46 |
|
|
static void tty_invalidate(struct tty *); |
47 |
|
|
static void tty_colours(struct tty *, const struct grid_cell *); |
48 |
|
|
static void tty_check_fg(struct tty *, const struct window_pane *, |
49 |
|
|
struct grid_cell *); |
50 |
|
|
static void tty_check_bg(struct tty *, const struct window_pane *, |
51 |
|
|
struct grid_cell *); |
52 |
|
|
static void tty_colours_fg(struct tty *, const struct grid_cell *); |
53 |
|
|
static void tty_colours_bg(struct tty *, const struct grid_cell *); |
54 |
|
|
|
55 |
|
|
static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, |
56 |
|
|
u_int); |
57 |
|
|
static void tty_region(struct tty *, u_int, u_int); |
58 |
|
|
static void tty_margin_pane(struct tty *, const struct tty_ctx *); |
59 |
|
|
static void tty_margin(struct tty *, u_int, u_int); |
60 |
|
|
static int tty_large_region(struct tty *, const struct tty_ctx *); |
61 |
|
|
static int tty_fake_bce(const struct tty *, const struct window_pane *, |
62 |
|
|
u_int); |
63 |
|
|
static void tty_redraw_region(struct tty *, const struct tty_ctx *); |
64 |
|
|
static void tty_emulate_repeat(struct tty *, enum tty_code_code, |
65 |
|
|
enum tty_code_code, u_int); |
66 |
|
|
static void tty_repeat_space(struct tty *, u_int); |
67 |
|
|
static void tty_cell(struct tty *, const struct grid_cell *, |
68 |
|
|
const struct window_pane *); |
69 |
|
|
static void tty_default_colours(struct grid_cell *, |
70 |
|
|
const struct window_pane *); |
71 |
|
|
static void tty_default_attributes(struct tty *, const struct window_pane *, |
72 |
|
|
u_int); |
73 |
|
|
|
74 |
|
|
#define tty_use_margin(tty) \ |
75 |
|
|
((tty)->term_type == TTY_VT420) |
76 |
|
|
|
77 |
|
|
#define tty_pane_full_width(tty, ctx) \ |
78 |
|
|
((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx) |
79 |
|
|
|
80 |
|
|
#define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) |
81 |
|
|
#define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) |
82 |
|
|
#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) |
83 |
|
|
|
84 |
|
|
void |
85 |
|
|
tty_create_log(void) |
86 |
|
|
{ |
87 |
|
|
char name[64]; |
88 |
|
|
|
89 |
|
|
xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); |
90 |
|
|
|
91 |
|
|
tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); |
92 |
|
|
if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) |
93 |
|
|
fatal("fcntl failed"); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
int |
97 |
|
|
tty_init(struct tty *tty, struct client *c, int fd, char *term) |
98 |
|
|
{ |
99 |
|
|
if (!isatty(fd)) |
100 |
|
|
return (-1); |
101 |
|
|
|
102 |
|
|
memset(tty, 0, sizeof *tty); |
103 |
|
|
|
104 |
|
|
if (term == NULL || *term == '\0') |
105 |
|
|
tty->term_name = xstrdup("unknown"); |
106 |
|
|
else |
107 |
|
|
tty->term_name = xstrdup(term); |
108 |
|
|
|
109 |
|
|
tty->fd = fd; |
110 |
|
|
tty->client = c; |
111 |
|
|
|
112 |
|
|
tty->cstyle = 0; |
113 |
|
|
tty->ccolour = xstrdup(""); |
114 |
|
|
|
115 |
|
|
tty->flags = 0; |
116 |
|
|
|
117 |
|
|
tty->term_flags = 0; |
118 |
|
|
tty->term_type = TTY_UNKNOWN; |
119 |
|
|
|
120 |
|
|
return (0); |
121 |
|
|
} |
122 |
|
|
|
123 |
|
|
void |
124 |
|
|
tty_resize(struct tty *tty) |
125 |
|
|
{ |
126 |
|
|
struct client *c = tty->client; |
127 |
|
|
struct winsize ws; |
128 |
|
|
u_int sx, sy; |
129 |
|
|
|
130 |
|
|
if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { |
131 |
|
|
sx = ws.ws_col; |
132 |
|
|
if (sx == 0) |
133 |
|
|
sx = 80; |
134 |
|
|
sy = ws.ws_row; |
135 |
|
|
if (sy == 0) |
136 |
|
|
sy = 24; |
137 |
|
|
} else { |
138 |
|
|
sx = 80; |
139 |
|
|
sy = 24; |
140 |
|
|
} |
141 |
|
|
log_debug("%s: %s now %ux%u", __func__, c->name, sx, sy); |
142 |
|
|
tty_set_size(tty, sx, sy); |
143 |
|
|
tty_invalidate(tty); |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
void |
147 |
|
|
tty_set_size(struct tty *tty, u_int sx, u_int sy) |
148 |
|
|
{ |
149 |
|
|
tty->sx = sx; |
150 |
|
|
tty->sy = sy; |
151 |
|
|
} |
152 |
|
|
|
153 |
|
|
static void |
154 |
|
|
tty_read_callback(__unused int fd, __unused short events, void *data) |
155 |
|
|
{ |
156 |
|
|
struct tty *tty = data; |
157 |
|
|
struct client *c = tty->client; |
158 |
|
|
size_t size = EVBUFFER_LENGTH(tty->in); |
159 |
|
|
int nread; |
160 |
|
|
|
161 |
|
|
nread = evbuffer_read(tty->in, tty->fd, -1); |
162 |
|
|
if (nread == 0 || nread == -1) { |
163 |
|
|
event_del(&tty->event_in); |
164 |
|
|
server_client_lost(tty->client); |
165 |
|
|
return; |
166 |
|
|
} |
167 |
|
|
log_debug("%s: read %d bytes (already %zu)", c->name, nread, size); |
168 |
|
|
|
169 |
|
|
while (tty_keys_next(tty)) |
170 |
|
|
; |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
static void |
174 |
|
|
tty_timer_callback(__unused int fd, __unused short events, void *data) |
175 |
|
|
{ |
176 |
|
|
struct tty *tty = data; |
177 |
|
|
struct client *c = tty->client; |
178 |
|
|
struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; |
179 |
|
|
|
180 |
|
|
log_debug("%s: %zu discarded", c->name, tty->discarded); |
181 |
|
|
|
182 |
|
|
c->flags |= CLIENT_REDRAW; |
183 |
|
|
c->discarded += tty->discarded; |
184 |
|
|
|
185 |
|
|
if (tty->discarded < TTY_BLOCK_STOP(tty)) { |
186 |
|
|
tty->flags &= ~TTY_BLOCK; |
187 |
|
|
tty_invalidate(tty); |
188 |
|
|
return; |
189 |
|
|
} |
190 |
|
|
tty->discarded = 0; |
191 |
|
|
evtimer_add(&tty->timer, &tv); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
static int |
195 |
|
|
tty_block_maybe(struct tty *tty) |
196 |
|
|
{ |
197 |
|
|
struct client *c = tty->client; |
198 |
|
|
size_t size = EVBUFFER_LENGTH(tty->out); |
199 |
|
|
struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; |
200 |
|
|
|
201 |
|
|
if (size < TTY_BLOCK_START(tty)) |
202 |
|
|
return (0); |
203 |
|
|
|
204 |
|
|
if (tty->flags & TTY_BLOCK) |
205 |
|
|
return (1); |
206 |
|
|
tty->flags |= TTY_BLOCK; |
207 |
|
|
|
208 |
|
|
log_debug("%s: can't keep up, %zu discarded", c->name, size); |
209 |
|
|
|
210 |
|
|
evbuffer_drain(tty->out, size); |
211 |
|
|
c->discarded += size; |
212 |
|
|
|
213 |
|
|
tty->discarded = 0; |
214 |
|
|
evtimer_add(&tty->timer, &tv); |
215 |
|
|
return (1); |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
static void |
219 |
|
|
tty_write_callback(__unused int fd, __unused short events, void *data) |
220 |
|
|
{ |
221 |
|
|
struct tty *tty = data; |
222 |
|
|
struct client *c = tty->client; |
223 |
|
|
size_t size = EVBUFFER_LENGTH(tty->out); |
224 |
|
|
int nwrite; |
225 |
|
|
|
226 |
|
|
nwrite = evbuffer_write(tty->out, tty->fd); |
227 |
|
|
if (nwrite == -1) |
228 |
|
|
return; |
229 |
|
|
log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size); |
230 |
|
|
|
231 |
|
|
if (c->redraw > 0) { |
232 |
|
|
if ((size_t)nwrite >= c->redraw) |
233 |
|
|
c->redraw = 0; |
234 |
|
|
else |
235 |
|
|
c->redraw -= nwrite; |
236 |
|
|
log_debug("%s: waiting for redraw, %zu bytes left", c->name, |
237 |
|
|
c->redraw); |
238 |
|
|
} else if (tty_block_maybe(tty)) |
239 |
|
|
return; |
240 |
|
|
|
241 |
|
|
if (EVBUFFER_LENGTH(tty->out) != 0) |
242 |
|
|
event_add(&tty->event_out, NULL); |
243 |
|
|
} |
244 |
|
|
|
245 |
|
|
int |
246 |
|
|
tty_open(struct tty *tty, char **cause) |
247 |
|
|
{ |
248 |
|
|
tty->term = tty_term_find(tty->term_name, tty->fd, cause); |
249 |
|
|
if (tty->term == NULL) { |
250 |
|
|
tty_close(tty); |
251 |
|
|
return (-1); |
252 |
|
|
} |
253 |
|
|
tty->flags |= TTY_OPENED; |
254 |
|
|
|
255 |
|
|
tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER); |
256 |
|
|
|
257 |
|
|
event_set(&tty->event_in, tty->fd, EV_PERSIST|EV_READ, |
258 |
|
|
tty_read_callback, tty); |
259 |
|
|
tty->in = evbuffer_new(); |
260 |
|
|
|
261 |
|
|
event_set(&tty->event_out, tty->fd, EV_WRITE, tty_write_callback, tty); |
262 |
|
|
tty->out = evbuffer_new(); |
263 |
|
|
|
264 |
|
|
evtimer_set(&tty->timer, tty_timer_callback, tty); |
265 |
|
|
|
266 |
|
|
tty_start_tty(tty); |
267 |
|
|
|
268 |
|
|
tty_keys_build(tty); |
269 |
|
|
|
270 |
|
|
return (0); |
271 |
|
|
} |
272 |
|
|
|
273 |
|
|
void |
274 |
|
|
tty_start_tty(struct tty *tty) |
275 |
|
|
{ |
276 |
|
|
struct client *c = tty->client; |
277 |
|
|
struct termios tio; |
278 |
|
|
|
279 |
|
|
if (tty->fd != -1 && tcgetattr(tty->fd, &tty->tio) == 0) { |
280 |
|
|
setblocking(tty->fd, 0); |
281 |
|
|
event_add(&tty->event_in, NULL); |
282 |
|
|
|
283 |
|
|
memcpy(&tio, &tty->tio, sizeof tio); |
284 |
|
|
tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); |
285 |
|
|
tio.c_iflag |= IGNBRK; |
286 |
|
|
tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); |
287 |
|
|
tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| |
288 |
|
|
ECHOPRT|ECHOKE|ISIG); |
289 |
|
|
tio.c_cc[VMIN] = 1; |
290 |
|
|
tio.c_cc[VTIME] = 0; |
291 |
|
|
if (tcsetattr(tty->fd, TCSANOW, &tio) == 0) |
292 |
|
|
tcflush(tty->fd, TCIOFLUSH); |
293 |
|
|
} |
294 |
|
|
|
295 |
|
|
tty_putcode(tty, TTYC_SMCUP); |
296 |
|
|
|
297 |
|
|
tty_putcode(tty, TTYC_SMKX); |
298 |
|
|
tty_putcode(tty, TTYC_CLEAR); |
299 |
|
|
|
300 |
|
|
if (tty_acs_needed(tty)) { |
301 |
|
|
log_debug("%s: using capabilities for ACS", c->name); |
302 |
|
|
tty_putcode(tty, TTYC_ENACS); |
303 |
|
|
} else |
304 |
|
|
log_debug("%s: using UTF-8 for ACS", c->name); |
305 |
|
|
|
306 |
|
|
tty_putcode(tty, TTYC_CNORM); |
307 |
|
|
if (tty_term_has(tty->term, TTYC_KMOUS)) |
308 |
|
|
tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); |
309 |
|
|
|
310 |
|
|
if (tty_term_flag(tty->term, TTYC_XT)) { |
311 |
|
|
if (options_get_number(global_options, "focus-events")) { |
312 |
|
|
tty->flags |= TTY_FOCUS; |
313 |
|
|
tty_puts(tty, "\033[?1004h"); |
314 |
|
|
} |
315 |
|
|
tty_puts(tty, "\033[c"); |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
tty->flags |= TTY_STARTED; |
319 |
|
|
tty_invalidate(tty); |
320 |
|
|
|
321 |
|
|
tty_force_cursor_colour(tty, ""); |
322 |
|
|
|
323 |
|
|
tty->mouse_drag_flag = 0; |
324 |
|
|
tty->mouse_drag_update = NULL; |
325 |
|
|
tty->mouse_drag_release = NULL; |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
void |
329 |
|
|
tty_stop_tty(struct tty *tty) |
330 |
|
|
{ |
331 |
|
|
struct winsize ws; |
332 |
|
|
|
333 |
|
|
if (!(tty->flags & TTY_STARTED)) |
334 |
|
|
return; |
335 |
|
|
tty->flags &= ~TTY_STARTED; |
336 |
|
|
|
337 |
|
|
event_del(&tty->timer); |
338 |
|
|
tty->flags &= ~TTY_BLOCK; |
339 |
|
|
|
340 |
|
|
event_del(&tty->event_in); |
341 |
|
|
event_del(&tty->event_out); |
342 |
|
|
|
343 |
|
|
/* |
344 |
|
|
* Be flexible about error handling and try not kill the server just |
345 |
|
|
* because the fd is invalid. Things like ssh -t can easily leave us |
346 |
|
|
* with a dead tty. |
347 |
|
|
*/ |
348 |
|
|
if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) |
349 |
|
|
return; |
350 |
|
|
if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) |
351 |
|
|
return; |
352 |
|
|
|
353 |
|
|
tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); |
354 |
|
|
if (tty_acs_needed(tty)) |
355 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); |
356 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); |
357 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); |
358 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); |
359 |
|
|
if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) { |
360 |
|
|
if (tty_term_has(tty->term, TTYC_SE)) |
361 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); |
362 |
|
|
else |
363 |
|
|
tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0)); |
364 |
|
|
} |
365 |
|
|
if (tty->mode & MODE_BRACKETPASTE) |
366 |
|
|
tty_raw(tty, "\033[?2004l"); |
367 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); |
368 |
|
|
|
369 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); |
370 |
|
|
if (tty_term_has(tty->term, TTYC_KMOUS)) |
371 |
|
|
tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l"); |
372 |
|
|
|
373 |
|
|
if (tty_term_flag(tty->term, TTYC_XT)) { |
374 |
|
|
if (tty->flags & TTY_FOCUS) { |
375 |
|
|
tty->flags &= ~TTY_FOCUS; |
376 |
|
|
tty_raw(tty, "\033[?1004l"); |
377 |
|
|
} |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
if (tty_use_margin(tty)) |
381 |
|
|
tty_raw(tty, "\033[?69l"); /* DECLRMM */ |
382 |
|
|
tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); |
383 |
|
|
|
384 |
|
|
setblocking(tty->fd, 1); |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
void |
388 |
|
|
tty_close(struct tty *tty) |
389 |
|
|
{ |
390 |
|
|
if (event_initialized(&tty->key_timer)) |
391 |
|
|
evtimer_del(&tty->key_timer); |
392 |
|
|
tty_stop_tty(tty); |
393 |
|
|
|
394 |
|
|
if (tty->flags & TTY_OPENED) { |
395 |
|
|
evbuffer_free(tty->in); |
396 |
|
|
event_del(&tty->event_in); |
397 |
|
|
evbuffer_free(tty->out); |
398 |
|
|
event_del(&tty->event_out); |
399 |
|
|
|
400 |
|
|
tty_term_free(tty->term); |
401 |
|
|
tty_keys_free(tty); |
402 |
|
|
|
403 |
|
|
tty->flags &= ~TTY_OPENED; |
404 |
|
|
} |
405 |
|
|
|
406 |
|
|
if (tty->fd != -1) { |
407 |
|
|
close(tty->fd); |
408 |
|
|
tty->fd = -1; |
409 |
|
|
} |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
void |
413 |
|
|
tty_free(struct tty *tty) |
414 |
|
|
{ |
415 |
|
|
tty_close(tty); |
416 |
|
|
|
417 |
|
|
free(tty->ccolour); |
418 |
|
|
free(tty->term_name); |
419 |
|
|
} |
420 |
|
|
|
421 |
|
|
void |
422 |
|
|
tty_set_type(struct tty *tty, int type) |
423 |
|
|
{ |
424 |
|
|
tty->term_type = type; |
425 |
|
|
|
426 |
|
|
if (tty_use_margin(tty)) |
427 |
|
|
tty_puts(tty, "\033[?69h"); /* DECLRMM */ |
428 |
|
|
} |
429 |
|
|
|
430 |
|
|
void |
431 |
|
|
tty_raw(struct tty *tty, const char *s) |
432 |
|
|
{ |
433 |
|
|
ssize_t n, slen; |
434 |
|
|
u_int i; |
435 |
|
|
|
436 |
|
|
slen = strlen(s); |
437 |
|
|
for (i = 0; i < 5; i++) { |
438 |
|
|
n = write(tty->fd, s, slen); |
439 |
|
|
if (n >= 0) { |
440 |
|
|
s += n; |
441 |
|
|
slen -= n; |
442 |
|
|
if (slen == 0) |
443 |
|
|
break; |
444 |
|
|
} else if (n == -1 && errno != EAGAIN) |
445 |
|
|
break; |
446 |
|
|
usleep(100); |
447 |
|
|
} |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
void |
451 |
|
|
tty_putcode(struct tty *tty, enum tty_code_code code) |
452 |
|
|
{ |
453 |
|
|
tty_puts(tty, tty_term_string(tty->term, code)); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
void |
457 |
|
|
tty_putcode1(struct tty *tty, enum tty_code_code code, int a) |
458 |
|
|
{ |
459 |
|
|
if (a < 0) |
460 |
|
|
return; |
461 |
|
|
tty_puts(tty, tty_term_string1(tty->term, code, a)); |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
void |
465 |
|
|
tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b) |
466 |
|
|
{ |
467 |
|
|
if (a < 0 || b < 0) |
468 |
|
|
return; |
469 |
|
|
tty_puts(tty, tty_term_string2(tty->term, code, a, b)); |
470 |
|
|
} |
471 |
|
|
|
472 |
|
|
void |
473 |
|
|
tty_putcode3(struct tty *tty, enum tty_code_code code, int a, int b, int c) |
474 |
|
|
{ |
475 |
|
|
if (a < 0 || b < 0 || c < 0) |
476 |
|
|
return; |
477 |
|
|
tty_puts(tty, tty_term_string3(tty->term, code, a, b, c)); |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
void |
481 |
|
|
tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a) |
482 |
|
|
{ |
483 |
|
|
if (a != NULL) |
484 |
|
|
tty_puts(tty, tty_term_ptr1(tty->term, code, a)); |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
void |
488 |
|
|
tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, |
489 |
|
|
const void *b) |
490 |
|
|
{ |
491 |
|
|
if (a != NULL && b != NULL) |
492 |
|
|
tty_puts(tty, tty_term_ptr2(tty->term, code, a, b)); |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
static void |
496 |
|
|
tty_add(struct tty *tty, const char *buf, size_t len) |
497 |
|
|
{ |
498 |
|
|
struct client *c = tty->client; |
499 |
|
|
|
500 |
|
|
if (tty->flags & TTY_BLOCK) { |
501 |
|
|
tty->discarded += len; |
502 |
|
|
return; |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
evbuffer_add(tty->out, buf, len); |
506 |
|
|
log_debug("%s: %.*s", c->name, (int)len, buf); |
507 |
|
|
c->written += len; |
508 |
|
|
|
509 |
|
|
if (tty_log_fd != -1) |
510 |
|
|
write(tty_log_fd, buf, len); |
511 |
|
|
if (tty->flags & TTY_STARTED) |
512 |
|
|
event_add(&tty->event_out, NULL); |
513 |
|
|
} |
514 |
|
|
|
515 |
|
|
void |
516 |
|
|
tty_puts(struct tty *tty, const char *s) |
517 |
|
|
{ |
518 |
|
|
if (*s != '\0') |
519 |
|
|
tty_add(tty, s, strlen(s)); |
520 |
|
|
} |
521 |
|
|
|
522 |
|
|
void |
523 |
|
|
tty_putc(struct tty *tty, u_char ch) |
524 |
|
|
{ |
525 |
|
|
const char *acs; |
526 |
|
|
|
527 |
|
|
if (tty->cell.attr & GRID_ATTR_CHARSET) { |
528 |
|
|
acs = tty_acs_get(tty, ch); |
529 |
|
|
if (acs != NULL) |
530 |
|
|
tty_add(tty, acs, strlen(acs)); |
531 |
|
|
else |
532 |
|
|
tty_add(tty, &ch, 1); |
533 |
|
|
} else |
534 |
|
|
tty_add(tty, &ch, 1); |
535 |
|
|
|
536 |
|
|
if (ch >= 0x20 && ch != 0x7f) { |
537 |
|
|
if (tty->cx >= tty->sx) { |
538 |
|
|
tty->cx = 1; |
539 |
|
|
if (tty->cy != tty->rlower) |
540 |
|
|
tty->cy++; |
541 |
|
|
|
542 |
|
|
/* |
543 |
|
|
* On !xenl terminals, force the cursor position to |
544 |
|
|
* where we think it should be after a line wrap - this |
545 |
|
|
* means it works on sensible terminals as well. |
546 |
|
|
*/ |
547 |
|
|
if (tty->term->flags & TERM_EARLYWRAP) |
548 |
|
|
tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx); |
549 |
|
|
} else |
550 |
|
|
tty->cx++; |
551 |
|
|
} |
552 |
|
|
} |
553 |
|
|
|
554 |
|
|
void |
555 |
|
|
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) |
556 |
|
|
{ |
557 |
|
|
tty_add(tty, buf, len); |
558 |
|
|
if (tty->cx + width > tty->sx) { |
559 |
|
|
tty->cx = (tty->cx + width) - tty->sx; |
560 |
|
|
if (tty->cx <= tty->sx) |
561 |
|
|
tty->cy++; |
562 |
|
|
else |
563 |
|
|
tty->cx = tty->cy = UINT_MAX; |
564 |
|
|
} else |
565 |
|
|
tty->cx += width; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
static void |
569 |
|
|
tty_set_italics(struct tty *tty) |
570 |
|
|
{ |
571 |
|
|
const char *s; |
572 |
|
|
|
573 |
|
|
if (tty_term_has(tty->term, TTYC_SITM)) { |
574 |
|
|
s = options_get_string(global_options, "default-terminal"); |
575 |
|
|
if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { |
576 |
|
|
tty_putcode(tty, TTYC_SITM); |
577 |
|
|
return; |
578 |
|
|
} |
579 |
|
|
} |
580 |
|
|
tty_putcode(tty, TTYC_SMSO); |
581 |
|
|
} |
582 |
|
|
|
583 |
|
|
void |
584 |
|
|
tty_set_title(struct tty *tty, const char *title) |
585 |
|
|
{ |
586 |
|
|
if (!tty_term_has(tty->term, TTYC_TSL) || |
587 |
|
|
!tty_term_has(tty->term, TTYC_FSL)) |
588 |
|
|
return; |
589 |
|
|
|
590 |
|
|
tty_putcode(tty, TTYC_TSL); |
591 |
|
|
tty_puts(tty, title); |
592 |
|
|
tty_putcode(tty, TTYC_FSL); |
593 |
|
|
} |
594 |
|
|
|
595 |
|
|
static void |
596 |
|
|
tty_force_cursor_colour(struct tty *tty, const char *ccolour) |
597 |
|
|
{ |
598 |
|
|
if (*ccolour == '\0') |
599 |
|
|
tty_putcode(tty, TTYC_CR); |
600 |
|
|
else |
601 |
|
|
tty_putcode_ptr1(tty, TTYC_CS, ccolour); |
602 |
|
|
free(tty->ccolour); |
603 |
|
|
tty->ccolour = xstrdup(ccolour); |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
void |
607 |
|
|
tty_update_mode(struct tty *tty, int mode, struct screen *s) |
608 |
|
|
{ |
609 |
|
|
int changed; |
610 |
|
|
|
611 |
|
|
if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0) |
612 |
|
|
tty_force_cursor_colour(tty, s->ccolour); |
613 |
|
|
|
614 |
|
|
if (tty->flags & TTY_NOCURSOR) |
615 |
|
|
mode &= ~MODE_CURSOR; |
616 |
|
|
|
617 |
|
|
changed = mode ^ tty->mode; |
618 |
|
|
if (changed & MODE_BLINKING) { |
619 |
|
|
if (tty_term_has(tty->term, TTYC_CVVIS)) |
620 |
|
|
tty_putcode(tty, TTYC_CVVIS); |
621 |
|
|
else |
622 |
|
|
tty_putcode(tty, TTYC_CNORM); |
623 |
|
|
changed |= MODE_CURSOR; |
624 |
|
|
} |
625 |
|
|
if (changed & MODE_CURSOR) { |
626 |
|
|
if (mode & MODE_CURSOR) |
627 |
|
|
tty_putcode(tty, TTYC_CNORM); |
628 |
|
|
else |
629 |
|
|
tty_putcode(tty, TTYC_CIVIS); |
630 |
|
|
} |
631 |
|
|
if (s != NULL && tty->cstyle != s->cstyle) { |
632 |
|
|
if (tty_term_has(tty->term, TTYC_SS)) { |
633 |
|
|
if (s->cstyle == 0 && |
634 |
|
|
tty_term_has(tty->term, TTYC_SE)) |
635 |
|
|
tty_putcode(tty, TTYC_SE); |
636 |
|
|
else |
637 |
|
|
tty_putcode1(tty, TTYC_SS, s->cstyle); |
638 |
|
|
} |
639 |
|
|
tty->cstyle = s->cstyle; |
640 |
|
|
} |
641 |
|
|
if (changed & ALL_MOUSE_MODES) { |
642 |
|
|
if (mode & ALL_MOUSE_MODES) { |
643 |
|
|
/* |
644 |
|
|
* Enable the SGR (1006) extension unconditionally, as |
645 |
|
|
* it is safe from misinterpretation. |
646 |
|
|
*/ |
647 |
|
|
tty_puts(tty, "\033[?1006h"); |
648 |
|
|
if (mode & MODE_MOUSE_ALL) |
649 |
|
|
tty_puts(tty, "\033[?1003h"); |
650 |
|
|
else if (mode & MODE_MOUSE_BUTTON) |
651 |
|
|
tty_puts(tty, "\033[?1002h"); |
652 |
|
|
else if (mode & MODE_MOUSE_STANDARD) |
653 |
|
|
tty_puts(tty, "\033[?1000h"); |
654 |
|
|
} else { |
655 |
|
|
if (tty->mode & MODE_MOUSE_ALL) |
656 |
|
|
tty_puts(tty, "\033[?1003l"); |
657 |
|
|
else if (tty->mode & MODE_MOUSE_BUTTON) |
658 |
|
|
tty_puts(tty, "\033[?1002l"); |
659 |
|
|
else if (tty->mode & MODE_MOUSE_STANDARD) |
660 |
|
|
tty_puts(tty, "\033[?1000l"); |
661 |
|
|
tty_puts(tty, "\033[?1006l"); |
662 |
|
|
} |
663 |
|
|
} |
664 |
|
|
if (changed & MODE_BRACKETPASTE) { |
665 |
|
|
if (mode & MODE_BRACKETPASTE) |
666 |
|
|
tty_puts(tty, "\033[?2004h"); |
667 |
|
|
else |
668 |
|
|
tty_puts(tty, "\033[?2004l"); |
669 |
|
|
} |
670 |
|
|
tty->mode = mode; |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
static void |
674 |
|
|
tty_emulate_repeat(struct tty *tty, enum tty_code_code code, |
675 |
|
|
enum tty_code_code code1, u_int n) |
676 |
|
|
{ |
677 |
|
|
if (tty_term_has(tty->term, code)) |
678 |
|
|
tty_putcode1(tty, code, n); |
679 |
|
|
else { |
680 |
|
|
while (n-- > 0) |
681 |
|
|
tty_putcode(tty, code1); |
682 |
|
|
} |
683 |
|
|
} |
684 |
|
|
|
685 |
|
|
static void |
686 |
|
|
tty_repeat_space(struct tty *tty, u_int n) |
687 |
|
|
{ |
688 |
|
|
static char s[500]; |
689 |
|
|
|
690 |
|
|
if (*s != ' ') |
691 |
|
|
memset(s, ' ', sizeof s); |
692 |
|
|
|
693 |
|
|
while (n > sizeof s) { |
694 |
|
|
tty_putn(tty, s, sizeof s, sizeof s); |
695 |
|
|
n -= sizeof s; |
696 |
|
|
} |
697 |
|
|
if (n != 0) |
698 |
|
|
tty_putn(tty, s, n, n); |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
/* |
702 |
|
|
* Is the region large enough to be worth redrawing once later rather than |
703 |
|
|
* probably several times now? Currently yes if it is more than 50% of the |
704 |
|
|
* pane. |
705 |
|
|
*/ |
706 |
|
|
static int |
707 |
|
|
tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) |
708 |
|
|
{ |
709 |
|
|
struct window_pane *wp = ctx->wp; |
710 |
|
|
|
711 |
|
|
return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2); |
712 |
|
|
} |
713 |
|
|
|
714 |
|
|
/* |
715 |
|
|
* Return if BCE is needed but the terminal doesn't have it - it'll need to be |
716 |
|
|
* emulated. |
717 |
|
|
*/ |
718 |
|
|
static int |
719 |
|
|
tty_fake_bce(const struct tty *tty, const struct window_pane *wp, u_int bg) |
720 |
|
|
{ |
721 |
|
|
struct grid_cell gc; |
722 |
|
|
|
723 |
|
|
if (tty_term_flag(tty->term, TTYC_BCE)) |
724 |
|
|
return (0); |
725 |
|
|
|
726 |
|
|
memcpy(&gc, &grid_default_cell, sizeof gc); |
727 |
|
|
if (wp != NULL) |
728 |
|
|
tty_default_colours(&gc, wp); |
729 |
|
|
|
730 |
|
|
if (bg != 8 || gc.bg != 8) |
731 |
|
|
return (1); |
732 |
|
|
return (0); |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
/* |
736 |
|
|
* Redraw scroll region using data from screen (already updated). Used when |
737 |
|
|
* CSR not supported, or window is a pane that doesn't take up the full |
738 |
|
|
* width of the terminal. |
739 |
|
|
*/ |
740 |
|
|
static void |
741 |
|
|
tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) |
742 |
|
|
{ |
743 |
|
|
struct window_pane *wp = ctx->wp; |
744 |
|
|
struct screen *s = wp->screen; |
745 |
|
|
u_int i; |
746 |
|
|
|
747 |
|
|
/* |
748 |
|
|
* If region is large, schedule a window redraw. In most cases this is |
749 |
|
|
* likely to be followed by some more scrolling. |
750 |
|
|
*/ |
751 |
|
|
if (tty_large_region(tty, ctx)) { |
752 |
|
|
wp->flags |= PANE_REDRAW; |
753 |
|
|
return; |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { |
757 |
|
|
for (i = ctx->ocy; i < screen_size_y(s); i++) |
758 |
|
|
tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); |
759 |
|
|
} else { |
760 |
|
|
for (i = ctx->orupper; i <= ctx->orlower; i++) |
761 |
|
|
tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff); |
762 |
|
|
} |
763 |
|
|
} |
764 |
|
|
|
765 |
|
|
static void |
766 |
|
|
tty_clear_line(struct tty *tty, const struct window_pane *wp, u_int py, |
767 |
|
|
u_int px, u_int nx, u_int bg) |
768 |
|
|
{ |
769 |
|
|
log_debug("%s: %u at %u,%u", __func__, nx, px, py); |
770 |
|
|
|
771 |
|
|
/* Nothing to clear. */ |
772 |
|
|
if (nx == 0) |
773 |
|
|
return; |
774 |
|
|
|
775 |
|
|
/* If genuine BCE is available, can try escape sequences. */ |
776 |
|
|
if (!tty_fake_bce(tty, wp, bg)) { |
777 |
|
|
/* Off the end of the line, use EL if available. */ |
778 |
|
|
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { |
779 |
|
|
tty_cursor(tty, px, py); |
780 |
|
|
tty_putcode(tty, TTYC_EL); |
781 |
|
|
return; |
782 |
|
|
} |
783 |
|
|
|
784 |
|
|
/* At the start of the line. Use EL1. */ |
785 |
|
|
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) { |
786 |
|
|
tty_cursor(tty, px + nx - 1, py); |
787 |
|
|
tty_putcode(tty, TTYC_EL1); |
788 |
|
|
return; |
789 |
|
|
} |
790 |
|
|
|
791 |
|
|
/* Section of line. Use ECH if possible. */ |
792 |
|
|
if (tty_term_has(tty->term, TTYC_ECH)) { |
793 |
|
|
tty_cursor(tty, px, py); |
794 |
|
|
tty_putcode1(tty, TTYC_ECH, nx); |
795 |
|
|
return; |
796 |
|
|
} |
797 |
|
|
} |
798 |
|
|
|
799 |
|
|
/* Couldn't use an escape sequence, use spaces. */ |
800 |
|
|
tty_cursor(tty, px, py); |
801 |
|
|
tty_repeat_space(tty, nx); |
802 |
|
|
} |
803 |
|
|
|
804 |
|
|
static void |
805 |
|
|
tty_clear_area(struct tty *tty, const struct window_pane *wp, u_int py, |
806 |
|
|
u_int ny, u_int px, u_int nx, u_int bg) |
807 |
|
|
{ |
808 |
|
|
u_int yy; |
809 |
|
|
char tmp[64]; |
810 |
|
|
|
811 |
|
|
log_debug("%s: %u,%u at %u,%u", __func__, nx, ny, px, py); |
812 |
|
|
|
813 |
|
|
/* Nothing to clear. */ |
814 |
|
|
if (nx == 0 || ny == 0) |
815 |
|
|
return; |
816 |
|
|
|
817 |
|
|
/* If genuine BCE is available, can try escape sequences. */ |
818 |
|
|
if (!tty_fake_bce(tty, wp, bg)) { |
819 |
|
|
/* Use ED if clearing off the bottom of the terminal. */ |
820 |
|
|
if (px == 0 && |
821 |
|
|
px + nx >= tty->sx && |
822 |
|
|
py + ny >= tty->sy && |
823 |
|
|
tty_term_has(tty->term, TTYC_ED)) { |
824 |
|
|
tty_cursor(tty, 0, py); |
825 |
|
|
tty_putcode(tty, TTYC_ED); |
826 |
|
|
return; |
827 |
|
|
} |
828 |
|
|
|
829 |
|
|
/* |
830 |
|
|
* On VT420 compatible terminals we can use DECFRA if the |
831 |
|
|
* background colour isn't default (because it doesn't work |
832 |
|
|
* after SGR 0). |
833 |
|
|
*/ |
834 |
|
|
if (tty->term_type == TTY_VT420 && bg != 8) { |
835 |
|
|
xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", |
836 |
|
|
py + 1, px + 1, py + ny, px + nx); |
837 |
|
|
tty_puts(tty, tmp); |
838 |
|
|
return; |
839 |
|
|
} |
840 |
|
|
|
841 |
|
|
/* Full lines can be scrolled away to clear them. */ |
842 |
|
|
if (px == 0 && |
843 |
|
|
px + nx >= tty->sx && |
844 |
|
|
ny > 2 && |
845 |
|
|
tty_term_has(tty->term, TTYC_CSR) && |
846 |
|
|
tty_term_has(tty->term, TTYC_INDN)) { |
847 |
|
|
tty_region(tty, py, py + ny - 1); |
848 |
|
|
tty_margin_off(tty); |
849 |
|
|
tty_putcode1(tty, TTYC_INDN, ny); |
850 |
|
|
return; |
851 |
|
|
} |
852 |
|
|
|
853 |
|
|
/* |
854 |
|
|
* If margins are supported, can just scroll the area off to |
855 |
|
|
* clear it. |
856 |
|
|
*/ |
857 |
|
|
if (nx > 2 && |
858 |
|
|
ny > 2 && |
859 |
|
|
tty_term_has(tty->term, TTYC_CSR) && |
860 |
|
|
tty_use_margin(tty) && |
861 |
|
|
tty_term_has(tty->term, TTYC_INDN)) { |
862 |
|
|
tty_region(tty, py, py + ny - 1); |
863 |
|
|
tty_margin(tty, px, px + nx - 1); |
864 |
|
|
tty_putcode1(tty, TTYC_INDN, ny); |
865 |
|
|
return; |
866 |
|
|
} |
867 |
|
|
} |
868 |
|
|
|
869 |
|
|
/* Couldn't use an escape sequence, loop over the lines. */ |
870 |
|
|
for (yy = py; yy < py + ny; yy++) |
871 |
|
|
tty_clear_line(tty, wp, yy, px, nx, bg); |
872 |
|
|
} |
873 |
|
|
|
874 |
|
|
void |
875 |
|
|
tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox, |
876 |
|
|
u_int oy) |
877 |
|
|
{ |
878 |
|
|
tty_draw_line(tty, wp, wp->screen, py, ox, oy); |
879 |
|
|
} |
880 |
|
|
|
881 |
|
|
void |
882 |
|
|
tty_draw_line(struct tty *tty, const struct window_pane *wp, |
883 |
|
|
struct screen *s, u_int py, u_int ox, u_int oy) |
884 |
|
|
{ |
885 |
|
|
struct grid_cell gc, last; |
886 |
|
|
u_int i, j, ux, sx, nx, width; |
887 |
|
|
int flags, cleared = 0; |
888 |
|
|
char buf[512]; |
889 |
|
|
size_t len, old_len; |
890 |
|
|
|
891 |
|
|
flags = (tty->flags & TTY_NOCURSOR); |
892 |
|
|
tty->flags |= TTY_NOCURSOR; |
893 |
|
|
tty_update_mode(tty, tty->mode, s); |
894 |
|
|
|
895 |
|
|
tty_region_off(tty); |
896 |
|
|
tty_margin_off(tty); |
897 |
|
|
|
898 |
|
|
/* |
899 |
|
|
* Clamp the width to cellsize - note this is not cellused, because |
900 |
|
|
* there may be empty background cells after it (from BCE). |
901 |
|
|
*/ |
902 |
|
|
sx = screen_size_x(s); |
903 |
|
|
if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) |
904 |
|
|
sx = s->grid->linedata[s->grid->hsize + py].cellsize; |
905 |
|
|
if (sx > tty->sx) |
906 |
|
|
sx = tty->sx; |
907 |
|
|
ux = 0; |
908 |
|
|
|
909 |
|
|
if (wp == NULL || |
910 |
|
|
py == 0 || |
911 |
|
|
(~s->grid->linedata[s->grid->hsize + py - 1].flags & GRID_LINE_WRAPPED) || |
912 |
|
|
ox != 0 || |
913 |
|
|
tty->cx < tty->sx || |
914 |
|
|
screen_size_x(s) < tty->sx) { |
915 |
|
|
if (screen_size_x(s) < tty->sx && |
916 |
|
|
ox == 0 && |
917 |
|
|
sx != screen_size_x(s) && |
918 |
|
|
tty_term_has(tty->term, TTYC_EL1) && |
919 |
|
|
!tty_fake_bce(tty, wp, 8)) { |
920 |
|
|
tty_default_attributes(tty, wp, 8); |
921 |
|
|
tty_cursor(tty, screen_size_x(s) - 1, oy + py); |
922 |
|
|
tty_putcode(tty, TTYC_EL1); |
923 |
|
|
cleared = 1; |
924 |
|
|
} |
925 |
|
|
if (sx != 0) |
926 |
|
|
tty_cursor(tty, ox, oy + py); |
927 |
|
|
} else |
928 |
|
|
log_debug("%s: wrapped line %u", __func__, oy + py); |
929 |
|
|
|
930 |
|
|
memcpy(&last, &grid_default_cell, sizeof last); |
931 |
|
|
len = 0; |
932 |
|
|
width = 0; |
933 |
|
|
|
934 |
|
|
for (i = 0; i < sx; i++) { |
935 |
|
|
grid_view_get_cell(s->grid, i, py, &gc); |
936 |
|
|
if (len != 0 && |
937 |
|
|
(((~tty->flags & TTY_UTF8) && |
938 |
|
|
(gc.data.size != 1 || |
939 |
|
|
*gc.data.data >= 0x7f || |
940 |
|
|
gc.data.width != 1)) || |
941 |
|
|
(gc.attr & GRID_ATTR_CHARSET) || |
942 |
|
|
gc.flags != last.flags || |
943 |
|
|
gc.attr != last.attr || |
944 |
|
|
gc.fg != last.fg || |
945 |
|
|
gc.bg != last.bg || |
946 |
|
|
(sizeof buf) - len < gc.data.size)) { |
947 |
|
|
tty_attributes(tty, &last, wp); |
948 |
|
|
tty_putn(tty, buf, len, width); |
949 |
|
|
ux += width; |
950 |
|
|
|
951 |
|
|
len = 0; |
952 |
|
|
width = 0; |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
if (gc.flags & GRID_FLAG_SELECTED) |
956 |
|
|
screen_select_cell(s, &last, &gc); |
957 |
|
|
else |
958 |
|
|
memcpy(&last, &gc, sizeof last); |
959 |
|
|
if (((~tty->flags & TTY_UTF8) && |
960 |
|
|
(gc.data.size != 1 || |
961 |
|
|
*gc.data.data >= 0x7f || |
962 |
|
|
gc.data.width != 1)) || |
963 |
|
|
(gc.attr & GRID_ATTR_CHARSET)) { |
964 |
|
|
tty_attributes(tty, &last, wp); |
965 |
|
|
if (~tty->flags & TTY_UTF8) { |
966 |
|
|
for (j = 0; j < gc.data.width; j++) |
967 |
|
|
tty_putc(tty, '_'); |
968 |
|
|
} else { |
969 |
|
|
for (j = 0; j < gc.data.size; j++) |
970 |
|
|
tty_putc(tty, gc.data.data[j]); |
971 |
|
|
} |
972 |
|
|
ux += gc.data.width; |
973 |
|
|
} else { |
974 |
|
|
memcpy(buf + len, gc.data.data, gc.data.size); |
975 |
|
|
len += gc.data.size; |
976 |
|
|
width += gc.data.width; |
977 |
|
|
} |
978 |
|
|
} |
979 |
|
|
if (len != 0) { |
980 |
|
|
if (grid_cells_equal(&last, &grid_default_cell)) { |
981 |
|
|
old_len = len; |
982 |
|
|
while (len > 0 && buf[len - 1] == ' ') { |
983 |
|
|
len--; |
984 |
|
|
width--; |
985 |
|
|
} |
986 |
|
|
log_debug("%s: trimmed %zu spaces", __func__, |
987 |
|
|
old_len - len); |
988 |
|
|
} |
989 |
|
|
if (len != 0) { |
990 |
|
|
tty_attributes(tty, &last, wp); |
991 |
|
|
tty_putn(tty, buf, len, width); |
992 |
|
|
ux += width; |
993 |
|
|
} |
994 |
|
|
} |
995 |
|
|
|
996 |
|
|
nx = screen_size_x(s) - ux; |
997 |
|
|
if (!cleared && ux < tty->sx && nx != 0) { |
998 |
|
|
tty_default_attributes(tty, wp, 8); |
999 |
|
|
tty_clear_line(tty, wp, oy + py, ox + ux, nx, 8); |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; |
1003 |
|
|
tty_update_mode(tty, tty->mode, s); |
1004 |
|
|
} |
1005 |
|
|
|
1006 |
|
|
static int |
1007 |
|
|
tty_client_ready(struct client *c, struct window_pane *wp) |
1008 |
|
|
{ |
1009 |
|
|
if (c->session == NULL || c->tty.term == NULL) |
1010 |
|
|
return (0); |
1011 |
|
|
if (c->flags & (CLIENT_REDRAW|CLIENT_SUSPENDED)) |
1012 |
|
|
return (0); |
1013 |
|
|
if (c->tty.flags & TTY_FREEZE) |
1014 |
|
|
return (0); |
1015 |
|
|
if (c->session->curw->window != wp->window) |
1016 |
|
|
return (0); |
1017 |
|
|
return (1); |
1018 |
|
|
} |
1019 |
|
|
|
1020 |
|
|
void |
1021 |
|
|
tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), |
1022 |
|
|
struct tty_ctx *ctx) |
1023 |
|
|
{ |
1024 |
|
|
struct window_pane *wp = ctx->wp; |
1025 |
|
|
struct client *c; |
1026 |
|
|
|
1027 |
|
|
/* wp can be NULL if updating the screen but not the terminal. */ |
1028 |
|
|
if (wp == NULL) |
1029 |
|
|
return; |
1030 |
|
|
|
1031 |
|
|
if ((wp->flags & (PANE_REDRAW|PANE_DROP)) || !window_pane_visible(wp)) |
1032 |
|
|
return; |
1033 |
|
|
|
1034 |
|
|
TAILQ_FOREACH(c, &clients, entry) { |
1035 |
|
|
if (!tty_client_ready(c, wp)) |
1036 |
|
|
continue; |
1037 |
|
|
|
1038 |
|
|
ctx->xoff = wp->xoff; |
1039 |
|
|
ctx->yoff = wp->yoff; |
1040 |
|
|
if (status_at_line(c) == 0) |
1041 |
|
|
ctx->yoff += status_line_size(c->session); |
1042 |
|
|
|
1043 |
|
|
cmdfn(&c->tty, ctx); |
1044 |
|
|
} |
1045 |
|
|
} |
1046 |
|
|
|
1047 |
|
|
void |
1048 |
|
|
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) |
1049 |
|
|
{ |
1050 |
|
|
struct window_pane *wp = ctx->wp; |
1051 |
|
|
|
1052 |
|
|
if (!tty_pane_full_width(tty, ctx) || |
1053 |
|
|
tty_fake_bce(tty, wp, ctx->bg) || |
1054 |
|
|
(!tty_term_has(tty->term, TTYC_ICH) && |
1055 |
|
|
!tty_term_has(tty->term, TTYC_ICH1))) { |
1056 |
|
|
tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); |
1057 |
|
|
return; |
1058 |
|
|
} |
1059 |
|
|
|
1060 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1061 |
|
|
|
1062 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1063 |
|
|
|
1064 |
|
|
tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); |
1065 |
|
|
} |
1066 |
|
|
|
1067 |
|
|
void |
1068 |
|
|
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) |
1069 |
|
|
{ |
1070 |
|
|
struct window_pane *wp = ctx->wp; |
1071 |
|
|
|
1072 |
|
|
if (!tty_pane_full_width(tty, ctx) || |
1073 |
|
|
tty_fake_bce(tty, wp, ctx->bg) || |
1074 |
|
|
(!tty_term_has(tty->term, TTYC_DCH) && |
1075 |
|
|
!tty_term_has(tty->term, TTYC_DCH1))) { |
1076 |
|
|
tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff); |
1077 |
|
|
return; |
1078 |
|
|
} |
1079 |
|
|
|
1080 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1081 |
|
|
|
1082 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1083 |
|
|
|
1084 |
|
|
tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); |
1085 |
|
|
} |
1086 |
|
|
|
1087 |
|
|
void |
1088 |
|
|
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) |
1089 |
|
|
{ |
1090 |
|
|
tty_default_attributes(tty, ctx->wp, ctx->bg); |
1091 |
|
|
|
1092 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1093 |
|
|
|
1094 |
|
|
if (tty_term_has(tty->term, TTYC_ECH) && |
1095 |
|
|
!tty_fake_bce(tty, ctx->wp, 8)) |
1096 |
|
|
tty_putcode1(tty, TTYC_ECH, ctx->num); |
1097 |
|
|
else |
1098 |
|
|
tty_repeat_space(tty, ctx->num); |
1099 |
|
|
} |
1100 |
|
|
|
1101 |
|
|
void |
1102 |
|
|
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) |
1103 |
|
|
{ |
1104 |
|
|
if (!tty_pane_full_width(tty, ctx) || |
1105 |
|
|
tty_fake_bce(tty, ctx->wp, ctx->bg) || |
1106 |
|
|
!tty_term_has(tty->term, TTYC_CSR) || |
1107 |
|
|
!tty_term_has(tty->term, TTYC_IL1)) { |
1108 |
|
|
tty_redraw_region(tty, ctx); |
1109 |
|
|
return; |
1110 |
|
|
} |
1111 |
|
|
|
1112 |
|
|
tty_default_attributes(tty, ctx->wp, ctx->bg); |
1113 |
|
|
|
1114 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1115 |
|
|
tty_margin_off(tty); |
1116 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1117 |
|
|
|
1118 |
|
|
tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); |
1119 |
|
|
tty->cx = tty->cy = UINT_MAX; |
1120 |
|
|
} |
1121 |
|
|
|
1122 |
|
|
void |
1123 |
|
|
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) |
1124 |
|
|
{ |
1125 |
|
|
if (!tty_pane_full_width(tty, ctx) || |
1126 |
|
|
tty_fake_bce(tty, ctx->wp, ctx->bg) || |
1127 |
|
|
!tty_term_has(tty->term, TTYC_CSR) || |
1128 |
|
|
!tty_term_has(tty->term, TTYC_DL1)) { |
1129 |
|
|
tty_redraw_region(tty, ctx); |
1130 |
|
|
return; |
1131 |
|
|
} |
1132 |
|
|
|
1133 |
|
|
tty_default_attributes(tty, ctx->wp, ctx->bg); |
1134 |
|
|
|
1135 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1136 |
|
|
tty_margin_off(tty); |
1137 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1138 |
|
|
|
1139 |
|
|
tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); |
1140 |
|
|
tty->cx = tty->cy = UINT_MAX; |
1141 |
|
|
} |
1142 |
|
|
|
1143 |
|
|
void |
1144 |
|
|
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) |
1145 |
|
|
{ |
1146 |
|
|
struct window_pane *wp = ctx->wp; |
1147 |
|
|
u_int nx, py = ctx->yoff + ctx->ocy; |
1148 |
|
|
|
1149 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1150 |
|
|
|
1151 |
|
|
nx = screen_size_x(wp->screen); |
1152 |
|
|
tty_clear_line(tty, wp, py, ctx->xoff, nx, ctx->bg); |
1153 |
|
|
} |
1154 |
|
|
|
1155 |
|
|
void |
1156 |
|
|
tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) |
1157 |
|
|
{ |
1158 |
|
|
struct window_pane *wp = ctx->wp; |
1159 |
|
|
u_int nx, py = ctx->yoff + ctx->ocy; |
1160 |
|
|
|
1161 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1162 |
|
|
|
1163 |
|
|
nx = screen_size_x(wp->screen) - ctx->ocx; |
1164 |
|
|
tty_clear_line(tty, wp, py, ctx->xoff + ctx->ocx, nx, ctx->bg); |
1165 |
|
|
} |
1166 |
|
|
|
1167 |
|
|
void |
1168 |
|
|
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) |
1169 |
|
|
{ |
1170 |
|
|
struct window_pane *wp = ctx->wp; |
1171 |
|
|
u_int py = ctx->yoff + ctx->ocy; |
1172 |
|
|
|
1173 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1174 |
|
|
|
1175 |
|
|
tty_clear_line(tty, wp, py, ctx->xoff, ctx->ocx + 1, ctx->bg); |
1176 |
|
|
} |
1177 |
|
|
|
1178 |
|
|
void |
1179 |
|
|
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) |
1180 |
|
|
{ |
1181 |
|
|
struct window_pane *wp = ctx->wp; |
1182 |
|
|
|
1183 |
|
|
if (ctx->ocy != ctx->orupper) |
1184 |
|
|
return; |
1185 |
|
|
|
1186 |
|
|
if (!tty_pane_full_width(tty, ctx) || |
1187 |
|
|
tty_fake_bce(tty, wp, 8) || |
1188 |
|
|
!tty_term_has(tty->term, TTYC_CSR) || |
1189 |
|
|
!tty_term_has(tty->term, TTYC_RI)) { |
1190 |
|
|
tty_redraw_region(tty, ctx); |
1191 |
|
|
return; |
1192 |
|
|
} |
1193 |
|
|
|
1194 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1195 |
|
|
|
1196 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1197 |
|
|
tty_margin_off(tty); |
1198 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); |
1199 |
|
|
|
1200 |
|
|
tty_putcode(tty, TTYC_RI); |
1201 |
|
|
} |
1202 |
|
|
|
1203 |
|
|
void |
1204 |
|
|
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) |
1205 |
|
|
{ |
1206 |
|
|
struct window_pane *wp = ctx->wp; |
1207 |
|
|
|
1208 |
|
|
if (ctx->ocy != ctx->orlower) |
1209 |
|
|
return; |
1210 |
|
|
|
1211 |
|
|
if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || |
1212 |
|
|
tty_fake_bce(tty, wp, 8) || |
1213 |
|
|
!tty_term_has(tty->term, TTYC_CSR)) { |
1214 |
|
|
tty_redraw_region(tty, ctx); |
1215 |
|
|
return; |
1216 |
|
|
} |
1217 |
|
|
|
1218 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1219 |
|
|
|
1220 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1221 |
|
|
tty_margin_pane(tty, ctx); |
1222 |
|
|
|
1223 |
|
|
/* |
1224 |
|
|
* If we want to wrap a pane, the cursor needs to be exactly on the |
1225 |
|
|
* right of the region. But if the pane isn't on the right, it may be |
1226 |
|
|
* off the edge - if so, move the cursor back to the right. |
1227 |
|
|
*/ |
1228 |
|
|
if (ctx->xoff + ctx->ocx > tty->rright) |
1229 |
|
|
tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy); |
1230 |
|
|
else |
1231 |
|
|
tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); |
1232 |
|
|
|
1233 |
|
|
tty_putc(tty, '\n'); |
1234 |
|
|
} |
1235 |
|
|
|
1236 |
|
|
void |
1237 |
|
|
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) |
1238 |
|
|
{ |
1239 |
|
|
struct window_pane *wp = ctx->wp; |
1240 |
|
|
u_int i; |
1241 |
|
|
|
1242 |
|
|
if ((!tty_pane_full_width(tty, ctx) && !tty_use_margin(tty)) || |
1243 |
|
|
tty_fake_bce(tty, wp, 8) || |
1244 |
|
|
!tty_term_has(tty->term, TTYC_CSR)) { |
1245 |
|
|
tty_redraw_region(tty, ctx); |
1246 |
|
|
return; |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1250 |
|
|
|
1251 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1252 |
|
|
tty_margin_pane(tty, ctx); |
1253 |
|
|
|
1254 |
|
|
if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) { |
1255 |
|
|
tty_cursor(tty, tty->rright, tty->rlower); |
1256 |
|
|
for (i = 0; i < ctx->num; i++) |
1257 |
|
|
tty_putc(tty, '\n'); |
1258 |
|
|
} else |
1259 |
|
|
tty_putcode1(tty, TTYC_INDN, ctx->num); |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
void |
1263 |
|
|
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) |
1264 |
|
|
{ |
1265 |
|
|
struct window_pane *wp = ctx->wp; |
1266 |
|
|
u_int px, py, nx, ny; |
1267 |
|
|
|
1268 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1269 |
|
|
|
1270 |
|
|
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); |
1271 |
|
|
tty_margin_off(tty); |
1272 |
|
|
|
1273 |
|
|
px = ctx->xoff; |
1274 |
|
|
nx = screen_size_x(wp->screen); |
1275 |
|
|
py = ctx->yoff + ctx->ocy + 1; |
1276 |
|
|
ny = screen_size_y(wp->screen) - ctx->ocy - 1; |
1277 |
|
|
|
1278 |
|
|
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); |
1279 |
|
|
|
1280 |
|
|
px = ctx->xoff + ctx->ocx; |
1281 |
|
|
nx = screen_size_x(wp->screen) - ctx->ocx; |
1282 |
|
|
py = ctx->yoff + ctx->ocy; |
1283 |
|
|
|
1284 |
|
|
tty_clear_line(tty, wp, py, px, nx, ctx->bg); |
1285 |
|
|
} |
1286 |
|
|
|
1287 |
|
|
void |
1288 |
|
|
tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) |
1289 |
|
|
{ |
1290 |
|
|
struct window_pane *wp = ctx->wp; |
1291 |
|
|
u_int px, py, nx, ny; |
1292 |
|
|
|
1293 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1294 |
|
|
|
1295 |
|
|
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); |
1296 |
|
|
tty_margin_off(tty); |
1297 |
|
|
|
1298 |
|
|
px = ctx->xoff; |
1299 |
|
|
nx = screen_size_x(wp->screen); |
1300 |
|
|
py = ctx->yoff; |
1301 |
|
|
ny = ctx->ocy - 1; |
1302 |
|
|
|
1303 |
|
|
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); |
1304 |
|
|
|
1305 |
|
|
px = ctx->xoff; |
1306 |
|
|
nx = ctx->ocx + 1; |
1307 |
|
|
py = ctx->yoff + ctx->ocy; |
1308 |
|
|
|
1309 |
|
|
tty_clear_line(tty, wp, py, px, nx, ctx->bg); |
1310 |
|
|
} |
1311 |
|
|
|
1312 |
|
|
void |
1313 |
|
|
tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) |
1314 |
|
|
{ |
1315 |
|
|
struct window_pane *wp = ctx->wp; |
1316 |
|
|
u_int px, py, nx, ny; |
1317 |
|
|
|
1318 |
|
|
tty_default_attributes(tty, wp, ctx->bg); |
1319 |
|
|
|
1320 |
|
|
tty_region_pane(tty, ctx, 0, screen_size_y(wp->screen) - 1); |
1321 |
|
|
tty_margin_off(tty); |
1322 |
|
|
|
1323 |
|
|
px = ctx->xoff; |
1324 |
|
|
nx = screen_size_x(wp->screen); |
1325 |
|
|
py = ctx->yoff; |
1326 |
|
|
ny = screen_size_y(wp->screen); |
1327 |
|
|
|
1328 |
|
|
tty_clear_area(tty, wp, py, ny, px, nx, ctx->bg); |
1329 |
|
|
} |
1330 |
|
|
|
1331 |
|
|
void |
1332 |
|
|
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) |
1333 |
|
|
{ |
1334 |
|
|
struct window_pane *wp = ctx->wp; |
1335 |
|
|
struct screen *s = wp->screen; |
1336 |
|
|
u_int i, j; |
1337 |
|
|
|
1338 |
|
|
tty_attributes(tty, &grid_default_cell, wp); |
1339 |
|
|
|
1340 |
|
|
tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); |
1341 |
|
|
tty_margin_off(tty); |
1342 |
|
|
|
1343 |
|
|
for (j = 0; j < screen_size_y(s); j++) { |
1344 |
|
|
tty_cursor_pane(tty, ctx, 0, j); |
1345 |
|
|
for (i = 0; i < screen_size_x(s); i++) |
1346 |
|
|
tty_putc(tty, 'E'); |
1347 |
|
|
} |
1348 |
|
|
} |
1349 |
|
|
|
1350 |
|
|
void |
1351 |
|
|
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) |
1352 |
|
|
{ |
1353 |
|
|
if (ctx->xoff + ctx->ocx > tty->sx - 1 && ctx->ocy == ctx->orlower) { |
1354 |
|
|
if (tty_pane_full_width(tty, ctx)) |
1355 |
|
|
tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); |
1356 |
|
|
else |
1357 |
|
|
tty_margin_off(tty); |
1358 |
|
|
} |
1359 |
|
|
|
1360 |
|
|
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); |
1361 |
|
|
|
1362 |
|
|
tty_cell(tty, ctx->cell, ctx->wp); |
1363 |
|
|
} |
1364 |
|
|
|
1365 |
|
|
void |
1366 |
|
|
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) |
1367 |
|
|
{ |
1368 |
|
|
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); |
1369 |
|
|
|
1370 |
|
|
tty_attributes(tty, ctx->cell, ctx->wp); |
1371 |
|
|
tty_putn(tty, ctx->ptr, ctx->num, ctx->num); |
1372 |
|
|
} |
1373 |
|
|
|
1374 |
|
|
void |
1375 |
|
|
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) |
1376 |
|
|
{ |
1377 |
|
|
char *buf; |
1378 |
|
|
size_t off; |
1379 |
|
|
|
1380 |
|
|
if (!tty_term_has(tty->term, TTYC_MS)) |
1381 |
|
|
return; |
1382 |
|
|
|
1383 |
|
|
off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */ |
1384 |
|
|
buf = xmalloc(off); |
1385 |
|
|
|
1386 |
|
|
b64_ntop(ctx->ptr, ctx->num, buf, off); |
1387 |
|
|
tty_putcode_ptr2(tty, TTYC_MS, "", buf); |
1388 |
|
|
|
1389 |
|
|
free(buf); |
1390 |
|
|
} |
1391 |
|
|
|
1392 |
|
|
void |
1393 |
|
|
tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) |
1394 |
|
|
{ |
1395 |
|
|
tty_add(tty, ctx->ptr, ctx->num); |
1396 |
|
|
tty_invalidate(tty); |
1397 |
|
|
} |
1398 |
|
|
|
1399 |
|
|
static void |
1400 |
|
|
tty_cell(struct tty *tty, const struct grid_cell *gc, |
1401 |
|
|
const struct window_pane *wp) |
1402 |
|
|
{ |
1403 |
|
|
u_int i; |
1404 |
|
|
|
1405 |
|
|
/* Skip last character if terminal is stupid. */ |
1406 |
|
|
if ((tty->term->flags & TERM_EARLYWRAP) && |
1407 |
|
|
tty->cy == tty->sy - 1 && |
1408 |
|
|
tty->cx == tty->sx - 1) |
1409 |
|
|
return; |
1410 |
|
|
|
1411 |
|
|
/* If this is a padding character, do nothing. */ |
1412 |
|
|
if (gc->flags & GRID_FLAG_PADDING) |
1413 |
|
|
return; |
1414 |
|
|
|
1415 |
|
|
/* Set the attributes. */ |
1416 |
|
|
tty_attributes(tty, gc, wp); |
1417 |
|
|
|
1418 |
|
|
/* Get the cell and if ASCII write with putc to do ACS translation. */ |
1419 |
|
|
if (gc->data.size == 1) { |
1420 |
|
|
if (*gc->data.data < 0x20 || *gc->data.data == 0x7f) |
1421 |
|
|
return; |
1422 |
|
|
tty_putc(tty, *gc->data.data); |
1423 |
|
|
return; |
1424 |
|
|
} |
1425 |
|
|
|
1426 |
|
|
/* If not UTF-8, write _. */ |
1427 |
|
|
if (!(tty->flags & TTY_UTF8)) { |
1428 |
|
|
for (i = 0; i < gc->data.width; i++) |
1429 |
|
|
tty_putc(tty, '_'); |
1430 |
|
|
return; |
1431 |
|
|
} |
1432 |
|
|
|
1433 |
|
|
/* Write the data. */ |
1434 |
|
|
tty_putn(tty, gc->data.data, gc->data.size, gc->data.width); |
1435 |
|
|
} |
1436 |
|
|
|
1437 |
|
|
void |
1438 |
|
|
tty_reset(struct tty *tty) |
1439 |
|
|
{ |
1440 |
|
|
struct grid_cell *gc = &tty->cell; |
1441 |
|
|
|
1442 |
|
|
if (!grid_cells_equal(gc, &grid_default_cell)) { |
1443 |
|
|
if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) |
1444 |
|
|
tty_putcode(tty, TTYC_RMACS); |
1445 |
|
|
tty_putcode(tty, TTYC_SGR0); |
1446 |
|
|
memcpy(gc, &grid_default_cell, sizeof *gc); |
1447 |
|
|
} |
1448 |
|
|
|
1449 |
|
|
memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); |
1450 |
|
|
tty->last_wp = -1; |
1451 |
|
|
} |
1452 |
|
|
|
1453 |
|
|
static void |
1454 |
|
|
tty_invalidate(struct tty *tty) |
1455 |
|
|
{ |
1456 |
|
|
memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); |
1457 |
|
|
|
1458 |
|
|
memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); |
1459 |
|
|
tty->last_wp = -1; |
1460 |
|
|
|
1461 |
|
|
tty->cx = tty->cy = UINT_MAX; |
1462 |
|
|
|
1463 |
|
|
tty->rupper = tty->rleft = UINT_MAX; |
1464 |
|
|
tty->rlower = tty->rright = UINT_MAX; |
1465 |
|
|
|
1466 |
|
|
if (tty->flags & TTY_STARTED) { |
1467 |
|
|
tty_putcode(tty, TTYC_SGR0); |
1468 |
|
|
|
1469 |
|
|
tty->mode = ALL_MODES; |
1470 |
|
|
tty_update_mode(tty, MODE_CURSOR, NULL); |
1471 |
|
|
|
1472 |
|
|
tty_cursor(tty, 0, 0); |
1473 |
|
|
tty_region_off(tty); |
1474 |
|
|
tty_margin_off(tty); |
1475 |
|
|
} else |
1476 |
|
|
tty->mode = MODE_CURSOR; |
1477 |
|
|
} |
1478 |
|
|
|
1479 |
|
|
/* Turn off margin. */ |
1480 |
|
|
void |
1481 |
|
|
tty_region_off(struct tty *tty) |
1482 |
|
|
{ |
1483 |
|
|
tty_region(tty, 0, tty->sy - 1); |
1484 |
|
|
} |
1485 |
|
|
|
1486 |
|
|
/* Set region inside pane. */ |
1487 |
|
|
static void |
1488 |
|
|
tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, |
1489 |
|
|
u_int rlower) |
1490 |
|
|
{ |
1491 |
|
|
tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower); |
1492 |
|
|
} |
1493 |
|
|
|
1494 |
|
|
/* Set region at absolute position. */ |
1495 |
|
|
static void |
1496 |
|
|
tty_region(struct tty *tty, u_int rupper, u_int rlower) |
1497 |
|
|
{ |
1498 |
|
|
if (tty->rlower == rlower && tty->rupper == rupper) |
1499 |
|
|
return; |
1500 |
|
|
if (!tty_term_has(tty->term, TTYC_CSR)) |
1501 |
|
|
return; |
1502 |
|
|
|
1503 |
|
|
tty->rupper = rupper; |
1504 |
|
|
tty->rlower = rlower; |
1505 |
|
|
|
1506 |
|
|
/* |
1507 |
|
|
* Some terminals (such as PuTTY) do not correctly reset the cursor to |
1508 |
|
|
* 0,0 if it is beyond the last column (they do not reset their wrap |
1509 |
|
|
* flag so further output causes a line feed). As a workaround, do an |
1510 |
|
|
* explicit move to 0 first. |
1511 |
|
|
*/ |
1512 |
|
|
if (tty->cx >= tty->sx) |
1513 |
|
|
tty_cursor(tty, 0, tty->cy); |
1514 |
|
|
|
1515 |
|
|
tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); |
1516 |
|
|
tty->cx = tty->cy = UINT_MAX; |
1517 |
|
|
} |
1518 |
|
|
|
1519 |
|
|
/* Turn off margin. */ |
1520 |
|
|
void |
1521 |
|
|
tty_margin_off(struct tty *tty) |
1522 |
|
|
{ |
1523 |
|
|
tty_margin(tty, 0, tty->sx - 1); |
1524 |
|
|
} |
1525 |
|
|
|
1526 |
|
|
/* Set margin inside pane. */ |
1527 |
|
|
static void |
1528 |
|
|
tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) |
1529 |
|
|
{ |
1530 |
|
|
tty_margin(tty, ctx->xoff, ctx->xoff + ctx->wp->sx - 1); |
1531 |
|
|
} |
1532 |
|
|
|
1533 |
|
|
/* Set margin at absolute position. */ |
1534 |
|
|
static void |
1535 |
|
|
tty_margin(struct tty *tty, u_int rleft, u_int rright) |
1536 |
|
|
{ |
1537 |
|
|
char s[64]; |
1538 |
|
|
|
1539 |
|
|
if (!tty_use_margin(tty)) |
1540 |
|
|
return; |
1541 |
|
|
if (tty->rleft == rleft && tty->rright == rright) |
1542 |
|
|
return; |
1543 |
|
|
|
1544 |
|
|
tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); |
1545 |
|
|
|
1546 |
|
|
tty->rleft = rleft; |
1547 |
|
|
tty->rright = rright; |
1548 |
|
|
|
1549 |
|
|
if (rleft == 0 && rright == tty->sx - 1) |
1550 |
|
|
snprintf(s, sizeof s, "\033[s"); |
1551 |
|
|
else |
1552 |
|
|
snprintf(s, sizeof s, "\033[%u;%us", rleft + 1, rright + 1); |
1553 |
|
|
tty_puts(tty, s); |
1554 |
|
|
tty->cx = tty->cy = UINT_MAX; |
1555 |
|
|
} |
1556 |
|
|
|
1557 |
|
|
/* |
1558 |
|
|
* Move the cursor, unless it would wrap itself when the next character is |
1559 |
|
|
* printed. |
1560 |
|
|
*/ |
1561 |
|
|
static void |
1562 |
|
|
tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, |
1563 |
|
|
u_int cx, u_int cy) |
1564 |
|
|
{ |
1565 |
|
|
if (!ctx->wrapped || |
1566 |
|
|
!tty_pane_full_width(tty, ctx) || |
1567 |
|
|
(tty->term->flags & TERM_EARLYWRAP) || |
1568 |
|
|
ctx->xoff + cx != 0 || |
1569 |
|
|
ctx->yoff + cy != tty->cy + 1 || |
1570 |
|
|
tty->cx < tty->sx || |
1571 |
|
|
tty->cy == tty->rlower) |
1572 |
|
|
tty_cursor_pane(tty, ctx, cx, cy); |
1573 |
|
|
else |
1574 |
|
|
log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy); |
1575 |
|
|
} |
1576 |
|
|
|
1577 |
|
|
/* Move cursor inside pane. */ |
1578 |
|
|
static void |
1579 |
|
|
tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) |
1580 |
|
|
{ |
1581 |
|
|
tty_cursor(tty, ctx->xoff + cx, ctx->yoff + cy); |
1582 |
|
|
} |
1583 |
|
|
|
1584 |
|
|
/* Move cursor to absolute position. */ |
1585 |
|
|
void |
1586 |
|
|
tty_cursor(struct tty *tty, u_int cx, u_int cy) |
1587 |
|
|
{ |
1588 |
|
|
struct tty_term *term = tty->term; |
1589 |
|
|
u_int thisx, thisy; |
1590 |
|
|
int change; |
1591 |
|
|
|
1592 |
|
|
if (cx > tty->sx - 1) |
1593 |
|
|
cx = tty->sx - 1; |
1594 |
|
|
|
1595 |
|
|
thisx = tty->cx; |
1596 |
|
|
thisy = tty->cy; |
1597 |
|
|
|
1598 |
|
|
/* No change. */ |
1599 |
|
|
if (cx == thisx && cy == thisy) |
1600 |
|
|
return; |
1601 |
|
|
|
1602 |
|
|
/* Very end of the line, just use absolute movement. */ |
1603 |
|
|
if (thisx > tty->sx - 1) |
1604 |
|
|
goto absolute; |
1605 |
|
|
|
1606 |
|
|
/* Move to home position (0, 0). */ |
1607 |
|
|
if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { |
1608 |
|
|
tty_putcode(tty, TTYC_HOME); |
1609 |
|
|
goto out; |
1610 |
|
|
} |
1611 |
|
|
|
1612 |
|
|
/* Zero on the next line. */ |
1613 |
|
|
if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower && |
1614 |
|
|
(!tty_use_margin(tty) || tty->rleft == 0)) { |
1615 |
|
|
tty_putc(tty, '\r'); |
1616 |
|
|
tty_putc(tty, '\n'); |
1617 |
|
|
goto out; |
1618 |
|
|
} |
1619 |
|
|
|
1620 |
|
|
/* Moving column or row. */ |
1621 |
|
|
if (cy == thisy) { |
1622 |
|
|
/* |
1623 |
|
|
* Moving column only, row staying the same. |
1624 |
|
|
*/ |
1625 |
|
|
|
1626 |
|
|
/* To left edge. */ |
1627 |
|
|
if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) { |
1628 |
|
|
tty_putc(tty, '\r'); |
1629 |
|
|
goto out; |
1630 |
|
|
} |
1631 |
|
|
|
1632 |
|
|
/* One to the left. */ |
1633 |
|
|
if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { |
1634 |
|
|
tty_putcode(tty, TTYC_CUB1); |
1635 |
|
|
goto out; |
1636 |
|
|
} |
1637 |
|
|
|
1638 |
|
|
/* One to the right. */ |
1639 |
|
|
if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { |
1640 |
|
|
tty_putcode(tty, TTYC_CUF1); |
1641 |
|
|
goto out; |
1642 |
|
|
} |
1643 |
|
|
|
1644 |
|
|
/* Calculate difference. */ |
1645 |
|
|
change = thisx - cx; /* +ve left, -ve right */ |
1646 |
|
|
|
1647 |
|
|
/* |
1648 |
|
|
* Use HPA if change is larger than absolute, otherwise move |
1649 |
|
|
* the cursor with CUB/CUF. |
1650 |
|
|
*/ |
1651 |
|
|
if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { |
1652 |
|
|
tty_putcode1(tty, TTYC_HPA, cx); |
1653 |
|
|
goto out; |
1654 |
|
|
} else if (change > 0 && tty_term_has(term, TTYC_CUB)) { |
1655 |
|
|
if (change == 2 && tty_term_has(term, TTYC_CUB1)) { |
1656 |
|
|
tty_putcode(tty, TTYC_CUB1); |
1657 |
|
|
tty_putcode(tty, TTYC_CUB1); |
1658 |
|
|
goto out; |
1659 |
|
|
} |
1660 |
|
|
tty_putcode1(tty, TTYC_CUB, change); |
1661 |
|
|
goto out; |
1662 |
|
|
} else if (change < 0 && tty_term_has(term, TTYC_CUF)) { |
1663 |
|
|
tty_putcode1(tty, TTYC_CUF, -change); |
1664 |
|
|
goto out; |
1665 |
|
|
} |
1666 |
|
|
} else if (cx == thisx) { |
1667 |
|
|
/* |
1668 |
|
|
* Moving row only, column staying the same. |
1669 |
|
|
*/ |
1670 |
|
|
|
1671 |
|
|
/* One above. */ |
1672 |
|
|
if (thisy != tty->rupper && |
1673 |
|
|
cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { |
1674 |
|
|
tty_putcode(tty, TTYC_CUU1); |
1675 |
|
|
goto out; |
1676 |
|
|
} |
1677 |
|
|
|
1678 |
|
|
/* One below. */ |
1679 |
|
|
if (thisy != tty->rlower && |
1680 |
|
|
cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { |
1681 |
|
|
tty_putcode(tty, TTYC_CUD1); |
1682 |
|
|
goto out; |
1683 |
|
|
} |
1684 |
|
|
|
1685 |
|
|
/* Calculate difference. */ |
1686 |
|
|
change = thisy - cy; /* +ve up, -ve down */ |
1687 |
|
|
|
1688 |
|
|
/* |
1689 |
|
|
* Try to use VPA if change is larger than absolute or if this |
1690 |
|
|
* change would cross the scroll region, otherwise use CUU/CUD. |
1691 |
|
|
*/ |
1692 |
|
|
if ((u_int) abs(change) > cy || |
1693 |
|
|
(change < 0 && cy - change > tty->rlower) || |
1694 |
|
|
(change > 0 && cy - change < tty->rupper)) { |
1695 |
|
|
if (tty_term_has(term, TTYC_VPA)) { |
1696 |
|
|
tty_putcode1(tty, TTYC_VPA, cy); |
1697 |
|
|
goto out; |
1698 |
|
|
} |
1699 |
|
|
} else if (change > 0 && tty_term_has(term, TTYC_CUU)) { |
1700 |
|
|
tty_putcode1(tty, TTYC_CUU, change); |
1701 |
|
|
goto out; |
1702 |
|
|
} else if (change < 0 && tty_term_has(term, TTYC_CUD)) { |
1703 |
|
|
tty_putcode1(tty, TTYC_CUD, -change); |
1704 |
|
|
goto out; |
1705 |
|
|
} |
1706 |
|
|
} |
1707 |
|
|
|
1708 |
|
|
absolute: |
1709 |
|
|
/* Absolute movement. */ |
1710 |
|
|
tty_putcode2(tty, TTYC_CUP, cy, cx); |
1711 |
|
|
|
1712 |
|
|
out: |
1713 |
|
|
tty->cx = cx; |
1714 |
|
|
tty->cy = cy; |
1715 |
|
|
} |
1716 |
|
|
|
1717 |
|
|
void |
1718 |
|
|
tty_attributes(struct tty *tty, const struct grid_cell *gc, |
1719 |
|
|
const struct window_pane *wp) |
1720 |
|
|
{ |
1721 |
|
|
struct grid_cell *tc = &tty->cell, gc2; |
1722 |
|
|
int changed; |
1723 |
|
|
|
1724 |
|
|
/* Ignore cell if it is the same as the last one. */ |
1725 |
|
|
if (wp != NULL && |
1726 |
|
|
(int)wp->id == tty->last_wp && |
1727 |
|
|
~(wp->window->flags & WINDOW_STYLECHANGED) && |
1728 |
|
|
gc->attr == tty->last_cell.attr && |
1729 |
|
|
gc->fg == tty->last_cell.fg && |
1730 |
|
|
gc->bg == tty->last_cell.bg) |
1731 |
|
|
return; |
1732 |
|
|
tty->last_wp = (wp != NULL ? (int)wp->id : -1); |
1733 |
|
|
memcpy(&tty->last_cell, gc, sizeof tty->last_cell); |
1734 |
|
|
|
1735 |
|
|
/* Copy cell and update default colours. */ |
1736 |
|
|
memcpy(&gc2, gc, sizeof gc2); |
1737 |
|
|
if (wp != NULL) |
1738 |
|
|
tty_default_colours(&gc2, wp); |
1739 |
|
|
|
1740 |
|
|
/* |
1741 |
|
|
* If no setab, try to use the reverse attribute as a best-effort for a |
1742 |
|
|
* non-default background. This is a bit of a hack but it doesn't do |
1743 |
|
|
* any serious harm and makes a couple of applications happier. |
1744 |
|
|
*/ |
1745 |
|
|
if (!tty_term_has(tty->term, TTYC_SETAB)) { |
1746 |
|
|
if (gc2.attr & GRID_ATTR_REVERSE) { |
1747 |
|
|
if (gc2.fg != 7 && gc2.fg != 8) |
1748 |
|
|
gc2.attr &= ~GRID_ATTR_REVERSE; |
1749 |
|
|
} else { |
1750 |
|
|
if (gc2.bg != 0 && gc2.bg != 8) |
1751 |
|
|
gc2.attr |= GRID_ATTR_REVERSE; |
1752 |
|
|
} |
1753 |
|
|
} |
1754 |
|
|
|
1755 |
|
|
/* Fix up the colours if necessary. */ |
1756 |
|
|
tty_check_fg(tty, wp, &gc2); |
1757 |
|
|
tty_check_bg(tty, wp, &gc2); |
1758 |
|
|
|
1759 |
|
|
/* If any bits are being cleared, reset everything. */ |
1760 |
|
|
if (tc->attr & ~gc2.attr) |
1761 |
|
|
tty_reset(tty); |
1762 |
|
|
|
1763 |
|
|
/* |
1764 |
|
|
* Set the colours. This may call tty_reset() (so it comes next) and |
1765 |
|
|
* may add to (NOT remove) the desired attributes by changing new_attr. |
1766 |
|
|
*/ |
1767 |
|
|
tty_colours(tty, &gc2); |
1768 |
|
|
|
1769 |
|
|
/* Filter out attribute bits already set. */ |
1770 |
|
|
changed = gc2.attr & ~tc->attr; |
1771 |
|
|
tc->attr = gc2.attr; |
1772 |
|
|
|
1773 |
|
|
/* Set the attributes. */ |
1774 |
|
|
if (changed & GRID_ATTR_BRIGHT) |
1775 |
|
|
tty_putcode(tty, TTYC_BOLD); |
1776 |
|
|
if (changed & GRID_ATTR_DIM) |
1777 |
|
|
tty_putcode(tty, TTYC_DIM); |
1778 |
|
|
if (changed & GRID_ATTR_ITALICS) |
1779 |
|
|
tty_set_italics(tty); |
1780 |
|
|
if (changed & GRID_ATTR_UNDERSCORE) |
1781 |
|
|
tty_putcode(tty, TTYC_SMUL); |
1782 |
|
|
if (changed & GRID_ATTR_BLINK) |
1783 |
|
|
tty_putcode(tty, TTYC_BLINK); |
1784 |
|
|
if (changed & GRID_ATTR_REVERSE) { |
1785 |
|
|
if (tty_term_has(tty->term, TTYC_REV)) |
1786 |
|
|
tty_putcode(tty, TTYC_REV); |
1787 |
|
|
else if (tty_term_has(tty->term, TTYC_SMSO)) |
1788 |
|
|
tty_putcode(tty, TTYC_SMSO); |
1789 |
|
|
} |
1790 |
|
|
if (changed & GRID_ATTR_HIDDEN) |
1791 |
|
|
tty_putcode(tty, TTYC_INVIS); |
1792 |
|
|
if (changed & GRID_ATTR_STRIKETHROUGH) |
1793 |
|
|
tty_putcode(tty, TTYC_SMXX); |
1794 |
|
|
if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) |
1795 |
|
|
tty_putcode(tty, TTYC_SMACS); |
1796 |
|
|
} |
1797 |
|
|
|
1798 |
|
|
static void |
1799 |
|
|
tty_colours(struct tty *tty, const struct grid_cell *gc) |
1800 |
|
|
{ |
1801 |
|
|
struct grid_cell *tc = &tty->cell; |
1802 |
|
|
int have_ax; |
1803 |
|
|
|
1804 |
|
|
/* No changes? Nothing is necessary. */ |
1805 |
|
|
if (gc->fg == tc->fg && gc->bg == tc->bg) |
1806 |
|
|
return; |
1807 |
|
|
|
1808 |
|
|
/* |
1809 |
|
|
* Is either the default colour? This is handled specially because the |
1810 |
|
|
* best solution might be to reset both colours to default, in which |
1811 |
|
|
* case if only one is default need to fall onward to set the other |
1812 |
|
|
* colour. |
1813 |
|
|
*/ |
1814 |
|
|
if (gc->fg == 8 || gc->bg == 8) { |
1815 |
|
|
/* |
1816 |
|
|
* If don't have AX but do have op, send sgr0 (op can't |
1817 |
|
|
* actually be used because it is sometimes the same as sgr0 |
1818 |
|
|
* and sometimes isn't). This resets both colours to default. |
1819 |
|
|
* |
1820 |
|
|
* Otherwise, try to set the default colour only as needed. |
1821 |
|
|
*/ |
1822 |
|
|
have_ax = tty_term_flag(tty->term, TTYC_AX); |
1823 |
|
|
if (!have_ax && tty_term_has(tty->term, TTYC_OP)) |
1824 |
|
|
tty_reset(tty); |
1825 |
|
|
else { |
1826 |
|
|
if (gc->fg == 8 && tc->fg != 8) { |
1827 |
|
|
if (have_ax) |
1828 |
|
|
tty_puts(tty, "\033[39m"); |
1829 |
|
|
else if (tc->fg != 7) |
1830 |
|
|
tty_putcode1(tty, TTYC_SETAF, 7); |
1831 |
|
|
tc->fg = 8; |
1832 |
|
|
} |
1833 |
|
|
if (gc->bg == 8 && tc->bg != 8) { |
1834 |
|
|
if (have_ax) |
1835 |
|
|
tty_puts(tty, "\033[49m"); |
1836 |
|
|
else if (tc->bg != 0) |
1837 |
|
|
tty_putcode1(tty, TTYC_SETAB, 0); |
1838 |
|
|
tc->bg = 8; |
1839 |
|
|
} |
1840 |
|
|
} |
1841 |
|
|
} |
1842 |
|
|
|
1843 |
|
|
/* Set the foreground colour. */ |
1844 |
|
|
if (gc->fg != 8 && gc->fg != tc->fg) |
1845 |
|
|
tty_colours_fg(tty, gc); |
1846 |
|
|
|
1847 |
|
|
/* |
1848 |
|
|
* Set the background colour. This must come after the foreground as |
1849 |
|
|
* tty_colour_fg() can call tty_reset(). |
1850 |
|
|
*/ |
1851 |
|
|
if (gc->bg != 8 && gc->bg != tc->bg) |
1852 |
|
|
tty_colours_bg(tty, gc); |
1853 |
|
|
} |
1854 |
|
|
|
1855 |
|
|
static void |
1856 |
|
|
tty_check_fg(struct tty *tty, const struct window_pane *wp, |
1857 |
|
|
struct grid_cell *gc) |
1858 |
|
|
{ |
1859 |
|
|
u_char r, g, b; |
1860 |
|
|
u_int colours; |
1861 |
|
|
int c; |
1862 |
|
|
|
1863 |
|
|
/* |
1864 |
|
|
* Perform substitution if this pane has a palette. If the bright |
1865 |
|
|
* attribute is set, use the bright entry in the palette by changing to |
1866 |
|
|
* the aixterm colour. |
1867 |
|
|
*/ |
1868 |
|
|
if (~gc->flags & GRID_FLAG_NOPALETTE) { |
1869 |
|
|
c = gc->fg; |
1870 |
|
|
if (c < 8 && gc->attr & GRID_ATTR_BRIGHT) |
1871 |
|
|
c += 90; |
1872 |
|
|
if ((c = window_pane_get_palette(wp, c)) != -1) |
1873 |
|
|
gc->fg = c; |
1874 |
|
|
} |
1875 |
|
|
|
1876 |
|
|
/* Is this a 24-bit colour? */ |
1877 |
|
|
if (gc->fg & COLOUR_FLAG_RGB) { |
1878 |
|
|
/* Not a 24-bit terminal? Translate to 256-colour palette. */ |
1879 |
|
|
if (!tty_term_has(tty->term, TTYC_SETRGBF)) { |
1880 |
|
|
colour_split_rgb(gc->fg, &r, &g, &b); |
1881 |
|
|
gc->fg = colour_find_rgb(r, g, b); |
1882 |
|
|
} else |
1883 |
|
|
return; |
1884 |
|
|
} |
1885 |
|
|
|
1886 |
|
|
/* How many colours does this terminal have? */ |
1887 |
|
|
if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) |
1888 |
|
|
colours = 256; |
1889 |
|
|
else |
1890 |
|
|
colours = tty_term_number(tty->term, TTYC_COLORS); |
1891 |
|
|
|
1892 |
|
|
/* Is this a 256-colour colour? */ |
1893 |
|
|
if (gc->fg & COLOUR_FLAG_256) { |
1894 |
|
|
/* And not a 256 colour mode? */ |
1895 |
|
|
if (colours != 256) { |
1896 |
|
|
gc->fg = colour_256to16(gc->fg); |
1897 |
|
|
if (gc->fg & 8) { |
1898 |
|
|
gc->fg &= 7; |
1899 |
|
|
if (colours >= 16) |
1900 |
|
|
gc->fg += 90; |
1901 |
|
|
else |
1902 |
|
|
gc->attr |= GRID_ATTR_BRIGHT; |
1903 |
|
|
} else |
1904 |
|
|
gc->attr &= ~GRID_ATTR_BRIGHT; |
1905 |
|
|
} |
1906 |
|
|
return; |
1907 |
|
|
} |
1908 |
|
|
|
1909 |
|
|
/* Is this an aixterm colour? */ |
1910 |
|
|
if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { |
1911 |
|
|
gc->fg -= 90; |
1912 |
|
|
gc->attr |= GRID_ATTR_BRIGHT; |
1913 |
|
|
} |
1914 |
|
|
} |
1915 |
|
|
|
1916 |
|
|
static void |
1917 |
|
|
tty_check_bg(struct tty *tty, const struct window_pane *wp, |
1918 |
|
|
struct grid_cell *gc) |
1919 |
|
|
{ |
1920 |
|
|
u_char r, g, b; |
1921 |
|
|
u_int colours; |
1922 |
|
|
int c; |
1923 |
|
|
|
1924 |
|
|
/* Perform substitution if this pane has a palette. */ |
1925 |
|
|
if (~gc->flags & GRID_FLAG_NOPALETTE) { |
1926 |
|
|
if ((c = window_pane_get_palette(wp, gc->bg)) != -1) |
1927 |
|
|
gc->bg = c; |
1928 |
|
|
} |
1929 |
|
|
|
1930 |
|
|
/* Is this a 24-bit colour? */ |
1931 |
|
|
if (gc->bg & COLOUR_FLAG_RGB) { |
1932 |
|
|
/* Not a 24-bit terminal? Translate to 256-colour palette. */ |
1933 |
|
|
if (!tty_term_has(tty->term, TTYC_SETRGBB)) { |
1934 |
|
|
colour_split_rgb(gc->bg, &r, &g, &b); |
1935 |
|
|
gc->bg = colour_find_rgb(r, g, b); |
1936 |
|
|
} else |
1937 |
|
|
return; |
1938 |
|
|
} |
1939 |
|
|
|
1940 |
|
|
/* How many colours does this terminal have? */ |
1941 |
|
|
if ((tty->term->flags|tty->term_flags) & TERM_256COLOURS) |
1942 |
|
|
colours = 256; |
1943 |
|
|
else |
1944 |
|
|
colours = tty_term_number(tty->term, TTYC_COLORS); |
1945 |
|
|
|
1946 |
|
|
/* Is this a 256-colour colour? */ |
1947 |
|
|
if (gc->bg & COLOUR_FLAG_256) { |
1948 |
|
|
/* |
1949 |
|
|
* And not a 256 colour mode? Translate to 16-colour |
1950 |
|
|
* palette. Bold background doesn't exist portably, so just |
1951 |
|
|
* discard the bold bit if set. |
1952 |
|
|
*/ |
1953 |
|
|
if (colours != 256) { |
1954 |
|
|
gc->bg = colour_256to16(gc->bg); |
1955 |
|
|
if (gc->bg & 8) { |
1956 |
|
|
gc->bg &= 7; |
1957 |
|
|
if (colours >= 16) |
1958 |
|
|
gc->fg += 90; |
1959 |
|
|
} |
1960 |
|
|
} |
1961 |
|
|
return; |
1962 |
|
|
} |
1963 |
|
|
|
1964 |
|
|
/* Is this an aixterm colour? */ |
1965 |
|
|
if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) |
1966 |
|
|
gc->bg -= 90; |
1967 |
|
|
} |
1968 |
|
|
|
1969 |
|
|
static void |
1970 |
|
|
tty_colours_fg(struct tty *tty, const struct grid_cell *gc) |
1971 |
|
|
{ |
1972 |
|
|
struct grid_cell *tc = &tty->cell; |
1973 |
|
|
char s[32]; |
1974 |
|
|
|
1975 |
|
|
/* Is this a 24-bit or 256-colour colour? */ |
1976 |
|
|
if (gc->fg & COLOUR_FLAG_RGB || |
1977 |
|
|
gc->fg & COLOUR_FLAG_256) { |
1978 |
|
|
if (tty_try_colour(tty, gc->fg, "38") == 0) |
1979 |
|
|
goto save_fg; |
1980 |
|
|
/* Should not get here, already converted in tty_check_fg. */ |
1981 |
|
|
return; |
1982 |
|
|
} |
1983 |
|
|
|
1984 |
|
|
/* Is this an aixterm bright colour? */ |
1985 |
|
|
if (gc->fg >= 90 && gc->fg <= 97) { |
1986 |
|
|
xsnprintf(s, sizeof s, "\033[%dm", gc->fg); |
1987 |
|
|
tty_puts(tty, s); |
1988 |
|
|
goto save_fg; |
1989 |
|
|
} |
1990 |
|
|
|
1991 |
|
|
/* Otherwise set the foreground colour. */ |
1992 |
|
|
tty_putcode1(tty, TTYC_SETAF, gc->fg); |
1993 |
|
|
|
1994 |
|
|
save_fg: |
1995 |
|
|
/* Save the new values in the terminal current cell. */ |
1996 |
|
|
tc->fg = gc->fg; |
1997 |
|
|
} |
1998 |
|
|
|
1999 |
|
|
static void |
2000 |
|
|
tty_colours_bg(struct tty *tty, const struct grid_cell *gc) |
2001 |
|
|
{ |
2002 |
|
|
struct grid_cell *tc = &tty->cell; |
2003 |
|
|
char s[32]; |
2004 |
|
|
|
2005 |
|
|
/* Is this a 24-bit or 256-colour colour? */ |
2006 |
|
|
if (gc->bg & COLOUR_FLAG_RGB || |
2007 |
|
|
gc->bg & COLOUR_FLAG_256) { |
2008 |
|
|
if (tty_try_colour(tty, gc->bg, "48") == 0) |
2009 |
|
|
goto save_bg; |
2010 |
|
|
/* Should not get here, already converted in tty_check_bg. */ |
2011 |
|
|
return; |
2012 |
|
|
} |
2013 |
|
|
|
2014 |
|
|
/* Is this an aixterm bright colour? */ |
2015 |
|
|
if (gc->bg >= 90 && gc->bg <= 97) { |
2016 |
|
|
xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); |
2017 |
|
|
tty_puts(tty, s); |
2018 |
|
|
goto save_bg; |
2019 |
|
|
} |
2020 |
|
|
|
2021 |
|
|
/* Otherwise set the background colour. */ |
2022 |
|
|
tty_putcode1(tty, TTYC_SETAB, gc->bg); |
2023 |
|
|
|
2024 |
|
|
save_bg: |
2025 |
|
|
/* Save the new values in the terminal current cell. */ |
2026 |
|
|
tc->bg = gc->bg; |
2027 |
|
|
} |
2028 |
|
|
|
2029 |
|
|
static int |
2030 |
|
|
tty_try_colour(struct tty *tty, int colour, const char *type) |
2031 |
|
|
{ |
2032 |
|
|
u_char r, g, b; |
2033 |
|
|
char s[32]; |
2034 |
|
|
|
2035 |
|
|
if (colour & COLOUR_FLAG_256) { |
2036 |
|
|
/* |
2037 |
|
|
* If the user has specified -2 to the client, setaf and setab |
2038 |
|
|
* may not work (or they may not want to use them), so send the |
2039 |
|
|
* usual sequence. |
2040 |
|
|
*/ |
2041 |
|
|
if (tty->term_flags & TERM_256COLOURS) |
2042 |
|
|
goto fallback_256; |
2043 |
|
|
|
2044 |
|
|
/* |
2045 |
|
|
* If the terminfo entry has 256 colours and setaf and setab |
2046 |
|
|
* exist, assume that they work correctly. |
2047 |
|
|
*/ |
2048 |
|
|
if (tty->term->flags & TERM_256COLOURS) { |
2049 |
|
|
if (*type == '3') { |
2050 |
|
|
if (!tty_term_has(tty->term, TTYC_SETAF)) |
2051 |
|
|
goto fallback_256; |
2052 |
|
|
tty_putcode1(tty, TTYC_SETAF, colour & 0xff); |
2053 |
|
|
} else { |
2054 |
|
|
if (!tty_term_has(tty->term, TTYC_SETAB)) |
2055 |
|
|
goto fallback_256; |
2056 |
|
|
tty_putcode1(tty, TTYC_SETAB, colour & 0xff); |
2057 |
|
|
} |
2058 |
|
|
return (0); |
2059 |
|
|
} |
2060 |
|
|
goto fallback_256; |
2061 |
|
|
} |
2062 |
|
|
|
2063 |
|
|
if (colour & COLOUR_FLAG_RGB) { |
2064 |
|
|
if (*type == '3') { |
2065 |
|
|
if (!tty_term_has(tty->term, TTYC_SETRGBF)) |
2066 |
|
|
return (-1); |
2067 |
|
|
colour_split_rgb(colour & 0xffffff, &r, &g, &b); |
2068 |
|
|
tty_putcode3(tty, TTYC_SETRGBF, r, g, b); |
2069 |
|
|
} else { |
2070 |
|
|
if (!tty_term_has(tty->term, TTYC_SETRGBB)) |
2071 |
|
|
return (-1); |
2072 |
|
|
colour_split_rgb(colour & 0xffffff, &r, &g, &b); |
2073 |
|
|
tty_putcode3(tty, TTYC_SETRGBB, r, g, b); |
2074 |
|
|
} |
2075 |
|
|
return (0); |
2076 |
|
|
} |
2077 |
|
|
|
2078 |
|
|
return (-1); |
2079 |
|
|
|
2080 |
|
|
fallback_256: |
2081 |
|
|
xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff); |
2082 |
|
|
tty_puts(tty, s); |
2083 |
|
|
return (0); |
2084 |
|
|
} |
2085 |
|
|
|
2086 |
|
|
static void |
2087 |
|
|
tty_default_colours(struct grid_cell *gc, const struct window_pane *wp) |
2088 |
|
|
{ |
2089 |
|
|
struct window *w = wp->window; |
2090 |
|
|
struct options *oo = w->options; |
2091 |
|
|
const struct grid_cell *agc, *pgc, *wgc; |
2092 |
|
|
int c; |
2093 |
|
|
|
2094 |
|
|
if (w->flags & WINDOW_STYLECHANGED) { |
2095 |
|
|
w->flags &= ~WINDOW_STYLECHANGED; |
2096 |
|
|
agc = options_get_style(oo, "window-active-style"); |
2097 |
|
|
memcpy(&w->active_style, agc, sizeof w->active_style); |
2098 |
|
|
wgc = options_get_style(oo, "window-style"); |
2099 |
|
|
memcpy(&w->style, wgc, sizeof w->style); |
2100 |
|
|
} else { |
2101 |
|
|
agc = &w->active_style; |
2102 |
|
|
wgc = &w->style; |
2103 |
|
|
} |
2104 |
|
|
pgc = &wp->colgc; |
2105 |
|
|
|
2106 |
|
|
if (gc->fg == 8) { |
2107 |
|
|
if (pgc->fg != 8) |
2108 |
|
|
gc->fg = pgc->fg; |
2109 |
|
|
else if (wp == w->active && agc->fg != 8) |
2110 |
|
|
gc->fg = agc->fg; |
2111 |
|
|
else |
2112 |
|
|
gc->fg = wgc->fg; |
2113 |
|
|
|
2114 |
|
|
if (gc->fg != 8 && |
2115 |
|
|
(c = window_pane_get_palette(wp, gc->fg)) != -1) |
2116 |
|
|
gc->fg = c; |
2117 |
|
|
} |
2118 |
|
|
|
2119 |
|
|
if (gc->bg == 8) { |
2120 |
|
|
if (pgc->bg != 8) |
2121 |
|
|
gc->bg = pgc->bg; |
2122 |
|
|
else if (wp == w->active && agc->bg != 8) |
2123 |
|
|
gc->bg = agc->bg; |
2124 |
|
|
else |
2125 |
|
|
gc->bg = wgc->bg; |
2126 |
|
|
|
2127 |
|
|
if (gc->bg != 8 && |
2128 |
|
|
(c = window_pane_get_palette(wp, gc->bg)) != -1) |
2129 |
|
|
gc->bg = c; |
2130 |
|
|
} |
2131 |
|
|
} |
2132 |
|
|
|
2133 |
|
|
static void |
2134 |
|
|
tty_default_attributes(struct tty *tty, const struct window_pane *wp, u_int bg) |
2135 |
|
|
{ |
2136 |
|
|
static struct grid_cell gc; |
2137 |
|
|
|
2138 |
|
|
memcpy(&gc, &grid_default_cell, sizeof gc); |
2139 |
|
|
gc.bg = bg; |
2140 |
|
|
tty_attributes(tty, &gc, wp); |
2141 |
|
|
} |