xref: /openbsd/usr.bin/tmux/tty-acs.c (revision 771fbea0)
1 /* $OpenBSD: tty-acs.c,v 1.10 2020/05/16 16:26:34 nicm Exp $ */
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 static int
114 tty_acs_cmp(const void *key, const void *value)
115 {
116 	const struct tty_acs_entry	*entry = value;
117 	int				 test = *(u_char *)key;
118 
119 	return (test - entry->key);
120 }
121 
122 static int
123 tty_acs_reverse_cmp(const void *key, const void *value)
124 {
125 	const struct tty_acs_reverse_entry	*entry = value;
126 	const char				*test = key;
127 
128 	return (strcmp(test, entry->string));
129 }
130 
131 /* Should this terminal use ACS instead of UTF-8 line drawing? */
132 int
133 tty_acs_needed(struct tty *tty)
134 {
135 	if (tty == NULL)
136 		return (0);
137 
138 	/*
139 	 * If the U8 flag is present, it marks whether a terminal supports
140 	 * UTF-8 and ACS together.
141 	 *
142 	 * If it is present and zero, we force ACS - this gives users a way to
143 	 * turn off UTF-8 line drawing.
144 	 *
145 	 * If it is nonzero, we can fall through to the default and use UTF-8
146 	 * line drawing on UTF-8 terminals.
147 	 */
148 	if (tty_term_has(tty->term, TTYC_U8) &&
149 	    tty_term_number(tty->term, TTYC_U8) == 0)
150 		return (1);
151 
152 	if (tty->client->flags & CLIENT_UTF8)
153 		return (0);
154 	return (1);
155 }
156 
157 /* Retrieve ACS to output as UTF-8. */
158 const char *
159 tty_acs_get(struct tty *tty, u_char ch)
160 {
161 	const struct tty_acs_entry	*entry;
162 
163 	/* Use the ACS set instead of UTF-8 if needed. */
164 	if (tty_acs_needed(tty)) {
165 		if (tty->term->acs[ch][0] == '\0')
166 			return (NULL);
167 		return (&tty->term->acs[ch][0]);
168 	}
169 
170 	/* Otherwise look up the UTF-8 translation. */
171 	entry = bsearch(&ch, tty_acs_table, nitems(tty_acs_table),
172 	    sizeof tty_acs_table[0], tty_acs_cmp);
173 	if (entry == NULL)
174 		return (NULL);
175 	return (entry->string);
176 }
177 
178 /* Reverse UTF-8 into ACS. */
179 int
180 tty_acs_reverse_get(__unused struct tty *tty, const char *s, size_t slen)
181 {
182 	const struct tty_acs_reverse_entry	*table, *entry;
183 	u_int					 items;
184 
185 	if (slen == 2) {
186 		table = tty_acs_reverse2;
187 		items = nitems(tty_acs_reverse2);
188 	} else if (slen == 3) {
189 		table = tty_acs_reverse3;
190 		items = nitems(tty_acs_reverse3);
191 	} else
192 		return (-1);
193 	entry = bsearch(s, table, items, sizeof table[0], tty_acs_reverse_cmp);
194 	if (entry == NULL)
195 		return (-1);
196 	return (entry->key);
197 }
198