You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Merge pull request #108794 from bruvzg/macos_actool_export
[macOS] Add support for exporting macOS 26 Liquid Glass icons.
This commit is contained in:
@@ -309,11 +309,16 @@ String OS::get_bundle_resource_dir() const {
|
|||||||
return ".";
|
return ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path to macOS .app bundle embedded icon
|
// Path to macOS .app bundle embedded icon (.icns file).
|
||||||
String OS::get_bundle_icon_path() const {
|
String OS::get_bundle_icon_path() const {
|
||||||
return String();
|
return String();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name of macOS .app bundle embedded icon (Liquid Glass asset name).
|
||||||
|
String OS::get_bundle_icon_name() const {
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
// OS specific path for user://
|
// OS specific path for user://
|
||||||
String OS::get_user_data_dir(const String &p_user_dir) const {
|
String OS::get_user_data_dir(const String &p_user_dir) const {
|
||||||
return ".";
|
return ".";
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ public:
|
|||||||
virtual String get_temp_path() const;
|
virtual String get_temp_path() const;
|
||||||
virtual String get_bundle_resource_dir() const;
|
virtual String get_bundle_resource_dir() const;
|
||||||
virtual String get_bundle_icon_path() const;
|
virtual String get_bundle_icon_path() const;
|
||||||
|
virtual String get_bundle_icon_name() const;
|
||||||
|
|
||||||
virtual String get_user_data_dir(const String &p_user_dir) const;
|
virtual String get_user_data_dir(const String &p_user_dir) const;
|
||||||
virtual String get_user_data_dir() const;
|
virtual String get_user_data_dir() const;
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
|||||||
capitalize_string_remaps["aa"] = "AA";
|
capitalize_string_remaps["aa"] = "AA";
|
||||||
capitalize_string_remaps["aabb"] = "AABB";
|
capitalize_string_remaps["aabb"] = "AABB";
|
||||||
capitalize_string_remaps["adb"] = "ADB";
|
capitalize_string_remaps["adb"] = "ADB";
|
||||||
|
capitalize_string_remaps["actool"] = "actool";
|
||||||
capitalize_string_remaps["ao"] = "AO";
|
capitalize_string_remaps["ao"] = "AO";
|
||||||
capitalize_string_remaps["api"] = "API";
|
capitalize_string_remaps["api"] = "API";
|
||||||
capitalize_string_remaps["apk"] = "APK";
|
capitalize_string_remaps["apk"] = "APK";
|
||||||
|
|||||||
@@ -4567,8 +4567,13 @@ int Main::start() {
|
|||||||
sml->add_current_scene(scene);
|
sml->add_current_scene(scene);
|
||||||
|
|
||||||
#ifdef MACOS_ENABLED
|
#ifdef MACOS_ENABLED
|
||||||
|
#ifndef TOOLS_ENABLED
|
||||||
|
if ((FileAccess::exists(OS::get_singleton()->get_bundle_resource_dir().path_join("Assets.car")) && !OS::get_singleton()->get_bundle_icon_name().is_empty()) || (!OS::get_singleton()->get_bundle_icon_path().is_empty())) {
|
||||||
|
has_icon = true; // Bundle has embedded icon, do not override with project icon.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
String mac_icon_path = GLOBAL_GET("application/config/macos_native_icon");
|
String mac_icon_path = GLOBAL_GET("application/config/macos_native_icon");
|
||||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_ICON) && !mac_icon_path.is_empty()) {
|
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_ICON) && !mac_icon_path.is_empty() && !has_icon) {
|
||||||
DisplayServer::get_singleton()->set_native_icon(mac_icon_path);
|
DisplayServer::get_singleton()->set_native_icon(mac_icon_path);
|
||||||
has_icon = true;
|
has_icon = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<string>$name</string>
|
<string>$name</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icon.icns</string>
|
<string>icon.icns</string>
|
||||||
|
$liquid_glass_icon
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$bundle_identifier</string>
|
<string>$bundle_identifier</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
|||||||
@@ -38,6 +38,11 @@
|
|||||||
<member name="application/icon_interpolation" type="int" setter="" getter="">
|
<member name="application/icon_interpolation" type="int" setter="" getter="">
|
||||||
Interpolation method used to resize application icon.
|
Interpolation method used to resize application icon.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="application/liquid_glass_icon" type="String" setter="" getter="">
|
||||||
|
macOS 26 Liquid Glass icon source file. Use [url=https://developer.apple.com/icon-composer/]Icon Composer[/url] to create Liquid Glass icons.
|
||||||
|
[b]Note:[/b] Supported when exporting from macOS only, Xcode 26+ required.
|
||||||
|
[b]Note:[/b] Liquid Glass icons are supported on macOS 26 only, use [member application/icon] to set the icon for older macOS versions.
|
||||||
|
</member>
|
||||||
<member name="application/min_macos_version_arm64" type="String" setter="" getter="">
|
<member name="application/min_macos_version_arm64" type="String" setter="" getter="">
|
||||||
Minimum version of macOS required for this application to run on Apple Silicon Macs, in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
|
Minimum version of macOS required for this application to run on Apple Silicon Macs, in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).
|
||||||
</member>
|
</member>
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ void register_macos_exporter() {
|
|||||||
#else
|
#else
|
||||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE));
|
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/rcodesign", PROPERTY_HINT_GLOBAL_FILE));
|
||||||
#endif
|
#endif
|
||||||
|
EDITOR_DEF_BASIC("export/macos/actool", "");
|
||||||
|
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/macos/actool", PROPERTY_HINT_GLOBAL_FILE));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Ref<EditorExportPlatformMacOS> platform;
|
Ref<EditorExportPlatformMacOS> platform;
|
||||||
|
|||||||
@@ -306,6 +306,12 @@ bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportP
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef MACOS_ENABLED
|
||||||
|
if (p_option == "application/liquid_glass_icon") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
String custom_prof = p_preset->get("codesign/entitlements/custom_file");
|
String custom_prof = p_preset->get("codesign/entitlements/custom_file");
|
||||||
if (!custom_prof.is_empty() && p_option != "codesign/entitlements/custom_file" && p_option.begins_with("codesign/entitlements/")) {
|
if (!custom_prof.is_empty() && p_option != "codesign/entitlements/custom_file" && p_option.begins_with("codesign/entitlements/")) {
|
||||||
return false;
|
return false;
|
||||||
@@ -467,6 +473,7 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
|
|||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
|
||||||
|
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "debug/export_console_wrapper", PROPERTY_HINT_ENUM, "No,Debug Only,Debug and Release"), 1));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "debug/export_console_wrapper", PROPERTY_HINT_ENUM, "No,Debug Only,Debug and Release"), 1));
|
||||||
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/liquid_glass_icon", PROPERTY_HINT_FILE, "*.icon"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.icns,*.png,*.webp,*.svg"), ""));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.icns,*.png,*.webp,*.svg"), ""));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4));
|
||||||
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
|
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "", false, true));
|
||||||
@@ -828,7 +835,7 @@ void EditorExportPlatformMacOS::_fix_privacy_manifest(const Ref<EditorExportPres
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) {
|
void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary, bool p_lg_icon_exported, const String &p_lg_icon) {
|
||||||
String str = String::utf8((const char *)plist.ptr(), plist.size());
|
String str = String::utf8((const char *)plist.ptr(), plist.size());
|
||||||
String strnew;
|
String strnew;
|
||||||
Vector<String> lines = str.split("\n");
|
Vector<String> lines = str.split("\n");
|
||||||
@@ -872,6 +879,12 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
|
|||||||
strnew += lines[i].replace("$xcodever", p_preset->get("xcode/xcode_version")) + "\n";
|
strnew += lines[i].replace("$xcodever", p_preset->get("xcode/xcode_version")) + "\n";
|
||||||
} else if (lines[i].contains("$xcodebuild")) {
|
} else if (lines[i].contains("$xcodebuild")) {
|
||||||
strnew += lines[i].replace("$xcodebuild", p_preset->get("xcode/xcode_build")) + "\n";
|
strnew += lines[i].replace("$xcodebuild", p_preset->get("xcode/xcode_build")) + "\n";
|
||||||
|
} else if (lines[i].contains("$liquid_glass_icon")) {
|
||||||
|
if (p_lg_icon_exported) {
|
||||||
|
strnew += lines[i].replace("$liquid_glass_icon", "\t<key>CFBundleIconName</key>\n\t<string>" + p_lg_icon + "</string>\n");
|
||||||
|
} else {
|
||||||
|
strnew += lines[i].replace("$liquid_glass_icon", "");
|
||||||
|
}
|
||||||
} else if (lines[i].contains("$usage_descriptions")) {
|
} else if (lines[i].contains("$usage_descriptions")) {
|
||||||
String descriptions;
|
String descriptions;
|
||||||
if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) {
|
if (!((String)p_preset->get("privacy/microphone_usage_description")).is_empty()) {
|
||||||
@@ -933,6 +946,76 @@ void EditorExportPlatformMacOS::_fix_plist(const Ref<EditorExportPreset> &p_pres
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Error EditorExportPlatformMacOS::_export_liquid_glass_icon(const Ref<EditorExportPreset> &p_preset, const String &p_app_path, const String &p_icon_path) {
|
||||||
|
String actool = EDITOR_GET("export/macos/actool").operator String();
|
||||||
|
if (actool.is_empty()) {
|
||||||
|
actool = "actool";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> args;
|
||||||
|
args.push_back("--version");
|
||||||
|
String str;
|
||||||
|
String err_str;
|
||||||
|
int exitcode = 0;
|
||||||
|
|
||||||
|
Error err = OS::get_singleton()->execute(actool, args, &str, &exitcode, true);
|
||||||
|
if (err != OK) {
|
||||||
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Liquid Glass Icons"), TTR("Could not start 'actool' executable."));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
PList info_plist;
|
||||||
|
if (!info_plist.load_string(str, err_str)) {
|
||||||
|
print_verbose(str);
|
||||||
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Liquid Glass Icons"), TTR("Could not read 'actool' version."));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (info_plist.get_root()->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && info_plist.get_root()->data_dict.has("com.apple.actool.version")) {
|
||||||
|
Ref<PListNode> dict = info_plist.get_root()->data_dict["com.apple.actool.version"];
|
||||||
|
if (dict->data_type == PList::PLNodeType::PL_NODE_TYPE_DICT && dict->data_dict.has("short-bundle-version")) {
|
||||||
|
float version = String::utf8(dict->data_dict["short-bundle-version"]->data_string.get_data()).to_float();
|
||||||
|
if (version < 26.0) {
|
||||||
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Liquid Glass Icons"), vformat(TTR("At least version 26.0 of 'actool' is required (version %f found)."), version));
|
||||||
|
return ERR_UNAVAILABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str.clear();
|
||||||
|
|
||||||
|
String plist = EditorPaths::get_singleton()->get_temp_dir().path_join("assetcatalog.plist");
|
||||||
|
args.clear();
|
||||||
|
args.push_back(ProjectSettings::get_singleton()->globalize_path(p_icon_path));
|
||||||
|
args.push_back("--compile");
|
||||||
|
args.push_back(p_app_path + "/Contents/Resources/");
|
||||||
|
args.push_back("--output-format");
|
||||||
|
args.push_back("human-readable-text");
|
||||||
|
args.push_back("--lightweight-asset-runtime-mode");
|
||||||
|
args.push_back("enabled");
|
||||||
|
args.push_back("--app-icon");
|
||||||
|
args.push_back(p_icon_path.get_file().get_basename());
|
||||||
|
args.push_back("--include-all-app-icons");
|
||||||
|
args.push_back("--enable-on-demand-resources");
|
||||||
|
args.push_back("NO");
|
||||||
|
args.push_back("--development-region");
|
||||||
|
args.push_back("en");
|
||||||
|
args.push_back("--target-device");
|
||||||
|
args.push_back("mac");
|
||||||
|
args.push_back("--minimum-deployment-target");
|
||||||
|
args.push_back("26");
|
||||||
|
args.push_back("--platform");
|
||||||
|
args.push_back("macosx");
|
||||||
|
args.push_back("--output-partial-info-plist");
|
||||||
|
args.push_back(plist);
|
||||||
|
|
||||||
|
err = OS::get_singleton()->execute(actool, args, &str, &exitcode, true);
|
||||||
|
if (err != OK || str.contains("error:") || !FileAccess::exists(p_app_path + "/Contents/Resources/Assets.car") || !FileAccess::exists(plist)) {
|
||||||
|
print_verbose(str);
|
||||||
|
add_message(EXPORT_MESSAGE_WARNING, TTR("Liquid Glass Icons"), TTR("Could not export liquid glass icon:") + "\n" + str);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If we're running the macOS version of the Godot editor we'll:
|
* If we're running the macOS version of the Godot editor we'll:
|
||||||
* - export our application bundle to a temporary folder
|
* - export our application bundle to a temporary folder
|
||||||
@@ -1861,7 +1944,16 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file == "Contents/Info.plist") {
|
if (file == "Contents/Info.plist") {
|
||||||
_fix_plist(p_preset, data, pkg_name);
|
bool lg_icon_expored = false;
|
||||||
|
String lg_icon = p_preset->get("application/liquid_glass_icon");
|
||||||
|
#ifdef MACOS_ENABLED
|
||||||
|
// Export liquid glass.
|
||||||
|
if (!lg_icon.is_empty()) {
|
||||||
|
lg_icon_expored = (_export_liquid_glass_icon(p_preset, tmp_app_path_name, lg_icon) == OK);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// Modify plist.
|
||||||
|
_fix_plist(p_preset, data, pkg_name, lg_icon_expored, lg_icon.get_file().get_basename());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == "Contents/Resources/PrivacyInfo.xcprivacy") {
|
if (file == "Contents/Resources/PrivacyInfo.xcprivacy") {
|
||||||
|
|||||||
@@ -85,9 +85,10 @@ class EditorExportPlatformMacOS : public EditorExportPlatform {
|
|||||||
int menu_options = 0;
|
int menu_options = 0;
|
||||||
|
|
||||||
void _fix_privacy_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist);
|
void _fix_privacy_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist);
|
||||||
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary);
|
void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary, bool p_lg_icon_exported, const String &p_lg_icon);
|
||||||
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
|
void _make_icon(const Ref<EditorExportPreset> &p_preset, const Ref<Image> &p_icon, Vector<uint8_t> &p_data);
|
||||||
|
|
||||||
|
Error _export_liquid_glass_icon(const Ref<EditorExportPreset> &p_preset, const String &p_app_path, const String &p_icon_path);
|
||||||
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||||
void _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true, bool p_set_id = false);
|
void _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, bool p_warn = true, bool p_set_id = false);
|
||||||
void _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code = true);
|
void _code_sign_directory(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path, const String &p_helper_ent_path, bool p_should_error_on_non_code = true);
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public:
|
|||||||
virtual String get_temp_path() const override;
|
virtual String get_temp_path() const override;
|
||||||
virtual String get_bundle_resource_dir() const override;
|
virtual String get_bundle_resource_dir() const override;
|
||||||
virtual String get_bundle_icon_path() const override;
|
virtual String get_bundle_icon_path() const override;
|
||||||
|
virtual String get_bundle_icon_name() const override;
|
||||||
virtual String get_godot_dir_name() const override;
|
virtual String get_godot_dir_name() const override;
|
||||||
|
|
||||||
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
|
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
|
||||||
|
|||||||
@@ -469,6 +469,19 @@ String OS_MacOS::get_bundle_icon_path() const {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String OS_MacOS::get_bundle_icon_name() const {
|
||||||
|
String ret;
|
||||||
|
|
||||||
|
NSBundle *main = [NSBundle mainBundle];
|
||||||
|
if (main) {
|
||||||
|
NSString *icon_name = [[main infoDictionary] objectForKey:@"CFBundleIconName"];
|
||||||
|
if (icon_name) {
|
||||||
|
ret.append_utf8([icon_name UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Get properly capitalized engine name for system paths
|
// Get properly capitalized engine name for system paths
|
||||||
String OS_MacOS::get_godot_dir_name() const {
|
String OS_MacOS::get_godot_dir_name() const {
|
||||||
return String(GODOT_VERSION_SHORT_NAME).capitalize();
|
return String(GODOT_VERSION_SHORT_NAME).capitalize();
|
||||||
|
|||||||
Reference in New Issue
Block a user