-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtermutil.c
More file actions
236 lines (204 loc) · 9.98 KB
/
termutil.c
File metadata and controls
236 lines (204 loc) · 9.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include "termutil.h"
static struct TUInfo *TUINT_GetInfo(void) {
static struct TUInfo T = {};
// struct termios origattr;
// struct termios rawattr;
// ----------------------------------------------------------------------
// unsigned int init : 1; // Whether our structure is initialized
// unsigned int raw : 1; // Whether we're currently in raw mode
// ----------------------------------------------------------------------
// unsigned int utf8 : 1; // Whether UTF-8 is supported (otherwise ASCII)
// unsigned int cjk : 1; // Whether doublewidth CJK chars are supported
// unsigned int arw : 1; // Whether arabic ligatures are wide
// unsigned int em : 1; // Whether emoji are supported
// unsigned int emu : 1; // Whether emoji are unaligned (need forced)
// unsigned int emj : 1; // Whether emoji can be joined
return &T;
}
int TUInitialize(bool rawmodeenable) {
struct TUInfo *T = TUINT_GetInfo();
unsigned char c;
size_t row;
size_t col;
const char tpem1[] = "\xF0\x9F\x8F\x9B"; // U+1F3DB Classical Building 🏛
const char tpem2[] = "\xE2\x9A\x96"; // U+2696 Scales ⚖
const char tpem3[] = "\xE2\x9A\x94"; // U+2694 Crossed Swords ⚔
const char tpem4[] = "\xE2\x8C\x9B"; // U+231B Hourglass ⌛
const char tpem5[] = "\xE2\x98\x95"; // U+2615 Hot Beverage ☕
const char tpem6[] = "\xE2\x9B\xA9"; // U+26E9 Shinto Shrine ⛩
const char tpem1f[] = "\xF0\x9F\x8F\x9B\xEF\xB8\x8F"; // U+1F3DB Classical Building 🏛
const char tpem2f[] = "\xE2\x9A\x96\xEF\xB8\x8F"; // U+2696 Scales ⚖
const char tpem3f[] = "\xE2\x9A\x94\xEF\xB8\x8F"; // U+2694 Crossed Swords ⚔
const char tpem4f[] = "\xE2\x8C\x9B\xEF\xB8\x8F"; // U+231B Hourglass ⌛
const char tpem5f[] = "\xE2\x98\x95\xEF\xB8\x8F"; // U+2615 Hot Beverage ☕
const char tpem6f[] = "\xE2\x9B\xA9\xEF\xB8\x8F"; // U+26E9 Shinto Shrine ⛩
const char uc6_0[] = "\xF0\x9F\x8C\xBE"; // U+1F33E Ear of Rice 🌾
const char uc6_1[] = "\xF0\x9F\x98\x80"; // U+1F600 Grinning Face 😀
const char uc7_0[] = "\xF0\x9F\x9B\xA0"; // U+1F6E0 Hammer and Wrench 🛠
const char uc7emoji1[] = "\xF0\x9F\x95\xB4"; // U+1F574 🕴
const char uc7emoji2[] = "\xF0\x9F\x95\xB5"; // U+1F575 🕵
const char uc7emoji3[] = "\xF0\x9F\x95\xB6"; // U+1F576 🕶
const char uc7emoji4[] = "\xF0\x9F\x95\xB9"; // U+1F579 🕹
const char uc7emoji5[] = "\xF0\x9F\x96\x95"; // U+1F595 🖕
const char uc7emoji6[] = "\xF0\x9F\x96\x96"; // U+1F596 🖖
const char uc7emoji7[] = "\xF0\x9F\x93\x9D"; // U+1F4DD 📝
const char uc7emoji8[] = "\xF0\x9F\x9B\xA0"; // U+1F6E0 🛠
const char uc7emoji9[] = "\xF0\x9F\x8F\x8D"; // U+1F3CD 🏍
const char uc7emoji1f[] = "\xF0\x9F\x95\xB4\xEF\xB8\x8F"; // U+1F574 🕴
const char uc7emoji2f[] = "\xF0\x9F\x95\xB5\xEF\xB8\x8F"; // U+1F575 🕵
const char uc7emoji3f[] = "\xF0\x9F\x95\xB6\xEF\xB8\x8F"; // U+1F576 🕶
const char uc7emoji4f[] = "\xF0\x9F\x95\xB9\xEF\xB8\x8F"; // U+1F579 🕹
const char uc7emoji5f[] = "\xF0\x9F\x96\x95\xEF\xB8\x8F"; // U+1F595 🖕
const char uc7emoji6f[] = "\xF0\x9F\x96\x96\xEF\xB8\x8F"; // U+1F596 🖖
const char uc7emoji7f[] = "\xF0\x9F\x93\x9D\xEF\xB8\x8F"; // U+1F4DD 📝
const char uc7emoji8f[] = "\xF0\x9F\x9B\xA0\xEF\xB8\x8F"; // U+1F6E0 🛠
const char uc7emoji9f[] = "\xF0\x9F\x8F\x8D\xEF\xB8\x8F"; // U+1F3CD 🏍
const char uc8_0[] = "\xF0\x9F\x8F\xB9"; // U+1F3F9 Bow and Arrow 🏹
const char uc9_0[] = "\xF0\x9F\x9B\xB6"; // U+1F6F6 Canoe 🛶
const char ucA_0[] = "\xF0\x9F\xA7\x91"; // U+1F9D1 Adult 🧑
const char ucB_0[] = "\xF0\x9F\xA5\xBA"; // U+1F97A Face w Pleading Eyes 🥺
const char ucC_0[] = "\xF0\x9F\xAA\x93"; // U+1FA93 Axe 🪓
const char ucD_0[] = "\xF0\x9F\xAA\xB2"; // U+1FAB2 Beetle 🪲
#define env(a) (getenv(a))
#define streq(a,b) (!strcmp(a,b))
#define strhas(a,b) (!!strstr(a,b))
#define termname(a) (strncpy(T->termdesc, a, sizeof(T->termdesc)-1))
#define setcaps(u, c, a, e, eu, ej) do { \
T->utf8 = u; /* Whether UTF-8 is supported (otherwise ASCII) */ \
T->cjk = c; /* Whether doublewidth CJK chars are supported */ \
T->arw = a; /* Whether arabic ligatures are wide */ \
T->em = e; /* Whether emoji are supported */ \
T->emu = eu; /* Whether emoji are unaligned (need forced) */ \
T->emj = ej; /* Whether emoji can be joined */ \
} while (0)
#define UTF8 1
#define CJK 1
#define ARWIDE 1
#define EMOJIS 1
#define EMOJIFIXALIGN 1
#define EMOJIJOIN 1
if (!T->init) {
// If we haven't stored the original terminal settings or derived a
// raw mode setting yet, let's do that. But first let's ensure we're
// in an interactive terminal.
if (!isatty(STDIN_FILENO)) return -ENOTTY;
if (!env("TERM")) return -ENOTTY;
if (tcgetattr(STDIN_FILENO, &T->origattr) == -1) return -ENOTTY;
T->rawattr = T->origattr;
T->rawattr.c_lflag &= ~(ECHO|ICANON|IEXTEN|ISIG);
T->rawattr.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
T->rawattr.c_oflag &= ~(OPOST);
T->rawattr.c_cflag &= ~(CSIZE|PARENB);
T->rawattr.c_cflag |= (CS8);
T->rawattr.c_cc[VMIN] = 0;
T->rawattr.c_cc[VTIME] = 1;
// Now, use some heuristics to figure out what our text output looks
// like (UTF-8? Emoji behavior? Etc.).
// First, we'll look at some TERM variables that can be useful
if (streq(env("TERM"), "xterm-kitty")
setcaps(UTF8, CJK, !ARWIDE, EMOJIS, EMOJIFIXALIGN, EMOJIJOIN);
else if (streq(env("TERM"), "ansi")
setcaps(!UTF8, !CJK, !ARWIDE, !EMOJIS, !EMOJIFIXALIGN, !EMOJIJOIN);
else if (streq(env("TERM"), "uxrvt")
setcaps(UTF8, !CJK, !ARWIDE, !EMOJIS, !EMOJIFIXALIGN, !EMOJIJOIN);
else if (streq(env("TERM"), "mlterm") setcaps(1, 0, 0, 0, 0, 0);
else if (streq(env("TERM"), "xterm") setcaps(1, 1, 0, 1, 1, 0);
else if (streq(env("TERM"), "contour") setcaps(1, 1, 0, 1, 0, 1);
else if (streq(env("TERM"), "xterm-256color") {
if (!env("SSH_CLIENT") { // Running locally, no SSH
if (streq(env("TERM_PROGRAM"), "Apple_Terminal")) {
termname("macOS Terminal.app");
setcaps(1, 1, 1, 1, 0, 1);
} else if (streq(env("TERM_PROGRAM"), "WezTerm")) {
termname("WezTerm");
setcaps(1, 1, 1, 1, 0, 1);
} else if (env("GNOME_TERMINAL_SERVICE")) {
termname("GNOME Terminal");
setcaps(1, 1, 1, 1, 0, 1);
}
} else { // Running over SSH, let's try some ANSI escape codes
write(STDOUT_FILENO, "\e[6n", 4);
if (read(STDIN_FILENO,&c,1) < 1) {
// Weird iOS terminal apps like xTerminal and iTerminal
// don't support getting the cursor position, but they
// do support UTF-8 and emoji.
termname("iOS xTerminal/iTerminal");
setcaps(1, 1, 1, 1, 1, 0);
} else if (c != '\e' || scanf("[%zd;%zdR",&row,&col) != 2) {
// We read something in response but it's super jacked up
termname("Unknown SSH Terminal");
setcaps(0, 0, 0, 0, 0, 0);
} else {
// We got the cursor position, let's use that!
}
}
}
T->init = true;
}
if (rawmodeenable) {
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &T->rawattr) == -1) return -errno;
T->raw = true;
}
#undef setcaps
#undef strhas
#undef streq
#undef env
return 0;
}
// **********SCRATCH***********
// write(STDOUT_FILENO, "\e[6n", 4);
// if (read(STDIN_FILENO, &c, 1) < 1 ||
// c != '\e' ||
// (scanf("[%zd;%zdR", &row, &col) != 2)) {
// // Can't get cursor position :(
// }
// **********SCRATCH***********
// static ssize_t TU_INTERNAL_GetCursorMoveLength(const char * const text) {
// size_t row = 0;
// size_t col = 0;
// ssize_t chk;
// unsigned char c = 0;
// if (!TUTERM.rawmode) {
// fprintf(stderr, "Invalid use of internal API without Raw Mode enabled.\n");
// exit(EXIT_FAILURE);
// }
// write(STDOUT_FILENO, "\e[0G", 4); // Start of line
// write(STDOUT_FILENO, "\e[8m", 4); // Make hidden
// write(STDOUT_FILENO, text, strlen(text)); // Write text
// write(STDOUT_FILENO, "\e[28m", 5); // Undo hidden
// write(STDOUT_FILENO, "\e[6n", 4); // Get cursor pos'n
// write(STDOUT_FILENO, "\e[0G", 4); // Back to start
// chk = read(STDIN_FILENO, &c, 1);
// if (chk < 1 || c != '\e') {
// fprintf(stderr, "Terminal response did not start with ESC: %s\n", strerror(errno));
// exit(EXIT_FAILURE);
// }
// chk = scanf("[%zu;%zuR", &row, &col);
// if (chk != 2) {
// fprintf(stderr, "Terminal response did not contain cursor position: %s\n", strerror(errno));
// exit(EXIT_FAILURE);
// }
// return (col) ? col - 1 : 0;
// }
int main(void) {
ssize_t chk;
int retval = 0;
TUInit();
TURawMode(true);
if ((chk = TUGetCursorMoveLength(u8"丟")) < 0) goto ERRORHANDLING;
printf("Length of \'%s\' is %zu\r\n", u8"丟", chk);
if ((chk = TUGetCursorMoveLength(u8"🧑🌾")) < 0) goto ERRORHANDLING;
printf("Length of \'%s\' is %zu\r\n", u8"🧑🌾", chk);
TURawMode(false);
return 0;
ERRORHANDLING:
printf("Failed to get cursor move length: %s\r\n", strerror(-chk));
return chk;
}