Skip to content

Commit e296022

Browse files
authored
Merge pull request elfmz#3096 from exkrexpexfex/vs16tty
TTY:Trying to detect variation selector 16 support
2 parents 2062de3 + da03353 commit e296022

4 files changed

Lines changed: 89 additions & 2 deletions

File tree

WinPort/src/Backend/WinPortMain.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,82 @@ static void SetupStdHandles()
125125
}
126126
}
127127

128+
int detect_vs16_width() {
129+
// U+25AB + U+FE0F: ▫️
130+
const char sym[] = "\xE2\x96\xAB\xEF\xB8\x8F";
131+
132+
termios orig, raw;
133+
tcgetattr(STDIN_FILENO, &orig);
134+
raw = orig;
135+
raw.c_lflag &= ~(ICANON | ECHO);
136+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
137+
138+
auto readPos = []() {
139+
int row = -1, col = -1;
140+
char c;
141+
std::string buf;
142+
while (true) {
143+
fd_set rfds;
144+
FD_ZERO(&rfds);
145+
FD_SET(STDIN_FILENO, &rfds);
146+
147+
struct timeval tv;
148+
tv.tv_sec = 0;
149+
tv.tv_usec = 200000; //timeout 200 msec
150+
151+
int rv = select(STDIN_FILENO + 1, &rfds, nullptr, nullptr, &tv);
152+
if (rv <= 0) return std::make_pair(-1,-1);
153+
154+
if (read(STDIN_FILENO, &c, 1) != 1) return std::make_pair(-1,-1);
155+
buf.push_back(c);
156+
157+
if ( c == 'R') {
158+
auto esc_pos = buf.rfind('\x1b');
159+
if (esc_pos != std::string::npos && esc_pos < buf.size() - 1
160+
&& sscanf(buf.c_str() + esc_pos, "\x1b[%d;%dR", &row, &col) == 2) {
161+
return std::make_pair(row, col); // This is the cursor position: SUCCESS
162+
}
163+
buf.clear();
164+
}
165+
166+
if (buf.size() > 64) buf.clear();
167+
}
168+
};
169+
170+
// Query cursor before printing
171+
write(STDOUT_FILENO, "\x1b[6n", 4);
172+
write(STDOUT_FILENO, "\x1b[5n", 4);
173+
tcdrain(STDOUT_FILENO);
174+
auto [start_row, start_col] = readPos();
175+
176+
// Print the VS16 symbol
177+
write(STDOUT_FILENO, sym, strlen(sym));
178+
tcdrain(STDOUT_FILENO);
179+
180+
// Query cursor after printing
181+
write(STDOUT_FILENO, "\x1b[6n", 4);
182+
write(STDOUT_FILENO, "\x1b[5n", 4);
183+
tcdrain(STDOUT_FILENO);
184+
auto [end_row, end_col] = readPos();
185+
186+
// Remove the printed symbol
187+
if (start_col >= 0 && end_col >= 0) {
188+
int w = end_col - start_col;
189+
if (w > 0) {
190+
char seq[32];
191+
int n = snprintf(seq, sizeof(seq), "\x1b[%d;%dH", start_row, start_col);
192+
if (n > 0) {
193+
write(STDOUT_FILENO, seq, (size_t)n);
194+
write(STDOUT_FILENO, "\x1b[K", 3);
195+
tcdrain(STDOUT_FILENO);
196+
}
197+
}
198+
}
199+
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig);
200+
201+
if (start_col < 0 || end_col < 0) return -1;
202+
return end_col - start_col; // 1 = no emoji width, 2 = VS16 rendered full-width
203+
}
128204

129205
class NormalizeTerminalState
130206
{
@@ -440,6 +516,10 @@ extern "C" int WinPortMain(const char *full_exe_path, int argc, char **argv, int
440516
}
441517
}
442518
}
519+
//check variation selector 16 is usable before reassigning stdout
520+
if (arg_opts.tty && arg_opts.nodetect == NODETECT_NONE) {
521+
g_use_vs16 = detect_vs16_width() == 2;
522+
}
443523

444524
SetupStdHandles();
445525
if (!arg_opts.mortal) {

far2l/src/global.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,5 @@ DWORD RedrawTimeout = 200;
110110
FormatScreen FS;
111111

112112
DWORD ErrorMode;
113+
114+
bool g_use_vs16=true;

far2l/src/global.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,6 @@ extern DWORD RedrawTimeout;
103103
extern FormatScreen FS;
104104

105105
extern DWORD ErrorMode;
106+
107+
extern __attribute__((visibility("default"))) bool g_use_vs16;
108+

utils/include/CharClasses.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <memory>
66
#include <unordered_map>
77

8+
extern bool g_use_vs16;
9+
810
class CharClasses
911
{
1012
const wchar_t _c;
@@ -116,13 +118,13 @@ class CharClasses
116118
static inline bool IsFullWidth(const wchar_t* p) {
117119
if (!p || *p <= ASCII_MAX) return false;
118120
//Variation Selector-16 indicates that the previous character should be rendered as an image
119-
if (*(p + 1) == VARIATION_SELECTOR_16) return true;
121+
if (g_use_vs16 && *(p + 1) == VARIATION_SELECTOR_16) return true;
120122
return Get(*p) & IS_FULLWIDTH;
121123
}
122124
static inline bool IsFullWidth(const wchar_t* p, size_t n) {
123125
if (!p || *p <= ASCII_MAX) return false;
124126
//Variation Selector-16 indicates that the previous character should be rendered as an image
125-
if (n > 1 && *(p + 1) == VARIATION_SELECTOR_16) return true;
127+
if (g_use_vs16 && n > 1 && *(p + 1) == VARIATION_SELECTOR_16) return true;
126128
return Get(*p) & IS_FULLWIDTH;
127129
}
128130
static inline bool IsFullWidth(wchar_t c) {

0 commit comments

Comments
 (0)