ohmu
·
2026-10-15
1#include "terminaltheme.h"
2#include "common/io.h"
3#include "common/strutil.h"
4
5#include <inttypes.h>
6
7static bool detectByEscapeCode(FFTerminalThemeResult* result) {
8 // Windows Terminal removes all `\e`s in its output
9 if (ffGetTerminalResponse("\e]10;?\e\\" /*fg*/ "\e]11;?\e\\" /*bg*/,
10 6,
11 "%*[^0-9]10;rgb:%" SCNx16 "/%" SCNx16 "/%" SCNx16 /*"\e\\"*/ "%*[^0-9]11;rgb:%" SCNx16 "/%" SCNx16 "/%" SCNx16 /*"\e\\"*/,
12 &result->fg.r,
13 &result->fg.g,
14 &result->fg.b,
15 &result->bg.r,
16 &result->bg.g,
17 &result->bg.b) == nullptr) {
18 if (result->fg.r > 0x0100 || result->fg.g > 0x0100 || result->fg.b > 0x0100) {
19 result->fg.r /= 0x0100, result->fg.g /= 0x0100, result->fg.b /= 0x0100;
20 }
21 if (result->bg.r > 0x0100 || result->bg.g > 0x0100 || result->bg.b > 0x0100) {
22 result->bg.r /= 0x0100, result->bg.g /= 0x0100, result->bg.b /= 0x0100;
23 }
24 } else {
25 return false;
26 }
27
28 return true;
29}
30
31static FFTerminalThemeColor fgbgToColor(int num) {
32 // https://github.com/dalance/termbg/blob/13c478a433fa182e65c401d26a1e7792a7f7f453/src/lib.rs#L251
33 switch (num) {
34 case 0:
35 return (FFTerminalThemeColor) { 0, 0, 0, false }; // black
36 case 1:
37 return (FFTerminalThemeColor) { 205, 0, 0, false }; // red
38 case 2:
39 return (FFTerminalThemeColor) { 0, 205, 0, false }; // green
40 case 3:
41 return (FFTerminalThemeColor) { 205, 205, 0, false }; // yellow
42 case 4:
43 return (FFTerminalThemeColor) { 0, 0, 238, false }; // blue
44 case 5:
45 return (FFTerminalThemeColor) { 205, 0, 205, false }; // magenta
46 case 6:
47 return (FFTerminalThemeColor) { 0, 205, 205, false }; // cyan
48 case 7:
49 return (FFTerminalThemeColor) { 229, 229, 229, false }; // white
50
51 case 8:
52 return (FFTerminalThemeColor) { 127, 127, 127, false }; // bright black
53 case 9:
54 return (FFTerminalThemeColor) { 255, 0, 0, false }; // bright red
55 case 10:
56 return (FFTerminalThemeColor) { 0, 255, 0, false }; // bright green
57 case 11:
58 return (FFTerminalThemeColor) { 255, 255, 0, false }; // bright yellow
59 case 12:
60 return (FFTerminalThemeColor) { 92, 92, 255, false }; // bright blue
61 case 13:
62 return (FFTerminalThemeColor) { 255, 0, 255, false }; // bright magenta
63 case 14:
64 return (FFTerminalThemeColor) { 0, 255, 255, false }; // bright cyan
65 case 15:
66 return (FFTerminalThemeColor) { 255, 255, 255, false }; // bright white
67
68 default:
69 return (FFTerminalThemeColor) { 0, 0, 0, false }; // invalid
70 }
71}
72
73static bool detectByEnv(FFTerminalThemeResult* result) {
74 const char* color = getenv("COLORFGBG"); // 7;0
75
76 if (!ffStrSet(color)) {
77 return false;
78 }
79
80 int f, g;
81 if (sscanf(color, "%d;%d", &f, &g) != 2) {
82 return false;
83 }
84
85 result->fg = fgbgToColor(f);
86 result->bg = fgbgToColor(g);
87 return true;
88}
89
90static inline bool detectColor(FFTerminalThemeResult* result, bool forceEnv) {
91 if (!forceEnv && detectByEscapeCode(result)) {
92 return true;
93 }
94
95 return detectByEnv(result);
96}
97
98bool ffDetectTerminalTheme(FFTerminalThemeResult* result, bool forceEnv) {
99 if (!detectColor(result, forceEnv)) {
100 return false;
101 }
102 result->fg.dark = result->fg.r * 299 + result->fg.g * 587 + result->fg.b * 114 < 128000;
103 result->bg.dark = result->bg.r * 299 + result->bg.g * 587 + result->bg.b * 114 < 128000;
104 return true;
105}