199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv
3698d5317Sjmmv /*
4f26e8bc9Schristos * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv *
6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv *
10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv */
18698d5317Sjmmv
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv
21698d5317Sjmmv #include <stdlib.h>
22e271dbb8Schristos #include <string.h>
23698d5317Sjmmv
24698d5317Sjmmv #include "tmux.h"
25698d5317Sjmmv
26698d5317Sjmmv /* Table mapping ACS entries to UTF-8. */
27698d5317Sjmmv struct tty_acs_entry {
28698d5317Sjmmv u_char key;
29698d5317Sjmmv const char *string;
30698d5317Sjmmv };
31e9a2d6faSchristos static const struct tty_acs_entry tty_acs_table[] = {
3261fba46bSchristos { '+', "\342\206\222" }, /* arrow pointing right */
3361fba46bSchristos { ',', "\342\206\220" }, /* arrow pointing left */
3461fba46bSchristos { '-', "\342\206\221" }, /* arrow pointing up */
3561fba46bSchristos { '.', "\342\206\223" }, /* arrow pointing down */
3661fba46bSchristos { '0', "\342\226\256" }, /* solid square block */
3761fba46bSchristos { '`', "\342\227\206" }, /* diamond */
3861fba46bSchristos { 'a', "\342\226\222" }, /* checker board (stipple) */
3930744affSchristos { 'b', "\342\220\211" },
4030744affSchristos { 'c', "\342\220\214" },
4130744affSchristos { 'd', "\342\220\215" },
4230744affSchristos { 'e', "\342\220\212" },
4361fba46bSchristos { 'f', "\302\260" }, /* degree symbol */
4461fba46bSchristos { 'g', "\302\261" }, /* plus/minus */
4530744affSchristos { 'h', "\342\220\244" },
4630744affSchristos { 'i', "\342\220\213" },
4761fba46bSchristos { 'j', "\342\224\230" }, /* lower right corner */
4861fba46bSchristos { 'k', "\342\224\220" }, /* upper right corner */
4961fba46bSchristos { 'l', "\342\224\214" }, /* upper left corner */
5061fba46bSchristos { 'm', "\342\224\224" }, /* lower left corner */
5161fba46bSchristos { 'n', "\342\224\274" }, /* large plus or crossover */
5261fba46bSchristos { 'o', "\342\216\272" }, /* scan line 1 */
5361fba46bSchristos { 'p', "\342\216\273" }, /* scan line 3 */
5461fba46bSchristos { 'q', "\342\224\200" }, /* horizontal line */
5561fba46bSchristos { 'r', "\342\216\274" }, /* scan line 7 */
5661fba46bSchristos { 's', "\342\216\275" }, /* scan line 9 */
5761fba46bSchristos { 't', "\342\224\234" }, /* tee pointing right */
5861fba46bSchristos { 'u', "\342\224\244" }, /* tee pointing left */
5961fba46bSchristos { 'v', "\342\224\264" }, /* tee pointing up */
6061fba46bSchristos { 'w', "\342\224\254" }, /* tee pointing down */
6161fba46bSchristos { 'x', "\342\224\202" }, /* vertical line */
6261fba46bSchristos { 'y', "\342\211\244" }, /* less-than-or-equal-to */
6361fba46bSchristos { 'z', "\342\211\245" }, /* greater-than-or-equal-to */
6461fba46bSchristos { '{', "\317\200" }, /* greek pi */
6561fba46bSchristos { '|', "\342\211\240" }, /* not-equal */
6661fba46bSchristos { '}', "\302\243" }, /* UK pound sign */
6761fba46bSchristos { '~', "\302\267" } /* bullet */
68698d5317Sjmmv };
69698d5317Sjmmv
70e271dbb8Schristos /* Table mapping UTF-8 to ACS entries. */
71e271dbb8Schristos struct tty_acs_reverse_entry {
72e271dbb8Schristos const char *string;
73e271dbb8Schristos u_char key;
74e271dbb8Schristos };
75e271dbb8Schristos static const struct tty_acs_reverse_entry tty_acs_reverse2[] = {
76e271dbb8Schristos { "\302\267", '~' }
77e271dbb8Schristos };
78e271dbb8Schristos static const struct tty_acs_reverse_entry tty_acs_reverse3[] = {
79e271dbb8Schristos { "\342\224\200", 'q' },
80e271dbb8Schristos { "\342\224\201", 'q' },
81e271dbb8Schristos { "\342\224\202", 'x' },
82e271dbb8Schristos { "\342\224\203", 'x' },
83e271dbb8Schristos { "\342\224\214", 'l' },
84e271dbb8Schristos { "\342\224\217", 'k' },
85e271dbb8Schristos { "\342\224\220", 'k' },
86e271dbb8Schristos { "\342\224\223", 'l' },
87e271dbb8Schristos { "\342\224\224", 'm' },
88e271dbb8Schristos { "\342\224\227", 'm' },
89e271dbb8Schristos { "\342\224\230", 'j' },
90e271dbb8Schristos { "\342\224\233", 'j' },
91e271dbb8Schristos { "\342\224\234", 't' },
92e271dbb8Schristos { "\342\224\243", 't' },
93e271dbb8Schristos { "\342\224\244", 'u' },
94e271dbb8Schristos { "\342\224\253", 'u' },
95e271dbb8Schristos { "\342\224\263", 'w' },
96e271dbb8Schristos { "\342\224\264", 'v' },
97e271dbb8Schristos { "\342\224\273", 'v' },
98e271dbb8Schristos { "\342\224\274", 'n' },
99e271dbb8Schristos { "\342\225\213", 'n' },
100e271dbb8Schristos { "\342\225\220", 'q' },
101e271dbb8Schristos { "\342\225\221", 'x' },
102e271dbb8Schristos { "\342\225\224", 'l' },
103e271dbb8Schristos { "\342\225\227", 'k' },
104e271dbb8Schristos { "\342\225\232", 'm' },
105e271dbb8Schristos { "\342\225\235", 'j' },
106e271dbb8Schristos { "\342\225\240", 't' },
107e271dbb8Schristos { "\342\225\243", 'u' },
108e271dbb8Schristos { "\342\225\246", 'w' },
109e271dbb8Schristos { "\342\225\251", 'v' },
110e271dbb8Schristos { "\342\225\254", 'n' },
111e271dbb8Schristos };
11246548964Swiz
11346548964Swiz /* UTF-8 double borders. */
11446548964Swiz static const struct utf8_data tty_acs_double_borders_list[] = {
11546548964Swiz { "", 0, 0, 0 },
11646548964Swiz { "\342\225\221", 0, 3, 1 }, /* U+2551 */
11746548964Swiz { "\342\225\220", 0, 3, 1 }, /* U+2550 */
11846548964Swiz { "\342\225\224", 0, 3, 1 }, /* U+2554 */
11946548964Swiz { "\342\225\227", 0, 3, 1 }, /* U+2557 */
12046548964Swiz { "\342\225\232", 0, 3, 1 }, /* U+255A */
12146548964Swiz { "\342\225\235", 0, 3, 1 }, /* U+255D */
12246548964Swiz { "\342\225\246", 0, 3, 1 }, /* U+2566 */
12346548964Swiz { "\342\225\251", 0, 3, 1 }, /* U+2569 */
12446548964Swiz { "\342\225\240", 0, 3, 1 }, /* U+2560 */
12546548964Swiz { "\342\225\243", 0, 3, 1 }, /* U+2563 */
12646548964Swiz { "\342\225\254", 0, 3, 1 }, /* U+256C */
12746548964Swiz { "\302\267", 0, 2, 1 } /* U+00B7 */
12846548964Swiz };
12946548964Swiz
13046548964Swiz /* UTF-8 heavy borders. */
13146548964Swiz static const struct utf8_data tty_acs_heavy_borders_list[] = {
13246548964Swiz { "", 0, 0, 0 },
13346548964Swiz { "\342\224\203", 0, 3, 1 }, /* U+2503 */
13446548964Swiz { "\342\224\201", 0, 3, 1 }, /* U+2501 */
13546548964Swiz { "\342\224\217", 0, 3, 1 }, /* U+250F */
13646548964Swiz { "\342\224\223", 0, 3, 1 }, /* U+2513 */
13746548964Swiz { "\342\224\227", 0, 3, 1 }, /* U+2517 */
13846548964Swiz { "\342\224\233", 0, 3, 1 }, /* U+251B */
13946548964Swiz { "\342\224\263", 0, 3, 1 }, /* U+2533 */
14046548964Swiz { "\342\224\273", 0, 3, 1 }, /* U+253B */
14146548964Swiz { "\342\224\243", 0, 3, 1 }, /* U+2523 */
14246548964Swiz { "\342\224\253", 0, 3, 1 }, /* U+252B */
14346548964Swiz { "\342\225\213", 0, 3, 1 }, /* U+254B */
14446548964Swiz { "\302\267", 0, 2, 1 } /* U+00B7 */
14546548964Swiz };
14646548964Swiz
14746548964Swiz /* UTF-8 rounded borders. */
14846548964Swiz static const struct utf8_data tty_acs_rounded_borders_list[] = {
14946548964Swiz { "", 0, 0, 0 },
15046548964Swiz { "\342\224\202", 0, 3, 1 }, /* U+2502 */
15146548964Swiz { "\342\224\200", 0, 3, 1 }, /* U+2500 */
15246548964Swiz { "\342\225\255", 0, 3, 1 }, /* U+256D */
15346548964Swiz { "\342\225\256", 0, 3, 1 }, /* U+256E */
15446548964Swiz { "\342\225\260", 0, 3, 1 }, /* U+2570 */
15546548964Swiz { "\342\225\257", 0, 3, 1 }, /* U+256F */
15646548964Swiz { "\342\224\263", 0, 3, 1 }, /* U+2533 */
15746548964Swiz { "\342\224\273", 0, 3, 1 }, /* U+253B */
158*f844e94eSwiz { "\342\224\234", 0, 3, 1 }, /* U+2524 */
159*f844e94eSwiz { "\342\224\244", 0, 3, 1 }, /* U+251C */
16046548964Swiz { "\342\225\213", 0, 3, 1 }, /* U+254B */
16146548964Swiz { "\302\267", 0, 2, 1 } /* U+00B7 */
16246548964Swiz };
16346548964Swiz
16446548964Swiz /* Get cell border character for double style. */
16546548964Swiz const struct utf8_data *
tty_acs_double_borders(int cell_type)16646548964Swiz tty_acs_double_borders(int cell_type)
16746548964Swiz {
16846548964Swiz return (&tty_acs_double_borders_list[cell_type]);
16946548964Swiz }
17046548964Swiz
17146548964Swiz /* Get cell border character for heavy style. */
17246548964Swiz const struct utf8_data *
tty_acs_heavy_borders(int cell_type)17346548964Swiz tty_acs_heavy_borders(int cell_type)
17446548964Swiz {
17546548964Swiz return (&tty_acs_heavy_borders_list[cell_type]);
17646548964Swiz }
17746548964Swiz
17846548964Swiz /* Get cell border character for rounded style. */
17946548964Swiz const struct utf8_data *
tty_acs_rounded_borders(int cell_type)18046548964Swiz tty_acs_rounded_borders(int cell_type)
18146548964Swiz {
18246548964Swiz return (&tty_acs_rounded_borders_list[cell_type]);
18346548964Swiz }
184e271dbb8Schristos
185e9a2d6faSchristos static int
tty_acs_cmp(const void * key,const void * value)186698d5317Sjmmv tty_acs_cmp(const void *key, const void *value)
187698d5317Sjmmv {
188698d5317Sjmmv const struct tty_acs_entry *entry = value;
189e271dbb8Schristos int test = *(const u_char *)key;
190698d5317Sjmmv
191e271dbb8Schristos return (test - entry->key);
192e271dbb8Schristos }
193e271dbb8Schristos
194e271dbb8Schristos static int
tty_acs_reverse_cmp(const void * key,const void * value)195e271dbb8Schristos tty_acs_reverse_cmp(const void *key, const void *value)
196e271dbb8Schristos {
197e271dbb8Schristos const struct tty_acs_reverse_entry *entry = value;
198e271dbb8Schristos const char *test = key;
199e271dbb8Schristos
200e271dbb8Schristos return (strcmp(test, entry->string));
201698d5317Sjmmv }
202698d5317Sjmmv
203fe99a117Schristos /* Should this terminal use ACS instead of UTF-8 line drawing? */
204fe99a117Schristos int
tty_acs_needed(struct tty * tty)205fe99a117Schristos tty_acs_needed(struct tty *tty)
206fe99a117Schristos {
207fe99a117Schristos if (tty == NULL)
208fe99a117Schristos return (0);
209fe99a117Schristos
210fe99a117Schristos /*
211fe99a117Schristos * If the U8 flag is present, it marks whether a terminal supports
212fe99a117Schristos * UTF-8 and ACS together.
213fe99a117Schristos *
214fe99a117Schristos * If it is present and zero, we force ACS - this gives users a way to
215fe99a117Schristos * turn off UTF-8 line drawing.
216fe99a117Schristos *
217fe99a117Schristos * If it is nonzero, we can fall through to the default and use UTF-8
218fe99a117Schristos * line drawing on UTF-8 terminals.
219fe99a117Schristos */
220fe99a117Schristos if (tty_term_has(tty->term, TTYC_U8) &&
221fe99a117Schristos tty_term_number(tty->term, TTYC_U8) == 0)
222fe99a117Schristos return (1);
223fe99a117Schristos
224e271dbb8Schristos if (tty->client->flags & CLIENT_UTF8)
225fe99a117Schristos return (0);
226fe99a117Schristos return (1);
227fe99a117Schristos }
228fe99a117Schristos
229e271dbb8Schristos /* Retrieve ACS to output as UTF-8. */
230698d5317Sjmmv const char *
tty_acs_get(struct tty * tty,u_char ch)231698d5317Sjmmv tty_acs_get(struct tty *tty, u_char ch)
232698d5317Sjmmv {
233e271dbb8Schristos const struct tty_acs_entry *entry;
234698d5317Sjmmv
235fe99a117Schristos /* Use the ACS set instead of UTF-8 if needed. */
236fe99a117Schristos if (tty_acs_needed(tty)) {
237698d5317Sjmmv if (tty->term->acs[ch][0] == '\0')
238698d5317Sjmmv return (NULL);
239698d5317Sjmmv return (&tty->term->acs[ch][0]);
240698d5317Sjmmv }
241698d5317Sjmmv
242698d5317Sjmmv /* Otherwise look up the UTF-8 translation. */
243fe99a117Schristos entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
244fe99a117Schristos sizeof tty_acs_table[0], tty_acs_cmp);
245698d5317Sjmmv if (entry == NULL)
246698d5317Sjmmv return (NULL);
247698d5317Sjmmv return (entry->string);
248698d5317Sjmmv }
249e271dbb8Schristos
250e271dbb8Schristos /* Reverse UTF-8 into ACS. */
251e271dbb8Schristos int
tty_acs_reverse_get(__unused struct tty * tty,const char * s,size_t slen)252e271dbb8Schristos tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen)
253e271dbb8Schristos {
254e271dbb8Schristos const struct tty_acs_reverse_entry *table, *entry;
255e271dbb8Schristos u_int items;
256e271dbb8Schristos
257e271dbb8Schristos if (slen == 2) {
258e271dbb8Schristos table = tty_acs_reverse2;
259e271dbb8Schristos items = nitems(tty_acs_reverse2);
260e271dbb8Schristos } else if (slen == 3) {
261e271dbb8Schristos table = tty_acs_reverse3;
262e271dbb8Schristos items = nitems(tty_acs_reverse3);
263e271dbb8Schristos } else
264e271dbb8Schristos return (-1);
265e271dbb8Schristos entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp);
266e271dbb8Schristos if (entry == NULL)
267e271dbb8Schristos return (-1);
268e271dbb8Schristos return (entry->key);
269e271dbb8Schristos }
270