Compare commits

..

1 commit

Author SHA1 Message Date
8e387dc156
[compat] improve thread naming logic
All checks were successful
eden-license / license-header (pull_request) Successful in 27s
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-08-30 13:43:05 +00:00
18 changed files with 118 additions and 235 deletions

View file

@ -1,6 +1,6 @@
AppRun
eden.desktop
dev.eden_emu.eden.desktop
org.eden_emu.eden.desktop
shared/bin/eden
shared/lib/lib.path
shared/lib/ld-linux-x86-64.so.2

View file

@ -59,15 +59,15 @@ VERSION="$(echo "$EDEN_TAG")"
mkdir -p ./AppDir
cd ./AppDir
cp ../dist/dev.eden_emu.eden.desktop .
cp ../dist/dev.eden_emu.eden.svg .
cp ../dist/org.eden_emu.eden.desktop .
cp ../dist/org.eden_emu.eden.svg .
ln -sf ./dev.eden_emu.eden.svg ./.DirIcon
ln -sf ./org.eden_emu.eden.svg ./.DirIcon
UPINFO='gh-releases-zsync|eden-emulator|Releases|latest|*.AppImage.zsync'
if [ "$DEVEL" = 'true' ]; then
sed -i 's|Name=Eden|Name=Eden Nightly|' ./dev.eden_emu.eden.desktop
sed -i 's|Name=Eden|Name=Eden Nightly|' ./org.eden_emu.eden.desktop
UPINFO="$(echo "$UPINFO" | sed 's|Releases|nightly|')"
fi

View file

@ -6,7 +6,7 @@
which png2icns || [ which yay && yay libicns ] || exit
which magick || exit
export EDEN_SVG_ICO="dist/dev.eden_emu.eden.svg"
export EDEN_SVG_ICO="dist/org.eden_emu.eden.svg"
svgo --multipass $EDEN_SVG_ICO
magick -density 256x256 -background transparent $EDEN_SVG_ICO \

View file

@ -406,10 +406,8 @@ if (YUZU_USE_CPM)
if (NOT MSVC)
# boost sucks
# Solaris (and probably other NIXes) need explicit pthread definition
if (PLATFORM_SUN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthreads")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthreads")
if (NOT PLATFORM_LINUX AND NOT ANDROID)
target_compile_definitions(boost_container INTERFACE BOOST_HAS_PTHREADS)
endif()
target_compile_options(boost_heap INTERFACE -Wno-shadow)
@ -858,14 +856,14 @@ endif()
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
# https://www.freedesktop.org/software/appstream/docs/
if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "dist/dev.eden_emu.eden.desktop"
install(FILES "dist/org.eden_emu.eden.desktop"
DESTINATION "share/applications")
install(FILES "dist/dev.eden_emu.eden.svg"
install(FILES "dist/org.eden_emu.eden.svg"
DESTINATION "share/icons/hicolor/scalable/apps")
# TODO: these files need to be updated.
install(FILES "dist/dev.eden_emu.eden.xml"
install(FILES "dist/org.eden_emu.eden.xml"
DESTINATION "share/mime/packages")
install(FILES "dist/dev.eden_emu.eden.metainfo.xml"
install(FILES "dist/org.eden_emu.eden.metainfo.xml"
DESTINATION "share/metainfo")
endif()

View file

@ -10,7 +10,7 @@ Type=Application
Name=Eden
GenericName=Switch Emulator
Comment=Nintendo Switch video game console emulator
Icon=dev.eden_emu.eden
Icon=org.eden_emu.eden
TryExec=eden
Exec=eden %f
Categories=Game;Emulator;Qt;

View file

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Before After
Before After

View file

@ -63,22 +63,20 @@ if (NOT WIN32 AND NOT ANDROID)
set(FFmpeg_HWACCEL_INCLUDE_DIRS)
set(FFmpeg_HWACCEL_LDFLAGS)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"/usr/lib/xorg/amd64/libdrm.so")
else()
pkg_check_modules(LIBDRM libdrm REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS})
endif()
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"/usr/lib/xorg/amd64/libdrm.so")
else()
pkg_check_modules(LIBDRM libdrm REQUIRED)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
${LIBDRM_LIBRARIES})
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
${LIBDRM_INCLUDE_DIRS})
endif()
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-libdrm)
if(LIBVA_FOUND)
find_package(X11 REQUIRED)

