@@ -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
129205class 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 ) {
0 commit comments