diff --git a/uppsrc/CtrlLib/ChX11.cpp b/uppsrc/CtrlLib/ChX11.cpp index e9e1069c1..c62171a2d 100644 --- a/uppsrc/CtrlLib/ChX11.cpp +++ b/uppsrc/CtrlLib/ChX11.cpp @@ -3,104 +3,175 @@ #ifdef GUI_X11 namespace Upp { + +#define LLOG(x) + +VectorMap GetXSettings() +{ + VectorMap map; + + Window owner = XGetSelectionOwner(Xdisplay, XAtom("_XSETTINGS_S0")); + + if(owner == None) { + LLOG("No XSettings manager found for screen " << screen); + return map; + } + + Atom settings_atom = XAtom("_XSETTINGS_SETTINGS"); + String data = GetProperty(owner, settings_atom, settings_atom); + + if(data.IsEmpty()) { + LLOG("Failed to read XSettings property or property is empty."); + return map; + } + + const char *ptr = data.Begin(); + const char *lim = data.End(); + + if(data.GetCount() < 12) + return map; + + byte be = ptr[0]; + + auto Peek32 = [&](const char *p) { + return be ? Peek32be(p) : Peek32le(p); + }; + + auto Peek16 = [&](const char *p) { + return be ? Peek16be(p) : Peek16le(p); + }; + + int n_settings = Peek32(ptr + 8); + ptr += 12; + + for(int i = 0; i < n_settings && ptr < lim; i++) { + if(lim - ptr < 4) + break; + + byte type = *ptr++; // 0 = Integer, 1 = String, 2 = Color + ptr++; // unused + + int name_len = Peek16(ptr); + ptr += 2; + if(lim - ptr < name_len) + break; + String name(ptr, name_len); + ptr += (name_len + 3) & ~3; + + ptr += 4; // Last-change serial + + if(type == 0) { // Integer + if(lim - ptr < 4) + break; + map.GetAdd(name) = (int)Peek32(ptr); + ptr += 4; + } + else + if(type == 1) { // String + if(lim - ptr < 4) + break; + int str_len = Peek32(ptr); + ptr += 4; + if(lim - ptr < str_len) + break; + map.GetAdd(name) = String(ptr, str_len); + ptr += (str_len + 3) & ~3; // Align to 4 bytes + } + else + if(type == 2) { // Color - ignored + if(lim - ptr < 8) + break; + ptr += 8; + } + } + + return map; +} void ChHostSkin() { - String s = Sys("dump_xsettings"); - StringStream ss(s); - String font_name, theme; - int scaling = 1; - int xdpi = 98347; - while(!ss.IsEof()) { - String l = ss.GetLine(); - int q = l.Find(' '); - if(q >= 0) { - String id = l.Mid(0, q); - String value = l.Mid(q + 1); - if(id == "Gdk/WindowScalingFactor") - scaling = max(Atoi(value), 1); - if(id == "Gtk/FontName") - font_name = value; - if(id == "Xft/DPI") - xdpi = Nvl(StrInt(value), 98347); - if(id == "Net/ThemeName") - theme = value; - } - } - - int fontface = Font::ARIAL; - int fontheight = 13; - bool bold = false; - bool italic = false; - - const char *q = strrchr(font_name, ' '); - if(q) { - int h = atoi(q); - if(h) - fontheight = h; - String face(font_name, q); - fontface = Font::FindFaceNameIndex(face); - - if(fontface == 0) { - for(;;) { - const char *q = strrchr(face, ' '); - if(!q) break; - const char *s = q + 1; - if(stricmp(s, "Bold") == 0 || stricmp(s, "Heavy") == 0) - bold = true; - else - if(stricmp(s, "Italic") == 0 || stricmp(s, "Oblique") == 0) - italic = true; - else - if(stricmp(s, "Regular") == 0 || stricmp(s, "Light") || stricmp(s, "Medium")) - ; - else - continue; - face = String(~face, q); - } + VectorMap map = GetXSettings(); + try { + String font_name = map.Get("Gtk/FontName", ""); + String theme = map.Get("Net/ThemeName", ""); + //int scaling = map.Get("Gdk/WindowScalingFactor", 1); + int xdpi = map.Get("Xft/DPI", 98347); + + int fontface = Font::ARIAL; + int fontheight = 13; + bool bold = false; + bool italic = false; + + const char *q = strrchr(font_name, ' '); + if(q) { + int h = atoi(q); + if(h) + fontheight = h; + String face(font_name, q); fontface = Font::FindFaceNameIndex(face); + if(fontface == 0) { - if(ToUpper(face[0]) == 'M') - fontface = Font::COURIER; - else - if(ToUpper(face[0]) == 'S' && ToUpper(face[1]) == 'e') - fontface = Font::ROMAN; - else - fontface = Font::ARIAL; + for(;;) { + const char *q = strrchr(face, ' '); + if(!q) break; + const char *s = q + 1; + if(stricmp(s, "Bold") == 0 || stricmp(s, "Heavy") == 0) + bold = true; + else + if(stricmp(s, "Italic") == 0 || stricmp(s, "Oblique") == 0) + italic = true; + else + if(stricmp(s, "Regular") == 0 || stricmp(s, "Light") || stricmp(s, "Medium")) + ; + else + continue; + face = String(~face, q); + } + fontface = Font::FindFaceNameIndex(face); + if(fontface == 0) { + if(ToUpper(face[0]) == 'M') + fontface = Font::COURIER; + else + if(ToUpper(face[0]) == 'S' && ToUpper(face[1]) == 'e') + fontface = Font::ROMAN; + else + fontface = Font::ARIAL; + } } } - } + + Font gui_font = Font(fontface, fround(fontheight * xdpi / (72*1024.0))).Bold(bold).Italic(italic); + Font::SetDefaultFont(gui_font); - Font gui_font = Font(fontface, fround(fontheight * xdpi / (72*1024.0))).Bold(bold).Italic(italic); - Font::SetDefaultFont(gui_font); - - SColorFace_Write(Color(242, 241, 240)); - SColorMenu_Write(Color(242, 241, 240)); - SColorHighlight_Write(Color(50, 50, 250)); - - auto ThemeHasWord = [&](const char *text) { - int q = ToLower(theme).Find(text); - if(q >= 0) { - if(q > 0) { - int pc = theme[q - 1]; - if(!(IsUpper(theme[q]) && IsLower(pc) || !IsLetter(pc))) + SColorFace_Write(Color(242, 241, 240)); + SColorMenu_Write(Color(242, 241, 240)); + SColorHighlight_Write(Color(50, 50, 250)); + + auto ThemeHasWord = [&](const char *text) { + int q = ToLower(theme).Find(text); + if(q >= 0) { + if(q > 0) { + int pc = theme[q - 1]; + if(!(IsUpper(theme[q]) && IsLower(pc) || !IsLetter(pc))) + return false; + } + int l = strlen(text); + int nc = theme[q + l]; + if(!(IsLower(text[l - 1]) && IsUpper(nc) || !IsLetter(nc))) return false; + return true; } - int l = strlen(text); - int nc = theme[q + l]; - if(!(IsLower(text[l - 1]) && IsUpper(nc) || !IsLetter(nc))) - return false; - return true; - } - return false; - }; - - if(ThemeHasWord("dark") || ThemeHasWord("inverse") || ThemeHasWord("black")) - ChDarkSkin(); - else - ChStdSkin(); + return false; + }; + + if(ThemeHasWord("dark") || ThemeHasWord("inverse") || ThemeHasWord("black")) + ChDarkSkin(); + else + ChStdSkin(); + } + catch(ValueTypeError) {} } -} +}; #endif