summaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/ios/export/export_plugin.cpp366
-rw-r--r--platform/ios/export/export_plugin.h11
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp5
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h2
-rw-r--r--platform/macos/display_server_macos.h2
-rw-r--r--platform/macos/display_server_macos.mm4
-rw-r--r--platform/windows/SCsub16
-rw-r--r--platform/windows/display_server_windows.cpp4
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--platform/windows/godot.natvis141
10 files changed, 433 insertions, 120 deletions
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 395cd5d760..6943f0c637 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -868,7 +868,183 @@ struct ExportLibsData {
String dest_dir;
};
-void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
+void EditorExportPlatformIOS::_check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const {
+ Ref<PList> plist;
+ plist.instantiate();
+ plist->load_file(p_path.path_join("Info.plist"));
+ Ref<PListNode> root_node = plist->get_root();
+ if (root_node.is_null()) {
+ return;
+ }
+ Dictionary root = root_node->get_value();
+ if (!root.has("AvailableLibraries")) {
+ return;
+ }
+ Ref<PListNode> libs_node = root["AvailableLibraries"];
+ if (libs_node.is_null()) {
+ return;
+ }
+ Array libs = libs_node->get_value();
+ r_total_libs = libs.size();
+ for (int j = 0; j < libs.size(); j++) {
+ Ref<PListNode> lib_node = libs[j];
+ if (lib_node.is_null()) {
+ return;
+ }
+ Dictionary lib = lib_node->get_value();
+ if (lib.has("BinaryPath")) {
+ Ref<PListNode> path_node = lib["BinaryPath"];
+ if (path_node.is_valid()) {
+ String path = path_node->get_value();
+ if (path.ends_with(".a")) {
+ r_static_libs++;
+ }
+ if (path.ends_with(".dylib")) {
+ r_dylibs++;
+ }
+ if (path.ends_with(".framework")) {
+ r_frameworks++;
+ }
+ }
+ }
+ }
+}
+
+Error EditorExportPlatformIOS::_convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const {
+ print_line("Converting to .framework", p_source, " -> ", p_destination);
+
+ Ref<DirAccess> da = DirAccess::create_for_path(p_source);
+ if (da.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (filesystem_da.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+
+ if (!filesystem_da->dir_exists(p_destination)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(p_destination);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+
+ String asset = p_source.ends_with("/") ? p_source.left(p_source.length() - 1) : p_source;
+ if (asset.ends_with(".xcframework")) {
+ Ref<PList> plist;
+ plist.instantiate();
+ plist->load_file(p_source.path_join("Info.plist"));
+ Ref<PListNode> root_node = plist->get_root();
+ if (root_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Dictionary root = root_node->get_value();
+ if (!root.has("AvailableLibraries")) {
+ return ERR_CANT_OPEN;
+ }
+ Ref<PListNode> libs_node = root["AvailableLibraries"];
+ if (libs_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Array libs = libs_node->get_value();
+ for (int j = 0; j < libs.size(); j++) {
+ Ref<PListNode> lib_node = libs[j];
+ if (lib_node.is_null()) {
+ return ERR_CANT_OPEN;
+ }
+ Dictionary lib = lib_node->get_value();
+ if (lib.has("BinaryPath") && lib.has("LibraryPath") && lib.has("LibraryIdentifier")) {
+ Ref<PListNode> bpath_node = lib["BinaryPath"];
+ Ref<PListNode> lpath_node = lib["LibraryPath"];
+ Ref<PListNode> lid_node = lib["LibraryIdentifier"];
+ if (bpath_node.is_valid() && lpath_node.is_valid() && lid_node.is_valid()) {
+ String binary_path = bpath_node->get_value();
+ String library_identifier = lid_node->get_value();
+
+ String file_name = binary_path.get_basename().get_file();
+ String framework_name = file_name + ".framework";
+
+ bpath_node->data_string = framework_name.utf8();
+ lpath_node->data_string = framework_name.utf8();
+ if (!filesystem_da->dir_exists(p_destination.path_join(library_identifier))) {
+ filesystem_da->make_dir_recursive(p_destination.path_join(library_identifier));
+ }
+ _convert_to_framework(p_source.path_join(library_identifier).path_join(binary_path), p_destination.path_join(library_identifier).path_join(framework_name), p_id);
+ if (lib.has("DebugSymbolsPath")) {
+ Ref<PListNode> dpath_node = lib["DebugSymbolsPath"];
+ if (dpath_node.is_valid()) {
+ String dpath = dpath_node->get_value();
+ if (da->dir_exists(p_source.path_join(library_identifier).path_join(dpath))) {
+ da->copy_dir(p_source.path_join(library_identifier).path_join(dpath), p_destination.path_join(library_identifier).path_join("dSYMs"));
+ }
+ }
+ }
+ }
+ }
+ }
+ String info_plist = plist->save_text();
+
+ Ref<FileAccess> f = FileAccess::open(p_destination.path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ } else {
+ String file_name = p_destination.get_basename().get_file();
+ String framework_name = file_name + ".framework";
+
+ da->copy(p_source, p_destination.path_join(file_name));
+
+ // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
+ {
+ List<String> install_name_args;
+ install_name_args.push_back("-id");
+ install_name_args.push_back(String("@rpath").path_join(framework_name).path_join(file_name));
+ install_name_args.push_back(p_destination.path_join(file_name));
+
+ OS::get_singleton()->execute("install_name_tool", install_name_args);
+ }
+
+ // Creating Info.plist
+ {
+ String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n"
+ " <dict>\n"
+ " <key>CFBundleShortVersionString</key>\n"
+ " <string>1.0</string>\n"
+ " <key>CFBundleIdentifier</key>\n"
+ " <string>$id.framework.$name</string>\n"
+ " <key>CFBundleName</key>\n"
+ " <string>$name</string>\n"
+ " <key>CFBundleExecutable</key>\n"
+ " <string>$name</string>\n"
+ " <key>DTPlatformName</key>\n"
+ " <string>iphoneos</string>\n"
+ " <key>CFBundleInfoDictionaryVersion</key>\n"
+ " <string>6.0</string>\n"
+ " <key>CFBundleVersion</key>\n"
+ " <string>1</string>\n"
+ " <key>CFBundlePackageType</key>\n"
+ " <string>FMWK</string>\n"
+ " <key>MinimumOSVersion</key>\n"
+ " <string>12.0</string>\n"
+ " </dict>\n"
+ "</plist>";
+
+ String info_plist = info_plist_format.replace("$id", p_id).replace("$name", file_name);
+
+ Ref<FileAccess> f = FileAccess::open(p_destination.path_join("Info.plist"), FileAccess::WRITE);
+ if (f.is_valid()) {
+ f->store_string(info_plist);
+ }
+ }
+ }
+
+ return OK;
+}
+
+void EditorExportPlatformIOS::_add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) {
// that is just a random number, we just need Godot IDs not to clash with
// existing IDs in the project.
PbxId current_id = { 0x58938401, 0, 0 };
@@ -893,7 +1069,12 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
String type;
if (asset.exported_path.ends_with(".framework")) {
- if (asset.should_embed) {
+ int total_libs = 0;
+ int static_libs = 0;
+ int dylibs = 0;
+ int frameworks = 0;
+ _check_xcframework_content(p_out_dir.path_join(asset.exported_path), total_libs, static_libs, dylibs, frameworks);
+ if (asset.should_embed && (static_libs != total_libs)) {
additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
framework_id = (++current_id).str();
pbx_embeded_frameworks += framework_id + ",\n";
@@ -956,12 +1137,12 @@ void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPrese
}
}
-Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
String binary_name = p_out_dir.get_file().get_basename();
Ref<DirAccess> da = DirAccess::create_for_path(p_asset);
if (da.is_null()) {
- ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + ".");
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't open directory: " + p_asset + ".");
}
bool file_exists = da->file_exists(p_asset);
bool dir_exists = da->dir_exists(p_asset);
@@ -975,7 +1156,8 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
String destination;
String asset_path;
- bool create_framework = false;
+ Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
if (p_is_framework && asset.ends_with(".dylib")) {
// For iOS we need to turn .dylib into .framework
@@ -995,10 +1177,23 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
asset_path = asset_path.path_join(framework_name);
destination_dir = p_out_dir.path_join(asset_path);
destination = destination_dir.path_join(file_name);
- create_framework = true;
- } else if (p_is_framework && (asset.ends_with(".framework") || asset.ends_with(".xcframework"))) {
- asset_path = String("dylibs").path_join(base_dir);
+ // Convert to framework and copy.
+ Error err = _convert_to_framework(p_asset, destination, p_preset->get("application/bundle_identifier"));
+ if (err) {
+ return err;
+ }
+ } else if (p_is_framework && asset.ends_with(".xcframework")) {
+ // For iOS we need to turn .dylib inside .xcframework
+ // into .framework to be able to send application to AppStore
+
+ int total_libs = 0;
+ int static_libs = 0;
+ int dylibs = 0;
+ int frameworks = 0;
+ _check_xcframework_content(p_asset, total_libs, static_libs, dylibs, frameworks);
+
+ asset_path = String("dylibs").path_join(base_dir);
String file_name;
if (!p_custom_file_name) {
@@ -1010,8 +1205,29 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
asset_path = asset_path.path_join(file_name);
destination_dir = p_out_dir.path_join(asset_path);
destination = destination_dir;
- } else {
- asset_path = base_dir;
+
+ if (dylibs > 0) {
+ // Convert to framework and copy.
+ Error err = _convert_to_framework(p_asset, destination, p_preset->get("application/bundle_identifier"));
+ if (err) {
+ return err;
+ }
+ } else {
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
+ }
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
+ }
+ } else if (p_is_framework && asset.ends_with(".framework")) {
+ // Framework.
+ asset_path = String("dylibs").path_join(base_dir);
String file_name;
@@ -1021,99 +1237,67 @@ Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String
file_name = *p_custom_file_name;
}
- destination_dir = p_out_dir.path_join(asset_path);
asset_path = asset_path.path_join(file_name);
- destination = p_out_dir.path_join(asset_path);
- }
-
- Ref<DirAccess> filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- ERR_FAIL_COND_V_MSG(filesystem_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'.");
+ destination_dir = p_out_dir.path_join(asset_path);
+ destination = destination_dir;
- if (!filesystem_da->dir_exists(destination_dir)) {
- Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
- if (make_dir_err) {
- return make_dir_err;
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
+ }
}
- }
-
- Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
- if (err) {
- return err;
- }
- if (asset_path.ends_with("/")) {
- asset_path = asset_path.left(asset_path.length() - 1);
- }
- IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed };
- r_exported_assets.push_back(exported_asset);
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
+ } else {
+ // Unknown resource.
+ asset_path = base_dir;
- if (create_framework) {
String file_name;
if (!p_custom_file_name) {
- file_name = p_asset.get_basename().get_file();
+ file_name = p_asset.get_file();
} else {
file_name = *p_custom_file_name;
}
- String framework_name = file_name + ".framework";
-
- // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib
- {
- List<String> install_name_args;
- install_name_args.push_back("-id");
- install_name_args.push_back(String("@rpath").path_join(framework_name).path_join(file_name));
- install_name_args.push_back(destination);
-
- OS::get_singleton()->execute("install_name_tool", install_name_args);
- }
-
- // Creating Info.plist
- {
- String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
- "<plist version=\"1.0\">\n"
- "<dict>\n"
- "<key>CFBundleShortVersionString</key>\n"
- "<string>1.0</string>\n"
- "<key>CFBundleIdentifier</key>\n"
- "<string>com.gdextension.framework.$name</string>\n"
- "<key>CFBundleName</key>\n"
- "<string>$name</string>\n"
- "<key>CFBundleExecutable</key>\n"
- "<string>$name</string>\n"
- "<key>DTPlatformName</key>\n"
- "<string>iphoneos</string>\n"
- "<key>CFBundleInfoDictionaryVersion</key>\n"
- "<string>6.0</string>\n"
- "<key>CFBundleVersion</key>\n"
- "<string>1</string>\n"
- "<key>CFBundlePackageType</key>\n"
- "<string>FMWK</string>\n"
- "<key>MinimumOSVersion</key>\n"
- "<string>10.0</string>\n"
- "</dict>\n"
- "</plist>";
-
- String info_plist = info_plist_format.replace("$name", file_name);
+ destination_dir = p_out_dir.path_join(asset_path);
+ asset_path = asset_path.path_join(file_name);
+ destination = p_out_dir.path_join(asset_path);
- Ref<FileAccess> f = FileAccess::open(destination_dir.path_join("Info.plist"), FileAccess::WRITE);
- if (f.is_valid()) {
- f->store_string(info_plist);
+ // Copy as is.
+ if (!filesystem_da->dir_exists(destination_dir)) {
+ Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir);
+ if (make_dir_err) {
+ return make_dir_err;
}
}
+ Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination);
+ if (err) {
+ return err;
+ }
}
+ if (asset_path.ends_with("/")) {
+ asset_path = asset_path.left(asset_path.length() - 1);
+ }
+ IOSExportAsset exported_asset = { binary_name.path_join(asset_path), p_is_framework, p_should_embed };
+ r_exported_assets.push_back(exported_asset);
+
return OK;
}
-Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) {
for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) {
const String &asset = p_assets[f_idx];
if (asset.begins_with("res://")) {
- Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ Error err = _copy_asset(p_preset, p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
} else if (ProjectSettings::get_singleton()->localize_path(asset).begins_with("res://")) {
- Error err = _copy_asset(p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
+ Error err = _copy_asset(p_preset, p_out_dir, ProjectSettings::get_singleton()->localize_path(asset), nullptr, p_is_framework, p_should_embed, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
} else {
// either SDK-builtin or already a part of the export template
@@ -1125,26 +1309,26 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
return OK;
}
-Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
+Error EditorExportPlatformIOS::_export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) {
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
for (int i = 0; i < export_plugins.size(); i++) {
Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks();
- Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets);
+ Error err = _export_additional_assets(p_preset, p_out_dir, linked_frameworks, true, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks();
- err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, embedded_frameworks, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs();
for (int j = 0; j < project_static_libs.size(); j++) {
project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project
}
- err = _export_additional_assets(p_out_dir, project_static_libs, true, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, project_static_libs, true, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files();
- err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, p_out_dir, ios_bundle_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err, err);
}
@@ -1152,7 +1336,7 @@ Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir
for (int i = 0; i < p_libraries.size(); ++i) {
library_paths.push_back(p_libraries[i].path);
}
- Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets);
+ Error err = _export_additional_assets(p_preset, p_out_dir, library_paths, true, true, r_exported_assets);
ERR_FAIL_COND_V(err, err);
return OK;
@@ -1197,7 +1381,7 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
String plugin_binary_result_file = plugin.binary.get_file();
// We shouldn't embed .xcframework that contains static libraries.
// Static libraries are not embedded anyway.
- err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
+ err = _copy_asset(p_preset, dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Adding dependencies.
@@ -1322,15 +1506,15 @@ Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset>
// Export files
{
// Export linked plugin dependency
- err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, dest_dir, plugin_linked_dependencies, true, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Export embedded plugin dependency
- err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
+ err = _export_additional_assets(p_preset, dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
// Export plugin files
- err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets);
+ err = _export_additional_assets(p_preset, dest_dir, plugin_files, false, false, r_exported_assets);
ERR_FAIL_COND_V(err != OK, err);
}
@@ -1729,8 +1913,8 @@ Error EditorExportPlatformIOS::_export_project_helper(const Ref<EditorExportPres
}
print_line("Exporting additional assets");
- _export_additional_assets(binary_dir, libraries, assets);
- _add_assets_to_project(p_preset, project_file_data, assets);
+ _export_additional_assets(p_preset, binary_dir, libraries, assets);
+ _add_assets_to_project(dest_dir, p_preset, project_file_data, assets);
String project_file_name = binary_dir + ".xcodeproj/project.pbxproj";
{
Ref<FileAccess> f = FileAccess::open(project_file_name, FileAccess::WRITE);
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index 1f926c4d14..edbe566dab 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -133,10 +133,13 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
Vector<ExportArchitecture> _get_supported_architectures() const;
Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset) const;
- void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
- Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
- Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
- Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
+ void _check_xcframework_content(const String &p_path, int &r_total_libs, int &r_static_libs, int &r_dylibs, int &r_frameworks) const;
+ Error _convert_to_framework(const String &p_source, const String &p_destination, const String &p_id) const;
+
+ void _add_assets_to_project(const String &p_out_dir, const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets);
+ Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+ Error _copy_asset(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets);
+ Error _export_additional_assets(const Ref<EditorExportPreset> &p_preset, const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets);
Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug);
Error _export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags, bool p_simulator, bool p_oneclick);
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index dc2196853d..d8a81266d0 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -2891,10 +2891,15 @@ void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
XFlush(x11_display);
}
+DisplayServerX11::WindowID DisplayServerX11::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerX11::window_is_focused(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), false);
+
const WindowData &wd = windows[p_window];
return wd.focused;
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index ac2c7843f6..378d8bb407 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -491,6 +491,8 @@ public:
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index f8fd0f93ef..cc343a6d64 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -428,6 +428,8 @@ public:
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index bd92b7d472..378688f78a 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -3723,6 +3723,10 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const {
return wd.focused;
}
+DisplayServerMacOS::WindowID DisplayServerMacOS::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerMacOS::window_can_draw(WindowID p_window) const {
return windows[p_window].is_visible;
}
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 0549e408a7..1cfbc33ef8 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -3,6 +3,7 @@
Import("env")
import os
+from pathlib import Path
from platform_methods import run_in_subprocess
import platform_windows_builders
@@ -25,6 +26,19 @@ common_win_wrap = [
"console_wrapper_windows.cpp",
]
+
+def arrange_program_clean(prog):
+ """
+ Given an SCons program, arrange for output files SCons doesn't know about
+ to be cleaned when SCons is called with --clean
+ """
+ extensions_to_clean = [".ilk", ".exp", ".pdb", ".lib"]
+ for program in prog:
+ executable_stem = Path(program.name).stem
+ extra_files_to_clean = [f"#bin/{executable_stem}{extension}" for extension in extensions_to_clean]
+ Clean(prog, extra_files_to_clean)
+
+
res_file = "godot_res.rc"
res_target = "godot_res" + env["OBJSUFFIX"]
res_obj = env.RES(res_target, res_file)
@@ -32,6 +46,7 @@ res_obj = env.RES(res_target, res_file)
sources = common_win + res_obj
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
+arrange_program_clean(prog)
# Build console wrapper app.
if env["windows_subsystem"] == "gui":
@@ -48,6 +63,7 @@ if env["windows_subsystem"] == "gui":
env_wrap.Append(LIBS=["version"])
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
+ arrange_program_clean(prog_wrap)
env_wrap.Depends(prog_wrap, prog)
# Microsoft Visual Studio Project Generation
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index a6c2cd7313..b56954ae81 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -1948,6 +1948,10 @@ bool DisplayServerWindows::window_is_focused(WindowID p_window) const {
return wd.window_focused;
}
+DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const {
+ return last_focused_window;
+}
+
bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
_THREAD_SAFE_METHOD_
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 29c2460c10..2668e14540 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -611,6 +611,8 @@ public:
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
+ virtual WindowID get_focused_window() const override;
+
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool can_any_window_draw() const override;
diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis
index 36b0919185..94cc52be98 100644
--- a/platform/windows/godot.natvis
+++ b/platform/windows/godot.natvis
@@ -5,11 +5,43 @@
<Item Name="[size]">_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Item>
<ArrayItems>
<Size>_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Size>
- <ValuePointer>_cowdata._ptr</ValuePointer>
+ <ValuePointer>($T1 *) _cowdata._ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
+ <Type Name="Array">
+ <Expand>
+ <Item Name="[size]">_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
+ <ArrayItems>
+ <Size>_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer>(Variant *) _p->array._cowdata._ptr</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="TypedArray&lt;*&gt;">
+ <Expand>
+ <Item Name="[size]"> _p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
+ <ArrayItems>
+ <Size>_p->array._cowdata._ptr ? (((const unsigned int *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer >(Variant *) _p->array._cowdata._ptr</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="Dictionary">
+ <Expand>
+ <Item Name="[size]">_p &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
+ <LinkedListItems>
+ <Size>_p &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Size>
+ <HeadPointer>_p ? _p->variant_map.head_element : nullptr</HeadPointer>
+ <NextPointer>next</NextPointer>
+ <ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
<Type Name="LocalVector&lt;*&gt;">
<Expand>
<Item Name="[size]">count</Item>
@@ -32,18 +64,80 @@
</Expand>
</Type>
- <Type Name="HashMap&lt;*,*&gt;">
+ <Type Name="NodePath">
+ <DisplayString Condition="!data">[empty]</DisplayString>
+ <DisplayString Condition="!!data">{{[absolute] = {data->absolute} [path] = {data->path,view(NodePathHelper)} [subpath] = {data->subpath,view(NodePathHelper)}}}</DisplayString>
<Expand>
- <Item Name="[size]">num_elements</Item>
+ <Item Name="[path]">data->path,view(NodePathHelper)</Item>
+ <Item Name="[subpath]">data->subpath,view(NodePathHelper)</Item>
+ <Item Name="[absolute]">data->absolute</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="Vector&lt;StringName&gt;" IncludeView="NodePathHelper">
+ <Expand>
+ <ArrayItems>
+ <Size>_cowdata._ptr ? (((const unsigned int *)(_cowdata._ptr))[-1]) : 0</Size>
+ <ValuePointer>((StringName *)_cowdata._ptr),view(NodePathHelper)</ValuePointer>
+ </ArrayItems>
+ </Expand>
+ </Type>
+
+ <Type Name="StringName" IncludeView="NodePathHelper">
+ <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname,s8b}</DisplayString>
+ <DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32b}</DisplayString>
+ <DisplayString Condition="!_data">[empty]</DisplayString>
+ <StringView Condition="_data &amp;&amp; _data->cname">_data->cname,s8b</StringView>
+ <StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32b</StringView>
+ </Type>
+
+ <Type Name="HashMapElement&lt;*,*&gt;">
+ <DisplayString>{{Key = {($T1 *) &amp;data.key} Value = {($T2 *) &amp;data.value}}}</DisplayString>
+ <Expand>
+ <Item Name="[key]">($T1 *) &amp;data.key</Item>
+ <Item Name="[value]">($T2 *) &amp;data.value</Item>
+ </Expand>
+ </Type>
+
+ <!-- elements displayed by index -->
+ <Type Name="HashMap&lt;*,*,*,*,*&gt;" Priority="Medium">
+ <Expand>
+ <Item Name="[size]">head_element ? num_elements : 0</Item>
<LinkedListItems>
- <Size>num_elements</Size>
+ <Size>head_element ? num_elements : 0</Size>
<HeadPointer>head_element</HeadPointer>
<NextPointer>next</NextPointer>
- <ValueNode>data</ValueNode>
+ <ValueNode>(*this)</ValueNode>
</LinkedListItems>
</Expand>
</Type>
+ <!-- elements by key:value -->
+ <!-- show elements by index by specifying "ShowElementsByIndex"-->
+ <Type Name="HashMap&lt;*,*,*,*,*&gt;" ExcludeView="ShowElementsByIndex" Priority="MediumHigh">
+ <Expand>
+ <Item Name="[size]">head_element ? num_elements : 0</Item>
+ <LinkedListItems>
+ <Size>head_element ? num_elements : 0</Size>
+ <HeadPointer>head_element</HeadPointer>
+ <NextPointer>next</NextPointer>
+ <ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
+ </LinkedListItems>
+ </Expand>
+ </Type>
+
+ <Type Name="KeyValue&lt;*,*&gt;" IncludeView="MapHelper">
+ <DisplayString>{value}</DisplayString>
+ </Type>
+
+ <Type Name="HashMapElement&lt;*,*&gt;" IncludeView="MapHelper">
+ <DisplayString>{data.value}</DisplayString>
+ <Expand>
+ <Item Name="[key]" >($T1 *) &amp;data.key</Item>
+ <Item Name="[value]">($T2 *) &amp;data.value</Item>
+ </Expand>
+ </Type>
+
<Type Name="VMap&lt;*,*&gt;">
<Expand>
<Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast&lt;int*&gt;(_cowdata._ptr) - 1)</Item>
@@ -58,11 +152,6 @@
<DisplayString Condition="dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)">{dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)->text}</DisplayString>
</Type>
- <!-- requires PR 64364
- <Type Name="GDScriptThreadContext">
- <DisplayString Condition="_is_main == true">main thread {_debug_thread_id}</DisplayString>
- </Type>
- -->
<Type Name="Variant">
<DisplayString Condition="type == Variant::NIL">nil</DisplayString>
@@ -82,22 +171,22 @@
<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::STRING_NAME">{*(StringName *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString>
- <DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString>
+ <DisplayString Condition="type == Variant::OBJECT">{*(*reinterpret_cast&lt;ObjData*&gt;(&amp;_data._mem[0])).obj}</DisplayString>
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array}</DisplayString>
- <!-- broken, will show incorrect data
- <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString>
- -->
+ <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array}</DisplayString>
+ <DisplayString Condition="type &lt; 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
<StringView Condition="type == Variant::STRING &amp;&amp; ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
@@ -116,20 +205,22 @@
<Item Name="[value]" Condition="type == Variant::PLANE">*(Plane *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::QUATERNION">*(Quaternion *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::STRING_NAME">*(StringName *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::OBJECT">*(*reinterpret_cast&lt;ObjData*&gt;(&amp;_data._mem[0])).obj</Item>
<Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">*(PackedFloat64Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">*(PackedStringArray *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">*(PackedVector2Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">*(PackedVector3Array *)_data._mem</Item>
- <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">*(PackedColorArray *)_data._mem</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array</Item>
+ <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array</Item>
+
</Expand>
</Type>
@@ -148,10 +239,10 @@
</Type>
<Type Name="StringName">
- <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname}</DisplayString>
+ <DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname,na}</DisplayString>
<DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32}</DisplayString>
<DisplayString Condition="!_data">[empty]</DisplayString>
- <StringView Condition="_data &amp;&amp; _data->cname">_data->cname</StringView>
+ <StringView Condition="_data &amp;&amp; _data->cname">_data->cname,na</StringView>
<StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32</StringView>
</Type>