💾 Archived View for gmi.noulin.net › gitRepositories › heartbeat › file › shpPackages › termbox › te… captured on 2024-06-20 at 11:55:54. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
term.h (9287B)
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_FUNCS_NUM, 17 }; 18 19 #define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h" 20 #define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l" 21 22 #define EUNSUPPORTED_TERM -1 23 24 // rxvt-256color 25 static const char *rxvt_256color_keys[] = { 26 "\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 }; 28 static const char *rxvt_256color_funcs[] = { 29 "\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>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, 30 }; 31 32 // Eterm 33 static const char *eterm_keys[] = { 34 "\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 }; 36 static const char *eterm_funcs[] = { 37 "\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", "", "", "", "", 38 }; 39 40 // screen 41 static const char *screen_keys[] = { 42 "\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 }; 44 static const char *screen_funcs[] = { 45 "\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>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, 46 }; 47 48 // rxvt-unicode 49 static const char *rxvt_unicode_keys[] = { 50 "\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 }; 52 static const char *rxvt_unicode_funcs[] = { 53 "\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>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, 54 }; 55 56 // linux 57 static const char *linux_keys[] = { 58 "\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 }; 60 static const char *linux_funcs[] = { 61 "", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J", "\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "", 62 }; 63 64 // xterm 65 static const char *xterm_keys[] = { 66 "\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 }; 68 static const char *xterm_funcs[] = { 69 "\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>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ, 70 }; 71 72 static struct term { 73 const char *name; 74 const char **keys; 75 const char **funcs; 76 } terms[] = { 77 {"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs}, 78 {"Eterm", eterm_keys, eterm_funcs}, 79 {"screen", screen_keys, screen_funcs}, 80 {"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs}, 81 {"linux", linux_keys, linux_funcs}, 82 {"xterm", xterm_keys, xterm_funcs}, 83 {0, 0, 0}, 84 }; 85 86 static bool init_from_terminfo = false; 87 static const char **keys; 88 static const char **funcs; 89 90 static int try_compatible(const char *term, const char *name, 91 const char **tkeys, const char **tfuncs) 92 { 93 if (strstr(term, name)) { 94 keys = tkeys; 95 funcs = tfuncs; 96 return 0; 97 } 98 99 return EUNSUPPORTED_TERM; 100 } 101 102 static int init_term_builtin(void) 103 { 104 int i; 105 const char *term = getenv("TERM"); 106 107 if (term) { 108 for (i = 0; terms[i].name; i++) { 109 if (!strcmp(terms[i].name, term)) { 110 keys = terms[i].keys; 111 funcs = terms[i].funcs; 112 return 0; 113 } 114 } 115 116 /* let's do some heuristic, maybe it's a compatible terminal */ 117 if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0) 118 return 0; 119 if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0) 120 return 0; 121 if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0) 122 return 0; 123 if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0) 124 return 0; 125 if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0) 126 return 0; 127 if (try_compatible(term, "tmux", screen_keys, screen_funcs) == 0) 128 return 0; 129 /* let's assume that 'cygwin' is xterm compatible */ 130 if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0) 131 return 0; 132 } 133 134 return EUNSUPPORTED_TERM; 135 } 136 137 //---------------------------------------------------------------------- 138 // terminfo 139 //---------------------------------------------------------------------- 140 141 static char *read_file(const char *file) { 142 FILE *f = fopen(file, "rb"); 143 if (!f) 144 return 0; 145 146 struct stat st; 147 if (fstat(fileno(f), &st) != 0) { 148 fclose(f); 149 return 0; 150 } 151 152 char *data = malloc(st.st_size); 153 if (!data) { 154 fclose(f); 155 return 0; 156 } 157 158 if (fread(data, 1, st.st_size, f) != (size_t)st.st_size) { 159 fclose(f); 160 free(data); 161 return 0; 162 } 163 164 fclose(f); 165 return data; 166 } 167 168 static char *terminfo_try_path(const char *path, const char *term) { 169 char tmp[4096]; 170 snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term); 171 tmp[sizeof(tmp)-1] = '\0'; 172 char *data = read_file(tmp); 173 if (data) { 174 return data; 175 } 176 177 // fallback to darwin specific dirs structure 178 snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term); 179 tmp[sizeof(tmp)-1] = '\0'; 180 return read_file(tmp); 181 } 182 183 static char *load_terminfo(void) { 184 char tmp[4096]; 185 const char *term = getenv("TERM"); 186 if (!term) { 187 return 0; 188 } 189 190 // if TERMINFO is set, no other directory should be searched 191 const char *terminfo = getenv("TERMINFO"); 192 if (terminfo) { 193 return terminfo_try_path(terminfo, term); 194 } 195 196 // next, consider ~/.terminfo 197 const char *home = getenv("HOME"); 198 if (home) { 199 snprintf(tmp, sizeof(tmp), "%s/.terminfo", home); 200 tmp[sizeof(tmp)-1] = '\0'; 201 char *data = terminfo_try_path(tmp, term); 202 if (data) 203 return data; 204 } 205 206 // next, TERMINFO_DIRS 207 const char *dirs = getenv("TERMINFO_DIRS"); 208 if (dirs) { 209 snprintf(tmp, sizeof(tmp), "%s", dirs); 210 tmp[sizeof(tmp)-1] = '\0'; 211 char *dir = strtok(tmp, ":"); 212 while (dir) { 213 const char *cdir = dir; 214 if (strcmp(cdir, "") == 0) { 215 cdir = "/usr/share/terminfo"; 216 } 217 char *data = terminfo_try_path(cdir, term); 218 if (data) 219 return data; 220 dir = strtok(0, ":"); 221 } 222 } 223 224 // fallback to /usr/share/terminfo 225 return terminfo_try_path("/usr/share/terminfo", term); 226 } 227 228 #define TI_MAGIC 0432 229 #define TI_ALT_MAGIC 542 230 #define TI_HEADER_LENGTH 12 231 #define TB_KEYS_NUM 22 232 233 static const char *terminfo_copy_string(char *data, int str, int table) { 234 const int16_t off = *(int16_t*)(data + str); 235 const char *src = data + table + off; 236 int len = strlen(src); 237 char *dst = malloc(len+1); 238 strcpy(dst, src); 239 return dst; 240 } 241 242 static const int16_t ti_funcs[] = { 243 28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88, 244 }; 245 246 static const int16_t ti_keys[] = { 247 66, 68 /* apparently not a typo; 67 is F10 for whatever reason */, 69, 248 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82, 81, 87, 61, 249 79, 83, 250 }; 251 252 static int init_term(void) { 253 int i; 254 char *data = load_terminfo(); 255 if (!data) { 256 init_from_terminfo = false; 257 return init_term_builtin(); 258 } 259 260 int16_t *header = (int16_t*)data; 261 262 const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2; 263 264 if ((header[1] + header[2]) % 2) { 265 // old quirk to align everything on word boundaries 266 header[2] += 1; 267 } 268 269 const int str_offset = TI_HEADER_LENGTH + 270 header[1] + header[2] + number_sec_len * header[3]; 271 const int table_offset = str_offset + 2 * header[4]; 272 273 keys = malloc(sizeof(const char*) * (TB_KEYS_NUM+1)); 274 for (i = 0; i < TB_KEYS_NUM; i++) { 275 keys[i] = terminfo_copy_string(data, 276 str_offset + 2 * ti_keys[i], table_offset); 277 } 278 keys[TB_KEYS_NUM] = 0; 279 280 funcs = malloc(sizeof(const char*) * T_FUNCS_NUM); 281 // the last two entries are reserved for mouse. because the table offset is 282 // not there, the two entries have to fill in manually 283 for (i = 0; i < T_FUNCS_NUM-2; i++) { 284 funcs[i] = terminfo_copy_string(data, 285 str_offset + 2 * ti_funcs[i], table_offset); 286 } 287 288 funcs[T_FUNCS_NUM-2] = ENTER_MOUSE_SEQ; 289 funcs[T_FUNCS_NUM-1] = EXIT_MOUSE_SEQ; 290 291 init_from_terminfo = true; 292 free(data); 293 return 0; 294 } 295 296 static void shutdown_term(void) { 297 if (init_from_terminfo) { 298 int i; 299 for (i = 0; i < TB_KEYS_NUM; i++) { 300 free((void*)keys[i]); 301 } 302 // the last two entries are reserved for mouse. because the table offset 303 // is not there, the two entries have to fill in manually and do not 304 // need to be freed. 305 for (i = 0; i < T_FUNCS_NUM-2; i++) { 306 free((void*)funcs[i]); 307 } 308 free(keys); 309 free(funcs); 310 } 311 }