💾 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

View Raw

More Information

-=-=-=-=-=-=-

heartbeat

Log

Files

Refs

README

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 }