diff options
Diffstat (limited to 'platform/linuxbsd/os_linuxbsd.cpp')
-rw-r--r-- | platform/linuxbsd/os_linuxbsd.cpp | 394 |
1 files changed, 359 insertions, 35 deletions
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index f4e94f1a91..d5a75edeea 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -34,29 +34,28 @@ #include "main/main.h" #include "servers/display_server.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif + #ifdef X11_ENABLED -#include "display_server_x11.h" +#include "x11/display_server_x11.h" #endif #ifdef HAVE_MNTENT #include <mntent.h> #endif +#include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - -#include <dlfcn.h> -#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/utsname.h> #include <unistd.h> -#ifdef FONTCONFIG_ENABLED -#include "fontconfig-so_wrap.h" -#endif - void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) { const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" }; @@ -125,6 +124,8 @@ void OS_LinuxBSD::initialize() { crash_handler.initialize(); OS_Unix::initialize_core(); + + system_dir_desktop_cache = get_system_dir(SYSTEM_DIR_DESKTOP); } void OS_LinuxBSD::initialize_joypads() { @@ -242,6 +243,204 @@ String OS_LinuxBSD::get_version() const { return uts.version; } +Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + + const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970` + const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA` + const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970` + + String vendor_device_id_mappings; + List<String> lspci_args; + lspci_args.push_back("-n"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", lspci_args, &vendor_device_id_mappings); + if (err != OK || vendor_device_id_mappings.is_empty()) { + return Vector<String>(); + } + + // Usually found under "VGA", but for example NVIDIA mobile/laptop adapters are often listed under "3D" and some AMD adapters are under "Display". + const String dc_vga = "0300"; // VGA compatible controller + const String dc_display = "0302"; // Display controller + const String dc_3d = "0380"; // 3D controller + + // splitting results by device class allows prioritizing, if multiple devices are found. + Vector<String> class_vga_device_candidates; + Vector<String> class_display_device_candidates; + Vector<String> class_3d_device_candidates; + +#ifdef MODULE_REGEX_ENABLED + RegEx regex_id_format = RegEx(); + regex_id_format.compile("^[a-f0-9]{4}:[a-f0-9]{4}$"); // e.g. `10de:13c2`; IDs are always in hexadecimal +#endif + + Vector<String> value_lines = vendor_device_id_mappings.split("\n", false); // example: `02:00.0 0300: 10de:13c2 (rev a1)` + for (const String &line : value_lines) { + Vector<String> columns = line.split(" ", false); + if (columns.size() < 3) { + continue; + } + String device_class = columns[1].trim_suffix(":"); + String vendor_device_id_mapping = columns[2]; + +#ifdef MODULE_REGEX_ENABLED + if (regex_id_format.search(vendor_device_id_mapping).is_null()) { + continue; + } +#endif + + if (device_class == dc_vga) { + class_vga_device_candidates.push_back(vendor_device_id_mapping); + } else if (device_class == dc_display) { + class_display_device_candidates.push_back(vendor_device_id_mapping); + } else if (device_class == dc_3d) { + class_3d_device_candidates.push_back(vendor_device_id_mapping); + } + } + + // Check results against currently used device (`card_name`), in case the user has multiple graphics cards. + const String device_lit = "Device"; // line of interest + class_vga_device_candidates = OS_LinuxBSD::lspci_device_filter(class_vga_device_candidates, dc_vga, device_lit, card_name); + class_display_device_candidates = OS_LinuxBSD::lspci_device_filter(class_display_device_candidates, dc_display, device_lit, card_name); + class_3d_device_candidates = OS_LinuxBSD::lspci_device_filter(class_3d_device_candidates, dc_3d, device_lit, card_name); + + // Get driver names and filter out invalid ones, because some adapters are dummys used only for passthrough. + // And they have no indicator besides certain driver names. + const String kernel_lit = "Kernel driver in use"; // line of interest + const String dummys = "vfio"; // for e.g. pci passthrough dummy kernel driver `vfio-pci` + Vector<String> class_vga_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_vga_device_candidates, kernel_lit, dummys); + Vector<String> class_display_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_display_device_candidates, kernel_lit, dummys); + Vector<String> class_3d_device_drivers = OS_LinuxBSD::lspci_get_device_value(class_3d_device_candidates, kernel_lit, dummys); + + static String driver_name; + static String driver_version; + + // Use first valid value: + for (const String &driver : class_3d_device_drivers) { + driver_name = driver; + break; + } + if (driver_name.is_empty()) { + for (const String &driver : class_display_device_drivers) { + driver_name = driver; + break; + } + } + if (driver_name.is_empty()) { + for (const String &driver : class_vga_device_drivers) { + driver_name = driver; + break; + } + } + + Vector<String> info; + info.push_back(driver_name); + + String modinfo; + List<String> modinfo_args; + modinfo_args.push_back(driver_name); + err = const_cast<OS_LinuxBSD *>(this)->execute("modinfo", modinfo_args, &modinfo); + if (err != OK || modinfo.is_empty()) { + info.push_back(""); // So that this method always either returns an empty array, or an array of length 2. + return info; + } + Vector<String> lines = modinfo.split("\n", false); + for (const String &line : lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == "version") { + driver_version = columns[1].strip_edges(); // example value: `510.85.02` on Linux/BSD + break; + } + } + + info.push_back(driver_version); + + return info; +} + +Vector<String> OS_LinuxBSD::lspci_device_filter(Vector<String> vendor_device_id_mapping, String class_suffix, String check_column, String whitelist) const { + // NOTE: whitelist can be changed to `Vector<String>`, if the need arises. + const String sep = ":"; + Vector<String> devices; + for (const String &mapping : vendor_device_id_mapping) { + String device; + List<String> d_args; + d_args.push_back("-d"); + d_args.push_back(mapping + sep + class_suffix); + d_args.push_back("-vmm"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", d_args, &device); // e.g. `lspci -d 10de:13c2:0300 -vmm` + if (err != OK) { + return Vector<String>(); + } else if (device.is_empty()) { + continue; + } + + Vector<String> device_lines = device.split("\n", false); + for (const String &line : device_lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == check_column) { + // for `column[0] == "Device"` this may contain `GM204 [GeForce GTX 970]` + bool is_valid = true; + if (!whitelist.is_empty()) { + is_valid = columns[1].strip_edges().contains(whitelist); + } + if (is_valid) { + devices.push_back(mapping); + } + break; + } + } + } + return devices; +} + +Vector<String> OS_LinuxBSD::lspci_get_device_value(Vector<String> vendor_device_id_mapping, String check_column, String blacklist) const { + // NOTE: blacklist can be changed to `Vector<String>`, if the need arises. + const String sep = ":"; + Vector<String> values; + for (const String &mapping : vendor_device_id_mapping) { + String device; + List<String> d_args; + d_args.push_back("-d"); + d_args.push_back(mapping); + d_args.push_back("-k"); + Error err = const_cast<OS_LinuxBSD *>(this)->execute("lspci", d_args, &device); // e.g. `lspci -d 10de:13c2 -k` + if (err != OK) { + return Vector<String>(); + } else if (device.is_empty()) { + continue; + } + + Vector<String> device_lines = device.split("\n", false); + for (const String &line : device_lines) { + Vector<String> columns = line.split(":", false, 1); + if (columns.size() < 2) { + continue; + } + if (columns[0].strip_edges() == check_column) { + // for `column[0] == "Kernel driver in use"` this may contain `nvidia` + bool is_valid = true; + const String value = columns[1].strip_edges(); + if (!blacklist.is_empty()) { + is_valid = !value.contains(blacklist); + } + if (is_valid) { + values.push_back(value); + } + break; + } + } + } + return values; +} + Error OS_LinuxBSD::shell_open(String p_uri) { Error ok; int err_code; @@ -280,7 +479,16 @@ Error OS_LinuxBSD::shell_open(String p_uri) { } bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; +#ifdef FONTCONFIG_ENABLED + if (p_feature == "system_fonts") { + return font_config_initialized; + } +#endif + if (p_feature == "pc") { + return true; + } + + return false; } uint64_t OS_LinuxBSD::get_embedded_pck_offset() const { @@ -373,15 +581,9 @@ Vector<String> OS_LinuxBSD::get_system_fonts() const { if (!font_config_initialized) { ERR_FAIL_V_MSG(Vector<String>(), "Unable to load fontconfig, system font support is disabled."); } + HashSet<String> font_names; Vector<String> ret; - - FcConfig *config = FcInitLoadConfigAndFonts(); - ERR_FAIL_COND_V(!config, ret); - - FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, nullptr); - ERR_FAIL_COND_V(!object_set, ret); - static const char *allowed_formats[] = { "TrueType", "CFF" }; for (size_t i = 0; i < sizeof(allowed_formats) / sizeof(const char *); i++) { FcPattern *pattern = FcPatternCreate(); @@ -404,8 +606,6 @@ Vector<String> OS_LinuxBSD::get_system_fonts() const { } FcPatternDestroy(pattern); } - FcObjectSetDestroy(object_set); - FcConfigDestroy(config); for (const String &E : font_names) { ret.push_back(E); @@ -416,25 +616,122 @@ Vector<String> OS_LinuxBSD::get_system_fonts() const { #endif } -String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, bool p_italic) const { +#ifdef FONTCONFIG_ENABLED +int OS_LinuxBSD::_weight_to_fc(int p_weight) const { + if (p_weight < 150) { + return FC_WEIGHT_THIN; + } else if (p_weight < 250) { + return FC_WEIGHT_EXTRALIGHT; + } else if (p_weight < 325) { + return FC_WEIGHT_LIGHT; + } else if (p_weight < 375) { + return FC_WEIGHT_DEMILIGHT; + } else if (p_weight < 390) { + return FC_WEIGHT_BOOK; + } else if (p_weight < 450) { + return FC_WEIGHT_REGULAR; + } else if (p_weight < 550) { + return FC_WEIGHT_MEDIUM; + } else if (p_weight < 650) { + return FC_WEIGHT_DEMIBOLD; + } else if (p_weight < 750) { + return FC_WEIGHT_BOLD; + } else if (p_weight < 850) { + return FC_WEIGHT_EXTRABOLD; + } else if (p_weight < 925) { + return FC_WEIGHT_BLACK; + } else { + return FC_WEIGHT_EXTRABLACK; + } +} + +int OS_LinuxBSD::_stretch_to_fc(int p_stretch) const { + if (p_stretch < 56) { + return FC_WIDTH_ULTRACONDENSED; + } else if (p_stretch < 69) { + return FC_WIDTH_EXTRACONDENSED; + } else if (p_stretch < 81) { + return FC_WIDTH_CONDENSED; + } else if (p_stretch < 93) { + return FC_WIDTH_SEMICONDENSED; + } else if (p_stretch < 106) { + return FC_WIDTH_NORMAL; + } else if (p_stretch < 137) { + return FC_WIDTH_SEMIEXPANDED; + } else if (p_stretch < 144) { + return FC_WIDTH_EXPANDED; + } else if (p_stretch < 162) { + return FC_WIDTH_EXTRAEXPANDED; + } else { + return FC_WIDTH_ULTRAEXPANDED; + } +} +#endif // FONTCONFIG_ENABLED + +Vector<String> OS_LinuxBSD::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const { #ifdef FONTCONFIG_ENABLED if (!font_config_initialized) { - ERR_FAIL_V_MSG(String(), "Unable to load fontconfig, system font support is disabled."); + ERR_FAIL_V_MSG(Vector<String>(), "Unable to load fontconfig, system font support is disabled."); } - String ret; + Vector<String> ret; + FcPattern *pattern = FcPatternCreate(); + if (pattern) { + FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); + FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); + FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); + FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); + + FcCharSet *char_set = FcCharSetCreate(); + for (int i = 0; i < p_text.size(); i++) { + FcCharSetAddChar(char_set, p_text[i]); + } + FcPatternAddCharSet(pattern, FC_CHARSET, char_set); - FcConfig *config = FcInitLoadConfigAndFonts(); - ERR_FAIL_COND_V(!config, ret); + FcLangSet *lang_set = FcLangSetCreate(); + FcLangSetAdd(lang_set, reinterpret_cast<const FcChar8 *>(p_locale.utf8().get_data())); + FcPatternAddLangSet(pattern, FC_LANG, lang_set); - FcObjectSet *object_set = FcObjectSetBuild(FC_FAMILY, FC_FILE, nullptr); - ERR_FAIL_COND_V(!object_set, ret); + FcConfigSubstitute(0, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcResult result; + FcPattern *match = FcFontMatch(0, pattern, &result); + if (match) { + char *file_name = nullptr; + if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { + if (file_name) { + ret.push_back(String::utf8(file_name)); + } + } + FcPatternDestroy(match); + } + FcPatternDestroy(pattern); + FcCharSetDestroy(char_set); + FcLangSetDestroy(lang_set); + } + return ret; +#else + ERR_FAIL_V_MSG(Vector<String>(), "Godot was compiled without fontconfig, system font support is disabled."); +#endif +} + +String OS_LinuxBSD::get_system_font_path(const String &p_font_name, int p_weight, int p_stretch, bool p_italic) const { +#ifdef FONTCONFIG_ENABLED + if (!font_config_initialized) { + ERR_FAIL_V_MSG(String(), "Unable to load fontconfig, system font support is disabled."); + } + + String ret; FcPattern *pattern = FcPatternCreate(); if (pattern) { + bool allow_substitutes = (p_font_name.to_lower() == "sans-serif") || (p_font_name.to_lower() == "serif") || (p_font_name.to_lower() == "monospace") || (p_font_name.to_lower() == "cursive") || (p_font_name.to_lower() == "fantasy"); + FcPatternAddBool(pattern, FC_SCALABLE, FcTrue); FcPatternAddString(pattern, FC_FAMILY, reinterpret_cast<const FcChar8 *>(p_font_name.utf8().get_data())); - FcPatternAddInteger(pattern, FC_WEIGHT, p_bold ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL); + FcPatternAddInteger(pattern, FC_WEIGHT, _weight_to_fc(p_weight)); + FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch)); FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN); FcConfigSubstitute(0, pattern, FcMatchPattern); @@ -443,6 +740,17 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, FcResult result; FcPattern *match = FcFontMatch(0, pattern, &result); if (match) { + if (!allow_substitutes) { + char *family_name = nullptr; + if (FcPatternGetString(match, FC_FAMILY, 0, reinterpret_cast<FcChar8 **>(&family_name)) == FcResultMatch) { + if (family_name && String::utf8(family_name).to_lower() != p_font_name.to_lower()) { + FcPatternDestroy(match); + FcPatternDestroy(pattern); + + return String(); + } + } + } char *file_name = nullptr; if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) { if (file_name) { @@ -454,8 +762,6 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, bool p_bold, } FcPatternDestroy(pattern); } - FcObjectSetDestroy(object_set); - FcConfigDestroy(config); return ret; #else @@ -509,6 +815,10 @@ String OS_LinuxBSD::get_cache_path() const { } String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { + if (p_dir == SYSTEM_DIR_DESKTOP && !system_dir_desktop_cache.is_empty()) { + return system_dir_desktop_cache; + } + String xdgparam; switch (p_dir) { @@ -517,31 +827,24 @@ String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const } break; case SYSTEM_DIR_DCIM: { xdgparam = "PICTURES"; - } break; case SYSTEM_DIR_DOCUMENTS: { xdgparam = "DOCUMENTS"; - } break; case SYSTEM_DIR_DOWNLOADS: { xdgparam = "DOWNLOAD"; - } break; case SYSTEM_DIR_MOVIES: { xdgparam = "VIDEOS"; - } break; case SYSTEM_DIR_MUSIC: { xdgparam = "MUSIC"; - } break; case SYSTEM_DIR_PICTURES: { xdgparam = "PICTURES"; - } break; case SYSTEM_DIR_RINGTONES: { xdgparam = "MUSIC"; - } break; } @@ -784,5 +1087,26 @@ OS_LinuxBSD::OS_LinuxBSD() { int dylibloader_verbose = 0; #endif font_config_initialized = (initialize_fontconfig(dylibloader_verbose) == 0); + if (font_config_initialized) { + config = FcInitLoadConfigAndFonts(); + if (!config) { + font_config_initialized = false; + } + object_set = FcObjectSetBuild(FC_FAMILY, FC_FILE, nullptr); + if (!object_set) { + font_config_initialized = false; + } + } +#endif // FONTCONFIG_ENABLED +} + +OS_LinuxBSD::~OS_LinuxBSD() { +#ifdef FONTCONFIG_ENABLED + if (object_set) { + FcObjectSetDestroy(object_set); + } + if (config) { + FcConfigDestroy(config); + } #endif // FONTCONFIG_ENABLED } |