much love
[mu.git] / tools / termbox / output.inl
blob490346f8e63ea8e2dac8fe5cf43505e792c4f4a8
1 enum {
2   T_ENTER_CA,
3   T_EXIT_CA,
4   T_SHOW_CURSOR,
5   T_HIDE_CURSOR,
6   T_CLEAR_SCREEN,
7   T_SGR0,
8   T_UNDERLINE,
9   T_BOLD,
10   T_BLINK,
11   T_REVERSE,
12   T_ENTER_KEYPAD,
13   T_EXIT_KEYPAD,
14   T_ENTER_MOUSE,
15   T_EXIT_MOUSE,
16   T_ENTER_BRACKETED_PASTE,
17   T_EXIT_BRACKETED_PASTE,
18   T_FUNCS_NUM,
21 #define EUNSUPPORTED_TERM -1
23 // rxvt-256color
24 static const char *rxvt_256color_keys[] = {
25   "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C", 0
27 static const char *rxvt_256color_funcs[] = {
28   "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l",
31 // Eterm
32 static const char *eterm_keys[] = {
33   "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C", 0
35 static const char *eterm_funcs[] = {
36   "\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", "", "",
39 // screen
40 static const char *screen_keys[] = {
41   "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", "\033OA", "\033OB", "\033OD", "\033OC", 0
43 static const char *screen_funcs[] = {
44   "\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l", "\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l",
47 // rxvt-unicode
48 static const char *rxvt_unicode_keys[] = {
49   "\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C", 0
51 static const char *rxvt_unicode_funcs[] = {
52   "\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l", "\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033=", "\033>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l",
55 // linux
56 static const char *linux_keys[] = {
57   "\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~", "\033[A", "\033[B", "\033[D", "\033[C", 0
59 static const char *linux_funcs[] = {
60   "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", "", "",
63 // xterm
64 static const char *xterm_keys[] = {
65   "\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~", "\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB", "\033OD", "\033OC", 0
67 static const char *xterm_funcs[] = {
68   "\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l", "\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "\033[?1h\033=", "\033[?1l\033>", "\033[?1000h", "\033[?1000l", "\033[?2004h", "\033[?2004l",
71 static struct term {
72   const char *name;
73   const char **keys;
74   const char **funcs;
75 } terms[] = {
76   {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
77   {"Eterm", eterm_keys, eterm_funcs},
78   {"screen", screen_keys, screen_funcs},
79   {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
80   {"linux", linux_keys, linux_funcs},
81   {"xterm", xterm_keys, xterm_funcs},
82   {0, 0, 0},
85 static bool init_from_terminfo = false;
86 static const char **keys;
87 static const char **funcs;
89 static int try_compatible(const char *term, const char *name,
90         const char **tkeys, const char **tfuncs)
92   if (strstr(term, name)) {
93     keys = tkeys;
94     funcs = tfuncs;
95     return 0;
96   }
98   return EUNSUPPORTED_TERM;
101 static int init_term_builtin(void)
103   int i;
104   const char *term = getenv("TERM");
106   if (term) {
107     for (i = 0; terms[i].name; i++) {
108       if (!strcmp(terms[i].name, term)) {
109         keys = terms[i].keys;
110         funcs = terms[i].funcs;
111         return 0;
112       }
113     }
115     /* let's do some heuristic, maybe it's a compatible terminal */
116     if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
117       return 0;
118     if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
119       return 0;
120     if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
121       return 0;
122     if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
123       return 0;
124     if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
125       return 0;
126     /* let's assume that 'cygwin' is xterm compatible */
127     if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
128       return 0;
129   }
131   return EUNSUPPORTED_TERM;
134 //----------------------------------------------------------------------
135 // terminfo
136 //----------------------------------------------------------------------
138 static char *read_file(const char *file) {
139   FILE *f = fopen(file, "rb");
140   if (!f)
141     return 0;
143   struct stat st;
144   if (fstat(fileno(f), &st) != 0) {
145     fclose(f);
146     return 0;
147   }
149   char *data = malloc(st.st_size);
150   if (!data) {
151     fclose(f);
152     return 0;
153   }
155   if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) {
156     fclose(f);
157     free(data);
158     return 0;
159   }
161   fclose(f);
162   return data;
165 static char *terminfo_try_path(const char *path, const char *term) {
166   char tmp[4096];
167   // snprintf guarantee for older compilers
168   assert(sizeof(tmp) > sizeof(path)+sizeof("/x/")+sizeof(term)+1);
169   snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
170   char *data = read_file(tmp);
171   if (data) {
172     return data;
173   }
175   // fallback to darwin specific dirs structure
176   // snprintf guarantee above still applies
177   snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
178   return read_file(tmp);
181 void string_copy(char* dest, const char* src, int dest_capacity) {
182   strncpy(dest, src, dest_capacity-1);
183   dest[dest_capacity-1] = '\0';
186 void string_append(char* dest, const char* src, int dest_capacity) {
187   strncat(dest, src, dest_capacity);
188   dest[dest_capacity-1] = '\0';
191 static char *load_terminfo(void) {
192   char tmp[4096];
193   const char *term = getenv("TERM");
194   if (!term) {
195     return 0;
196   }
198   // if TERMINFO is set, no other directory should be searched
199   const char *terminfo = getenv("TERMINFO");
200   if (terminfo) {
201     return terminfo_try_path(terminfo, term);
202   }
204   // next, consider ~/.terminfo
205   const char *home = getenv("HOME");
206   if (home) {
207     // snprintf guarantee for older compilers
208     assert(sizeof(tmp) > sizeof(home)+sizeof("/.terminfo")+1);
209     string_copy(tmp, home, sizeof(tmp));
210     string_append(tmp, "/.terminfo", sizeof(tmp));
211     char *data = terminfo_try_path(tmp, term);
212     if (data)
213       return data;
214   }
216   // next, TERMINFO_DIRS
217   const char *dirs = getenv("TERMINFO_DIRS");
218   if (dirs) {
219     // snprintf guarantee for older compilers
220     assert(sizeof(tmp) > sizeof(dirs));
221     string_copy(tmp, dirs, sizeof(tmp));
222     char *dir = strtok(tmp, ":");
223     while (dir) {
224       const char *cdir = dir;
225       if (strcmp(cdir, "") == 0) {
226         cdir = "/usr/share/terminfo";
227       }
228       char *data = terminfo_try_path(cdir, term);
229       if (data)
230         return data;
231       dir = strtok(0, ":");
232     }
233   }
235   // fallback to /usr/share/terminfo
236   return terminfo_try_path("/usr/share/terminfo", term);
239 #define TI_MAGIC 0432
240 #define TI_HEADER_LENGTH 12
241 #define TB_KEYS_NUM 22
243 static const char *terminfo_copy_string(char *data, int str, int table) {
244   const int16_t off = *(int16_t*)(data + str);
245   const char *src = data + table + off;
246   int len = strlen(src);
247   char *dst = malloc(len+1);
248   memcpy(dst, src, len+1);
249   return dst;
252 static const int16_t ti_funcs[] = {
253   28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
256 static const int16_t ti_keys[] = {
257   66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69,
258   70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61,
259   79, 83,
262 static int init_term(void) {
263   int i;
264   char *data = load_terminfo();
265   if (!data) {
266     init_from_terminfo = false;
267     return init_term_builtin();
268   }
270   int16_t *header = (int16_t*)data;
271   if ((header[1] + header[2]) % 2) {
272     // old quirk to align everything on word boundaries
273     header[2] += 1;
274   }
276   const int str_offset = TI_HEADER_LENGTH +
277     header[1] + header[2] + 2 * header[3];
278   const int table_offset = str_offset + 2 * header[4];
280   keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1));
281   for (i = 0; i < TB_KEYS_NUM; i++) {
282     keys[i] = terminfo_copy_string(data,
283       str_offset + 2 * ti_keys[i], table_offset);
284   }
285   keys[TB_KEYS_NUM] = 0;
287   funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
288   // the last four entries are reserved for mouse, bracketed paste. because the table offset is
289   // not there, the two entries have to fill in manually
290   for (i = 0; i < T_FUNCS_NUM-4; i++) {
291     funcs[i] = terminfo_copy_string(data,
292       str_offset + 2 * ti_funcs[i], table_offset);
293   }
295   funcs[T_FUNCS_NUM-4] = "\033[?1000h";
296   funcs[T_FUNCS_NUM-3] = "\033[?1000l";
297   funcs[T_FUNCS_NUM-2] = "\033[?2004h";
298   funcs[T_FUNCS_NUM-1] = "\033[?2004l";
300   init_from_terminfo = true;
301   free(data);
302   return 0;
305 static void shutdown_term(void) {
306   if (init_from_terminfo) {
307     int i;
308     for (i = 0; i < TB_KEYS_NUM; i++) {
309       free((void*)keys[i]);
310     }
311     // the last four entries are reserved for mouse, bracketed paste. because the table offset
312     // is not there, the two entries have to fill in manually and do not
313     // need to be freed.
314     for (i = 0; i < T_FUNCS_NUM-4; i++) {
315       free((void*)funcs[i]);
316     }
317     free(keys);
318     free(funcs);
319   }