GitHub

@@ -348,3 +348,144 @@ mirb_term_clear_below(void)

348348

{

349349

printf("\033[J");

350350

}

351+352+

#if !defined(_WIN32) && !defined(_WIN64)

353+

/*

354+

* Query terminal background color using OSC 11 escape sequence

355+

*

356+

* Protocol:

357+

* Send: ESC ] 11 ; ? ESC \ (or BEL instead of ESC \)

358+

* Recv: ESC ] 11 ; rgb:RRRR/GGGG/BBBB ESC \

359+

*

360+

* The response uses 16-bit color values (0000-FFFF per component).

361+

* Some terminals use 8-bit (00-FF) format instead.

362+

*/

363+

mirb_bg_color

364+

mirb_term_query_bg_color(int timeout_ms)

365+

{

366+

struct termios old_term, new_term;

367+

char buf[64];

368+

size_t total = 0;

369+

ssize_t n;

370+

int r, g, b;

371+

mirb_bg_color result = MIRB_BG_UNKNOWN;

372+

fd_set fds;

373+

struct timeval tv;

374+

const char *p;

375+376+

/* Must be a terminal */

377+

if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) {

378+

return MIRB_BG_UNKNOWN;

379+

}

380+381+

/* Save original terminal settings */

382+

if (tcgetattr(STDIN_FILENO, &old_term) < 0) {

383+

return MIRB_BG_UNKNOWN;

384+

}

385+386+

/* Flush any pending input first */

387+

tcflush(STDIN_FILENO, TCIFLUSH);

388+389+

/* Disable echo and canonical mode for raw read */

390+

new_term = old_term;

391+

new_term.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL);

392+

new_term.c_iflag &= ~(IXON | IXOFF | ICRNL);

393+

new_term.c_cc[VMIN] = 0;

394+

new_term.c_cc[VTIME] = 0;

395+396+

/* TCSAFLUSH: flush I/O and apply settings */

397+

if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new_term) < 0) {

398+

return MIRB_BG_UNKNOWN;

399+

}

400+401+

/* Wait for terminal settings to take effect */

402+

tcdrain(STDOUT_FILENO);

403+404+

/* Send OSC 11 query: ESC ] 11 ; ? ESC \ */

405+

if (write(STDOUT_FILENO, "\033]11;?\033\\", 8) != 8) {

406+

goto restore;

407+

}

408+

/* Ensure query is sent to terminal */

409+

tcdrain(STDOUT_FILENO);

410+

/* Give terminal time to process and respond */

411+

usleep(50000); /* 50ms */

412+413+

/* Read response with timeout - loop to get complete response */

414+

while (total < sizeof(buf) - 1) {

415+

int sel;

416+

FD_ZERO(&fds);

417+

FD_SET(STDIN_FILENO, &fds);

418+

tv.tv_sec = timeout_ms / 1000;

419+

tv.tv_usec = (timeout_ms % 1000) * 1000;

420+421+

sel = select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);

422+

if (sel < 0) {

423+

if (errno == EINTR) continue; /* Retry on signal interrupt */

424+

break; /* Other error */

425+

}

426+

if (sel == 0) break; /* Timeout */

427+428+

n = read(STDIN_FILENO, buf + total, sizeof(buf) - 1 - total);

429+

if (n <= 0) {

430+

if (n < 0 && errno == EINTR) continue; /* Retry on signal interrupt */

431+

break;

432+

}

433+

total += (size_t)n;

434+435+

/* Check for terminator: ESC \ (0x1b 0x5c) or BEL (0x07) */

436+

if (total >= 2 && buf[total-2] == '\033' && buf[total-1] == '\\') break;

437+

if (total >= 1 && buf[total-1] == '\007') break;

438+439+

/* Reduce timeout for subsequent reads */

440+

timeout_ms = 10;

441+

}

442+443+

if (total == 0) {

444+

goto restore;

445+

}

446+

buf[total] = '\0';

447+448+

/* Parse response: look for "rgb:" followed by hex values */

449+

p = strstr(buf, "rgb:");

450+

if (p) {

451+

p += 4;

452+

/* Try 16-bit format: rgb:RRRR/GGGG/BBBB */

453+

if (sscanf(p, "%4x/%4x/%4x", &r, &g, &b) == 3) {

454+

/* Normalize to 8-bit range */

455+

r >>= 8; g >>= 8; b >>= 8;

456+

}

457+

/* Try 8-bit format: rgb:RR/GG/BB */

458+

else if (sscanf(p, "%2x/%2x/%2x", &r, &g, &b) == 3) {

459+

/* Already 8-bit */

460+

}

461+

else {

462+

goto restore;

463+

}

464+465+

/* Calculate relative luminance (ITU-R BT.709 simplified) */

466+

/* Y = 0.2126*R + 0.7152*G + 0.0722*B */

467+

/* Scale: 0-255 input, threshold at ~127.5 */

468+

int luminance = (2126 * r + 7152 * g + 722 * b) / 10000;

469+

result = (luminance < 128) ? MIRB_BG_DARK : MIRB_BG_LIGHT;

470+

}

471+472+

restore:

473+

/* Flush any remaining input before restoring terminal */

474+

tcflush(STDIN_FILENO, TCIFLUSH);

475+

tcsetattr(STDIN_FILENO, TCSAFLUSH, &old_term);

476+

return result;

477+

}

478+479+

#else /* Windows */

480+481+

mirb_bg_color

482+

mirb_term_query_bg_color(int timeout_ms)

483+

{

484+

(void)timeout_ms;

485+

/* Windows Terminal supports OSC 11, but implementation requires

486+

* different I/O handling. For now, return unknown and rely on

487+

* fallback detection methods. */

488+

return MIRB_BG_UNKNOWN;

489+

}

490+491+

#endif /* _WIN32 */

Read the original on github.com ↗