1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2010 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 
24 #include "tmux.h"
25 
26 /* Table mapping ACS entries to UTF-8. */
27 struct tty_acs_entry {
28           u_char               key;
29           const char          *string;
30 };
31 static const struct tty_acs_entry tty_acs_table[] = {
32           { '+', "\342\206\222" },      /* arrow pointing right */
33           { ',', "\342\206\220" },      /* arrow pointing left */
34           { '-', "\342\206\221" },      /* arrow pointing up */
35           { '.', "\342\206\223" },      /* arrow pointing down */
36           { '0', "\342\226\256" },      /* solid square block */
37           { '`', "\342\227\206" },      /* diamond */
38           { 'a', "\342\226\222" },      /* checker board (stipple) */
39           { 'b', "\342\220\211" },
40           { 'c', "\342\220\214" },
41           { 'd', "\342\220\215" },
42           { 'e', "\342\220\212" },
43           { 'f', "\302\260" },                    /* degree symbol */
44           { 'g', "\302\261" },                    /* plus/minus */
45           { 'h', "\342\220\244" },
46           { 'i', "\342\220\213" },
47           { 'j', "\342\224\230" },      /* lower right corner */
48           { 'k', "\342\224\220" },      /* upper right corner */
49           { 'l', "\342\224\214" },      /* upper left corner */
50           { 'm', "\342\224\224" },      /* lower left corner */
51           { 'n', "\342\224\274" },      /* large plus or crossover */
52           { 'o', "\342\216\272" },      /* scan line 1 */
53           { 'p', "\342\216\273" },      /* scan line 3 */
54           { 'q', "\342\224\200" },      /* horizontal line */
55           { 'r', "\342\216\274" },      /* scan line 7 */
56           { 's', "\342\216\275" },      /* scan line 9 */
57           { 't', "\342\224\234" },      /* tee pointing right */
58           { 'u', "\342\224\244" },      /* tee pointing left */
59           { 'v', "\342\224\264" },      /* tee pointing up */
60           { 'w', "\342\224\254" },      /* tee pointing down */
61           { 'x', "\342\224\202" },      /* vertical line */
62           { 'y', "\342\211\244" },      /* less-than-or-equal-to */
63           { 'z', "\342\211\245" },      /* greater-than-or-equal-to */
64           { '{', "\317\200" },                    /* greek pi */
65           { '|', "\342\211\240" },      /* not-equal */
66           { '}', "\302\243" },                    /* UK pound sign */
67           { '~', "\302\267" }           /* bullet */
68 };
69 
70 /* Table mapping UTF-8 to ACS entries. */
71 struct tty_acs_reverse_entry {
72           const char          *string;
73           u_char               key;
74 };
75 static const struct tty_acs_reverse_entry tty_acs_reverse2[] = {
76           { "\302\267", '~' }
77 };
78 static const struct tty_acs_reverse_entry tty_acs_reverse3[] = {
79           { "\342\224\200", 'q' },
80           { "\342\224\201", 'q' },
81           { "\342\224\202", 'x' },
82           { "\342\224\203", 'x' },
83           { "\342\224\214", 'l' },
84           { "\342\224\217", 'k' },
85           { "\342\224\220", 'k' },
86           { "\342\224\223", 'l' },
87           { "\342\224\224", 'm' },
88           { "\342\224\227", 'm' },
89           { "\342\224\230", 'j' },
90           { "\342\224\233", 'j' },
91           { "\342\224\234", 't' },
92           { "\342\224\243", 't' },
93           { "\342\224\244", 'u' },
94           { "\342\224\253", 'u' },
95           { "\342\224\263", 'w' },
96           { "\342\224\264", 'v' },
97           { "\342\224\273", 'v' },
98           { "\342\224\274", 'n' },
99           { "\342\225\213", 'n' },
100           { "\342\225\220", 'q' },
101           { "\342\225\221", 'x' },
102           { "\342\225\224", 'l' },
103           { "\342\225\227", 'k' },
104           { "\342\225\232", 'm' },
105           { "\342\225\235", 'j' },
106           { "\342\225\240", 't' },
107           { "\342\225\243", 'u' },
108           { "\342\225\246", 'w' },
109           { "\342\225\251", 'v' },
110           { "\342\225\254", 'n' },
111 };
112 
113 /* UTF-8 double borders. */
114 static const struct utf8_data tty_acs_double_borders_list[] = {
115           { "", 0, 0, 0 },
116           { "\342\225\221", 0, 3, 1 }, /* U+2551 */
117           { "\342\225\220", 0, 3, 1 }, /* U+2550 */
118           { "\342\225\224", 0, 3, 1 }, /* U+2554 */
119           { "\342\225\227", 0, 3, 1 }, /* U+2557 */
120           { "\342\225\232", 0, 3, 1 }, /* U+255A */
121           { "\342\225\235", 0, 3, 1 }, /* U+255D */
122           { "\342\225\246", 0, 3, 1 }, /* U+2566 */
123           { "\342\225\251", 0, 3, 1 }, /* U+2569 */
124           { "\342\225\240", 0, 3, 1 }, /* U+2560 */
125           { "\342\225\243", 0, 3, 1 }, /* U+2563 */
126           { "\342\225\254", 0, 3, 1 }, /* U+256C */
127           { "\302\267",         0, 2, 1 }  /* U+00B7 */
128 };
129 
130 /* UTF-8 heavy borders. */
131 static const struct utf8_data tty_acs_heavy_borders_list[] = {
132           { "", 0, 0, 0 },
133           { "\342\224\203", 0, 3, 1 }, /* U+2503 */
134           { "\342\224\201", 0, 3, 1 }, /* U+2501 */
135           { "\342\224\217", 0, 3, 1 }, /* U+250F */
136           { "\342\224\223", 0, 3, 1 }, /* U+2513 */
137           { "\342\224\227", 0, 3, 1 }, /* U+2517 */
138           { "\342\224\233", 0, 3, 1 }, /* U+251B */
139           { "\342\224\263", 0, 3, 1 }, /* U+2533 */
140           { "\342\224\273", 0, 3, 1 }, /* U+253B */
141           { "\342\224\243", 0, 3, 1 }, /* U+2523 */
142           { "\342\224\253", 0, 3, 1 }, /* U+252B */
143           { "\342\225\213", 0, 3, 1 }, /* U+254B */
144           { "\302\267",         0, 2, 1 }  /* U+00B7 */
145 };
146 
147 /* UTF-8 rounded borders. */
148 static const struct utf8_data tty_acs_rounded_borders_list[] = {
149           { "", 0, 0, 0 },
150           { "\342\224\202", 0, 3, 1 }, /* U+2502 */
151           { "\342\224\200", 0, 3, 1 }, /* U+2500 */
152           { "\342\225\255", 0, 3, 1 }, /* U+256D */
153           { "\342\225\256", 0, 3, 1 }, /* U+256E */
154           { "\342\225\260", 0, 3, 1 }, /* U+2570 */
155           { "\342\225\257", 0, 3, 1 }, /* U+256F */
156           { "\342\224\263", 0, 3, 1 }, /* U+2533 */
157           { "\342\224\273", 0, 3, 1 }, /* U+253B */
158           { "\342\224\234", 0, 3, 1 }, /* U+2524 */
159           { "\342\224\244", 0, 3, 1 }, /* U+251C */
160           { "\342\225\213", 0, 3, 1 }, /* U+254B */
161           { "\302\267",         0, 2, 1 }  /* U+00B7 */
162 };
163 
164 /* Get cell border character for double style. */
165 const struct utf8_data *
tty_acs_double_borders(int cell_type)166 tty_acs_double_borders(int cell_type)
167 {
168           return (&tty_acs_double_borders_list[cell_type]);
169 }
170 
171 /* Get cell border character for heavy style. */
172 const struct utf8_data *
tty_acs_heavy_borders(int cell_type)173 tty_acs_heavy_borders(int cell_type)
174 {
175           return (&tty_acs_heavy_borders_list[cell_type]);
176 }
177 
178 /* Get cell border character for rounded style. */
179 const struct utf8_data *
tty_acs_rounded_borders(int cell_type)180 tty_acs_rounded_borders(int cell_type)
181 {
182           return (&tty_acs_rounded_borders_list[cell_type]);
183 }
184 
185 static int
tty_acs_cmp(const void * key,const void * value)186 tty_acs_cmp(const void *key, const void *value)
187 {
188           const struct tty_acs_entry    *entry = value;
189           int                                      test = *(const u_char *)key;
190 
191           return (test - entry->key);
192 }
193 
194 static int
tty_acs_reverse_cmp(const void * key,const void * value)195 tty_acs_reverse_cmp(const void *key, const void *value)
196 {
197           const struct tty_acs_reverse_entry      *entry = value;
198           const char                                        *test = key;
199 
200           return (strcmp(test, entry->string));
201 }
202 
203 /* Should this terminal use ACS instead of UTF-8 line drawing? */
204 int
tty_acs_needed(struct tty * tty)205 tty_acs_needed(struct tty *tty)
206 {
207           if (tty == NULL)
208                     return (0);
209 
210           /*
211            * If the U8 flag is present, it marks whether a terminal supports
212            * UTF-8 and ACS together.
213            *
214            * If it is present and zero, we force ACS - this gives users a way to
215            * turn off UTF-8 line drawing.
216            *
217            * If it is nonzero, we can fall through to the default and use UTF-8
218            * line drawing on UTF-8 terminals.
219            */
220           if (tty_term_has(tty->term, TTYC_U8) &&
221               tty_term_number(tty->term, TTYC_U8) == 0)
222                     return (1);
223 
224           if (tty->client->flags & CLIENT_UTF8)
225                     return (0);
226           return (1);
227 }
228 
229 /* Retrieve ACS to output as UTF-8. */
230 const char *
tty_acs_get(struct tty * tty,u_char ch)231 tty_acs_get(struct tty *tty, u_char ch)
232 {
233           const struct tty_acs_entry    *entry;
234 
235           /* Use the ACS set instead of UTF-8 if needed. */
236           if (tty_acs_needed(tty)) {
237                     if (tty->term->acs[ch][0] == '\0')
238                               return (NULL);
239                     return (&tty->term->acs[ch][0]);
240           }
241 
242           /* Otherwise look up the UTF-8 translation. */
243           entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
244               sizeof tty_acs_table[0], tty_acs_cmp);
245           if (entry == NULL)
246                     return (NULL);
247           return (entry->string);
248 }
249 
250 /* Reverse UTF-8 into ACS. */
251 int
tty_acs_reverse_get(__unused struct tty * tty,const char * s,size_t slen)252 tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen)
253 {
254           const struct tty_acs_reverse_entry      *table, *entry;
255           u_int                                              items;
256 
257           if (slen == 2) {
258                     table = tty_acs_reverse2;
259                     items = nitems(tty_acs_reverse2);
260           } else if (slen == 3) {
261                     table = tty_acs_reverse3;
262                     items = nitems(tty_acs_reverse3);
263           } else
264                     return (-1);
265           entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp);
266           if (entry == NULL)
267                     return (-1);
268           return (entry->key);
269 }
270