View file

@ -124,16 +124,11 @@ object CustomSettingsHandler {
// Check for driver requirements if activity and driverViewModel are provided
if (activity != null && driverViewModel != null) {
val rawDriverPath = extractDriverPath(customSettings)
if (rawDriverPath != null) {
// Normalize to local storage path (we only store drivers under driverStoragePath)
val driverFilename = rawDriverPath.substringAfterLast('/')
.substringAfterLast('\\')
val localDriverPath = "${GpuDriverHelper.driverStoragePath}$driverFilename"
Log.info("[CustomSettingsHandler] Custom settings specify driver: $rawDriverPath (normalized: $localDriverPath)")
val driverPath = extractDriverPath(customSettings)
if (driverPath != null) {
Log.info("[CustomSettingsHandler] Custom settings specify driver: $driverPath")
// Check if driver exists in the driver storage
val driverFile = File(localDriverPath)
val driverFile = File(driverPath)
if (!driverFile.exists()) {
Log.info("[CustomSettingsHandler] Driver not found locally: ${driverFile.name}")
@ -187,7 +182,7 @@ object CustomSettingsHandler {
}
// Attempt to download and install the driver
val driverUri = DriverResolver.ensureDriverAvailable(driverFilename, activity) { progress ->
val driverUri = DriverResolver.ensureDriverAvailable(driverPath, activity) { progress ->
progressChannel.trySend(progress.toInt())
}
@ -214,12 +209,12 @@ object CustomSettingsHandler {
return null
}
// Verify the downloaded driver (from normalized local path)
val installedFile = File(localDriverPath)
// Verify the downloaded driver
val installedFile = File(driverPath)
val metadata = GpuDriverHelper.getMetadataFromZip(installedFile)
if (metadata.name == null) {
Log.error(
"[CustomSettingsHandler] Downloaded driver is invalid: $localDriverPath"
"[CustomSettingsHandler] Downloaded driver is invalid: $driverPath"
)
Toast.makeText(
activity,
@ -237,7 +232,7 @@ object CustomSettingsHandler {
}
// Add to driver list
driverViewModel.onDriverAdded(Pair(localDriverPath, metadata))
driverViewModel.onDriverAdded(Pair(driverPath, metadata))
Log.info(
"[CustomSettingsHandler] Successfully downloaded and installed driver: ${metadata.name}"
)
@ -273,7 +268,7 @@ object CustomSettingsHandler {
// Driver exists, verify it's valid
val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
if (metadata.name == null) {
Log.error("[CustomSettingsHandler] Invalid driver file: $localDriverPath")
Log.error("[CustomSettingsHandler] Invalid driver file: $driverPath")
Toast.makeText(
activity,
activity.getString(
@ -464,8 +459,6 @@ object CustomSettingsHandler {
if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
return trimmed.substringAfter("driver_path=")
.trim()
.removeSurrounding("\"", "\"")
}
}

View file

@ -68,48 +68,6 @@ object DriverResolver {
val filename: String
)
// Matching helpers
private val KNOWN_SUFFIXES = listOf(
".adpkg.zip",
".zip",
".7z",
".tar.gz",
".tar.xz",
".rar"
)
private fun stripKnownSuffixes(name: String): String {
var result = name
var changed: Boolean
do {
changed = false
for (s in KNOWN_SUFFIXES) {
if (result.endsWith(s, ignoreCase = true)) {
result = result.dropLast(s.length)
changed = true
}
}
} while (changed)
return result
}
private fun normalizeName(name: String): String {
val base = stripKnownSuffixes(name.lowercase())
// Remove non-alphanumerics to make substring checks resilient
return base.replace(Regex("[^a-z0-9]+"), " ").trim()
}
private fun tokenize(name: String): Set<String> =
normalizeName(name).split(Regex("\\s+")).filter { it.isNotBlank() }.toSet()
// Jaccard similarity between two sets
private fun jaccard(a: Set<String>, b: Set<String>): Double {
if (a.isEmpty() || b.isEmpty()) return 0.0
val inter = a.intersect(b).size.toDouble()
val uni = a.union(b).size.toDouble()
return if (uni == 0.0) 0.0 else inter / uni
}
/**
* Resolve a driver download URL from its filename
* @param filename The driver filename (e.g., "turnip_mrpurple-T19-toasted.adpkg.zip")
@ -140,7 +98,7 @@ object DriverResolver {
async {
searchRepository(repoPath, filename)
}
}.firstNotNullOfOrNull { it.await() }.also { resolved ->
}.mapNotNull { it.await() }.firstOrNull().also { resolved ->
// Cache the result if found
resolved?.let {
urlCache[filename] = it
@ -161,56 +119,22 @@ object DriverResolver {
releaseCache[repoPath] = it
}
// First pass: exact name (case-insensitive) against asset filenames
val target = filename.lowercase()
// Search through all releases and artifacts
for (release in releases) {
for (artifact in release.artifacts) {
if (artifact.name.equals(filename, ignoreCase = true) || artifact.name.lowercase() == target) {
Log.info("[DriverResolver] Found $filename in $repoPath/${release.tagName}")
if (artifact.name == filename) {
Log.info(
"[DriverResolver] Found $filename in $repoPath/${release.tagName}"
)
return@withContext ResolvedDriver(
downloadUrl = artifact.url.toString(),
repoPath = repoPath,
releaseTag = release.tagName,
filename = artifact.name
filename = filename
)
}
}
}
// Second pass: fuzzy match by asset filenames only
val reqNorm = normalizeName(filename)
val reqTokens = tokenize(filename)
var best: ResolvedDriver? = null
var bestScore = 0.0
for (release in releases) {
for (artifact in release.artifacts) {
val artNorm = normalizeName(artifact.name)
val artTokens = tokenize(artifact.name)
var score = jaccard(reqTokens, artTokens)
// Boost if one normalized name contains the other
if (artNorm.contains(reqNorm) || reqNorm.contains(artNorm)) {
score = maxOf(score, 0.92)
}
if (score > bestScore) {
bestScore = score
best = ResolvedDriver(
downloadUrl = artifact.url.toString(),
repoPath = repoPath,
releaseTag = release.tagName,
filename = artifact.name
)
}
}
}
// Threshold to avoid bad guesses, this worked fine in testing but might need tuning
if (best != null && bestScore >= 0.6) {
Log.info("[DriverResolver] Fuzzy matched $filename -> ${best.filename} in ${best.repoPath} (score=%.2f)".format(bestScore))
return@withContext best
}
null
} catch (e: Exception) {
Log.error("[DriverResolver] Failed to search $repoPath: ${e.message}")
@ -372,8 +296,8 @@ object DriverResolver {
context: Context,
onProgress: ((Float) -> Unit)? = null
): Uri? {
// Extract filename from path (support both separators)
val filename = driverPath.substringAfterLast('/').substringAfterLast('\\')
// Extract filename from path
val filename = driverPath.substringAfterLast('/')
// Check if driver already exists locally
val localPath = "${GpuDriverHelper.driverStoragePath}$filename"

View file

@ -417,11 +417,14 @@ static void* ChooseVirtualBase(size_t virtual_size) {
#else
static void* ChooseVirtualBase(size_t virtual_size) {
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
#if defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__)
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
if (virtual_base != MAP_FAILED)
if (virtual_base != MAP_FAILED) {
return virtual_base;
}
#endif
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
}

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
@ -22,16 +19,6 @@
namespace Dynarmic::Common {
// prevents this function from printing 56,000 character warning messages
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-stack-usage"
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wno-stack-usage"
#endif
template<typename Function, typename... Values>
inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
#ifdef _MSC_VER
@ -47,11 +34,4 @@ inline auto GenerateLookupTableFromList(Function f, mcl::mp::list<Values...>) {
return MapT(pair_array.begin(), pair_array.end());
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
} // namespace Dynarmic::Common

View file

@ -1378,13 +1378,13 @@ void Device::CollectPhysicalMemoryInfo() {
device_access_memory += mem_properties.memoryHeaps[element].size;
}
if (!is_integrated) {
const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
const u64 reserve_memory = std::min<u64>(device_access_memory / 4, 2_GiB);
device_access_memory -= reserve_memory;
if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
// Account for resolution scaling in memory limits
const size_t normal_memory = 6_GiB;
const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
const size_t normal_memory = 8_GiB;
const size_t scaler_memory = 2_GiB * Settings::values.resolution_info.ScaleUp(1);
device_access_memory =
std::min<u64>(device_access_memory, normal_memory + scaler_memory);
}
@ -1393,7 +1393,7 @@ void Device::CollectPhysicalMemoryInfo() {
}
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
device_access_memory = static_cast<u64>(std::max<s64>(
std::min<s64>(available_memory - 8_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB)));
std::min<s64>(available_memory - 4_GiB, 6_GiB), std::min<s64>(local_memory, 6_GiB)));
}
void Device::CollectToolingInfo() {

View file

@ -553,6 +553,9 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
// Gen keys if necessary
OnCheckFirmwareDecryption();
// Check firmware
OnCheckFirmware();
game_list->LoadCompatibilityList();
// force reload on first load to ensure add-ons get updated
game_list->PopulateAsync(UISettings::values.game_dirs, false);
@ -3091,7 +3094,34 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::filesystem::path& command,
const std::string& arguments, const std::string& categories,
const std::string& keywords, const std::string& name) try {
#ifdef _WIN32 // Windows
#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#elif defined(_WIN32) // Windows
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
@ -3153,34 +3183,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
return false;
}
return true;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX
std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
if (!shortcut_stream.is_open()) {
LOG_ERROR(Frontend, "Failed to create shortcut");
return false;
}
// TODO: Migrate fmt::print to std::print in futures STD C++ 23.
fmt::print(shortcut_stream, "[Desktop Entry]\n");
fmt::print(shortcut_stream, "Type=Application\n");
fmt::print(shortcut_stream, "Version=1.0\n");
fmt::print(shortcut_stream, "Name={}\n", name);
if (!comment.empty()) {
fmt::print(shortcut_stream, "Comment={}\n", comment);
}
if (std::filesystem::is_regular_file(icon_path)) {
fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
}
fmt::print(shortcut_stream, "TryExec={}\n", command.string());
fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
if (!categories.empty()) {
fmt::print(shortcut_stream, "Categories={}\n", categories);
}
if (!keywords.empty()) {
fmt::print(shortcut_stream, "Keywords={}\n", keywords);
}
return true;
#else // Unsupported platform
#else // Unsupported platform
return false;
#endif
} catch (const std::exception& e) {
@ -3225,7 +3228,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
#if defined(_WIN32)
out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
ico_extension = "ico";
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
#elif defined(__linux__) || defined(__FreeBSD__)
out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
#endif
// Create icons directory if it doesn't exist
@ -4456,6 +4459,7 @@ void GMainWindow::InstallFirmware(const QString& location, bool recursive) {
progress.close();
OnCheckFirmwareDecryption();
OnCheckFirmware();
}
void GMainWindow::OnInstallFirmware() {
@ -4576,6 +4580,7 @@ void GMainWindow::OnInstallDecryptionKeys() {
}
OnCheckFirmwareDecryption();
OnCheckFirmware();
}
void GMainWindow::OnAbout() {
@ -4604,7 +4609,6 @@ void GMainWindow::OnToggleStatusBar() {
void GMainWindow::OnGameListRefresh() {
// force reload add-ons etc
game_list->ForceRefreshGameDirectory();
SetFirmwareVersion();
}
void GMainWindow::OnAlbum() {
@ -4703,42 +4707,13 @@ void GMainWindow::OnOpenControllerMenu() {
}
void GMainWindow::OnHomeMenu() {
auto result = FirmwareManager::VerifyFirmware(*system.get());
switch (result) {
case FirmwareManager::ErrorFirmwareMissing:
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install firmware to use the Home Menu."));
return;
case FirmwareManager::ErrorFirmwareCorrupted:
QMessageBox::warning(this, tr("Firmware Corrupted"),
tr(FirmwareManager::GetFirmwareCheckString(result)));
return;
case FirmwareManager::ErrorFirmwareTooNew: {
if (!UISettings::values.show_fw_warning.GetValue()) break;
QMessageBox box(QMessageBox::Warning,
tr("Firmware Too New"),
tr(FirmwareManager::GetFirmwareCheckString(result)) + tr("\nContinue anyways?"),
QMessageBox::Yes | QMessageBox::No,
this);
QCheckBox *checkbox = new QCheckBox(tr("Don't show again"));
box.setCheckBox(checkbox);
int button = box.exec();
if (checkbox->isChecked()) {
UISettings::values.show_fw_warning.SetValue(false);
}
if (button == static_cast<int>(QMessageBox::No)) return;
break;
} default:
break;
}
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
QMessageBox::warning(this, tr("No firmware available"),
tr("Please install the firmware to use the Home Menu."));
return;
}
auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
if (!qlaunch_applet_nca) {
@ -4878,7 +4853,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
}
}
#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
#if defined(__linux__)
// Special case for AppImages
// Warn once if we are making a shortcut to a volatile AppImage
if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
@ -4888,7 +4863,7 @@ void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program
}
UISettings::values.shortcut_already_warned = true;
}
#endif
#endif // __linux__
// Create shortcut
std::string arguments{arguments_};
@ -5265,6 +5240,19 @@ void GMainWindow::OnCheckFirmwareDecryption() {
UpdateMenuState();
}
void GMainWindow::OnCheckFirmware() {
auto result = FirmwareManager::VerifyFirmware(*system.get());
switch (result) {
case FirmwareManager::FirmwareGood:
break;
default:
QMessageBox::warning(this, tr("Firmware Read Error"),
tr(FirmwareManager::GetFirmwareCheckString(result)));
break;
}
}
bool GMainWindow::CheckFirmwarePresence() {
return FirmwareManager::CheckFirmwarePresence(*system.get());
}
@ -5742,13 +5730,17 @@ int main(int argc, char* argv[]) {
#ifdef _WIN32
// Increases the maximum open file limit to 8192
_setmaxstdio(8192);
#elif defined(__APPLE__)
#endif
#ifdef __APPLE__
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
const auto bin_path = Common::FS::GetBundleDirectory() / "..";
chdir(Common::FS::PathToUTF8String(bin_path).c_str());
#elif defined(__unix__) && !defined(__ANDROID__)
#endif
#ifdef __linux__
// Set the DISPLAY variable in order to open web browsers
// TODO (lat9nq): Find a better solution for AppImages to start external applications
if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
@ -5757,7 +5749,7 @@ int main(int argc, char* argv[]) {
// Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
// suffix.
QGuiApplication::setDesktopFileName(QStringLiteral("dev.eden_emu.eden"));
QGuiApplication::setDesktopFileName(QStringLiteral("org.eden_emu.eden"));
#endif
SetHighDPIAttributes();

View file

@ -424,6 +424,7 @@ private slots:
void OnCreateHomeMenuShortcut(GameListShortcutTarget target);
void OnCaptureScreenshot();
void OnCheckFirmwareDecryption();
void OnCheckFirmware();
void OnLanguageChanged(const QString& locale);
void OnMouseActivity();
bool OnShutdownBegin();

View file

@ -212,9 +212,6 @@ struct Values {
// Play time
Setting<bool> show_play_time{linkage, true, "show_play_time", Category::UiGameList};
// misc
Setting<bool> show_fw_warning{linkage, true, "show_fw_warning", Category::Miscellaneous};
bool configuration_applied;
bool reset_to_defaults;
bool shortcut_already_warned{false};

View file

@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -141,7 +138,7 @@ bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image)
icon_file.Close();
return true;
#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
#elif defined(__linux__) || defined(__FreeBSD__)
// Convert and write the icon as a PNG
if (!image.save(QString::fromStdString(icon_path.string()))) {
LOG_ERROR(Frontend, "Could not write icon as PNG to file");