Compare commits
21 Commits
no-d24
...
qcom-weird
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92fb89cbf0 | ||
|
|
d656e347c8 | ||
|
|
8859809ebd | ||
|
|
318998cbb5 | ||
|
|
f58097e814 | ||
|
|
1efef85352 | ||
|
|
ea0e5d630c | ||
|
|
9e610ea098 | ||
|
|
41af6ea645 | ||
|
|
91b0432591 | ||
|
|
46239dafa1 | ||
|
|
bf23921f07 | ||
|
|
e63f71c787 | ||
|
|
027085e5ba | ||
|
|
f40025fd9b | ||
|
|
ed39ec4738 | ||
|
|
fe13539d72 | ||
|
|
be218cc020 | ||
|
|
c3cbe2d4d0 | ||
|
|
79b162a37c | ||
|
|
f3fbb3812f |
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||
- name: Eden Discord
|
||||
url: https://discord.gg/HstXbPch7X
|
||||
|
||||
@@ -14,6 +14,7 @@ License: GPL-2.0-or-later
|
||||
|
||||
Files: dist/qt_themes/default/icons/256x256/eden.png
|
||||
dist/qt_themes/default/icons/256x256/eden_named.png
|
||||
dist/Assets.car
|
||||
dist/yuzu.bmp
|
||||
dist/eden.icns
|
||||
dist/eden.ico
|
||||
|
||||
@@ -128,9 +128,6 @@ if (YUZU_STATIC_BUILD)
|
||||
## find .a libs first (static, usually)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
|
||||
## some libraries define a Library::Name_static alternative ##
|
||||
set(YUZU_STATIC_SUFFIX _static)
|
||||
|
||||
## some libraries use CMAKE_IMPORT_LIBRARY_SUFFIX e.g. Harfbuzz ##
|
||||
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
|
||||
|
||||
@@ -161,20 +158,22 @@ if (YUZU_STATIC_BUILD)
|
||||
set(YUZU_USE_BUNDLED_OPENSSL ON)
|
||||
|
||||
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
||||
|
||||
## some libraries define a Library::Name_static alternative ##
|
||||
set(MBEDTLS_LIB_SUFFIX _static)
|
||||
elseif(APPLE)
|
||||
# these libs do not properly provide static libs/let you do it with cmake
|
||||
set(YUZU_USE_CPM ON)
|
||||
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
set(YUZU_USE_BUNDLED_SDL2 ON)
|
||||
set(YUZU_USE_BUNDLED_OPENSSL ON)
|
||||
|
||||
# IMPORTED_IMPLIB not set for imported target
|
||||
# TODO(crueter): wtf
|
||||
# these libs do not properly provide static libs/let you do it with cmake
|
||||
set(fmt_FORCE_BUNDLED ON)
|
||||
set(SPIRV-Tools_FORCE_BUNDLED ON)
|
||||
set(SPIRV-Headers_FORCE_BUNDLED ON)
|
||||
set(zstd_FORCE_BUNDLED ON)
|
||||
set(MbedTLS_FORCE_BUNDLED ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
# Contributing
|
||||
|
||||
**The Contributor's Guide has moved to [the yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).**
|
||||
You want to contribute? Please consult [the development guide](./docs/Development.md).
|
||||
|
||||
Don't forget to [get a git account](./docs/SIGNUP.md) - not a requirement per se but it's highly recommended.
|
||||
|
||||
@@ -21,7 +21,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
|
||||
|
||||
<p align="center">
|
||||
</a>
|
||||
<a href="https://discord.gg/kXAmGCXBGD">
|
||||
<a href="https://discord.gg/HstXbPch7X">
|
||||
<img src="https://img.shields.io/discord/1367654015269339267?color=5865F2&label=Eden&logo=discord&logoColor=white"
|
||||
alt="Discord">
|
||||
</a>
|
||||
@@ -52,8 +52,8 @@ Check out our [website](https://eden-emu.dev) for the latest news on exciting fe
|
||||
|
||||
## Development
|
||||
|
||||
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/kXAmGCXBGD) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
||||
You can also follow us on [X (Twitter)](https://x.com/edenemuofficial) for updates and announcements.
|
||||
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/HstXbPch7X) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
||||
You can also follow us on [X (Twitter)](https://nitter.poast.org/edenemuofficial) for updates and announcements.
|
||||
|
||||
If you would like to contribute, we are open to new developers and pull requests. Please ensure that your work is of a high standard and properly documented. You can also contact any of the developers on Discord or Revolt to learn more about the current state of the emulator.
|
||||
|
||||
@@ -82,7 +82,7 @@ Any donations received will go towards things such as:
|
||||
* Additional hardware (e.g. GPUs as needed to improve rendering support, other peripherals to add support for, etc.)
|
||||
* CI Infrastructure
|
||||
|
||||
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/edenemu) and talk to Camille or any of our other developers.
|
||||
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/HstXbPch7X) and talk to Camille or any of our other developers.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
BIN
dist/Assets.car
vendored
Normal file
BIN
dist/Assets.car
vendored
Normal file
Binary file not shown.
@@ -43,6 +43,12 @@ Various graphical filters exist - each of them aimed at a specific target/image
|
||||
- **Pros**: Offers decent pixel-art upscaling.
|
||||
- **Cons**: Only works for pixel-art.
|
||||
|
||||
### Anisotropy values
|
||||
|
||||
The anisotropy value is (value game wants + the set value); **Default** will use the native anisotropy value as it would be on hardware. **Automatic** sets it according to screen resolution. Turning off anisotropy is not recommended as it can break a myriad of games, however it is provided in the name of flexibility.
|
||||
|
||||
Values from x2, x4, x8, x16, x32 up to x64 values are provided. This should be enough to not need to revise those values in my lifetime ever again.
|
||||
|
||||
### External
|
||||
|
||||
While stock shaders offer a basic subset of options for most users, programs such as [ReShade](https://github.com/crosire/reshade) offer a more flexible experience. In addition to that users can also seek out modifications (mods) for enhancing visual experience (60 FPS mods, HDR, etc).
|
||||
|
||||
12
docs/user/Native.md
Normal file
12
docs/user/Native.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# User Handbook - Native Application Development
|
||||
|
||||
Debugging on physical hardware can get tedious and time consuming. Users are empowered with the debugging capabilities of the emulator to ensure their applications run as-is on the system. To the greatest extent possible atleast.
|
||||
|
||||
## Debugging
|
||||
|
||||
**Standard key prefix**: Allows to redirect the key manager to a file other than `prod.keys` (for example `other` would redirect to `other.keys`). This is useful for testing multiple keysets. Default is `prod`.
|
||||
|
||||
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as it will be writen in-place (that is, it will be filled with the default serial and then overwrite the serial from the beginning).
|
||||
- Battery serial: `YUZU0EMULATOR14022024`
|
||||
- Board serial: `YUZ10000000001`
|
||||
If the user were to set their board serial as `ABC`, then it will be written in-place and the resulting serial would be `ABC10000000001`. There are no underlying checks to ensure correctness of serials other than a hard limit of 16-characters for both.
|
||||
@@ -11,3 +11,4 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e
|
||||
- **[Testing](Testing.md)**
|
||||
- **[Data, savefiles and storage](Storage.md)**
|
||||
- **[Orphaned Profiles](Orphaned.md)**
|
||||
- **[Native Application Development](Native.md)**
|
||||
|
||||
@@ -29,8 +29,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
||||
@@ -110,5 +109,15 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -222,6 +222,11 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun getUpdateUrl(version: String): String
|
||||
|
||||
/**
|
||||
* Return the URL to download the APK for the given version
|
||||
*/
|
||||
external fun getUpdateApkUrl(version: String, packageId: String): String
|
||||
|
||||
/**
|
||||
* Returns whether the update checker is enabled through CMAKE options.
|
||||
*/
|
||||
|
||||
@@ -235,10 +235,13 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
val isPhysicalKeyboard = event.source and InputDevice.SOURCE_KEYBOARD == InputDevice.SOURCE_KEYBOARD &&
|
||||
event.device?.isVirtual == false
|
||||
|
||||
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
|
||||
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD &&
|
||||
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
|
||||
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
|
||||
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE &&
|
||||
!isPhysicalKeyboard
|
||||
) {
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||
USE_LRU_CACHE("use_lru_cache"),
|
||||
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning");
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
|
||||
ENABLE_OVERLAY("enable_overlay");
|
||||
|
||||
|
||||
// external fun isFrameSkippingEnabled(): Boolean
|
||||
|
||||
@@ -61,7 +61,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
LOGIN_SHARE_APPLET("login_share_applet_mode"),
|
||||
WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"),
|
||||
MY_PAGE_APPLET("my_page_applet_mode"),
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide")
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
||||
|
||||
@@ -762,6 +762,7 @@ abstract class SettingsItem(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_UPDATE_CHECKS,
|
||||
titleId = R.string.enable_update_checks,
|
||||
descriptionId = R.string.enable_update_checks_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
@@ -787,6 +788,16 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.use_auto_stub_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
IntSetting.DEBUG_KNOBS,
|
||||
titleId = R.string.debug_knobs,
|
||||
descriptionId = R.string.debug_knobs_description,
|
||||
valueHint = R.string.debug_knobs_hint,
|
||||
min = 0,
|
||||
max = 65535
|
||||
)
|
||||
)
|
||||
|
||||
val fastmem = object : AbstractBooleanSetting {
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||
@@ -838,7 +849,14 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.airplane_mode_description
|
||||
)
|
||||
)
|
||||
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_OVERLAY,
|
||||
titleId = R.string.enable_overlay,
|
||||
descriptionId = R.string.enable_overlay_description
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,12 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
updateButtonState(isValid)
|
||||
}
|
||||
|
||||
/*
|
||||
* xbzk: these two events, along with attachRepeat feature,
|
||||
* were causing spinbox buttons to respond twice per press
|
||||
* cutting these out to retain accelerated press functionality
|
||||
* TODO: clean this out later if no issues arise
|
||||
*
|
||||
spinboxBinding.buttonDecrement.setOnClickListener {
|
||||
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
|
||||
val newValue = current - 1
|
||||
@@ -199,6 +205,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
spinboxBinding.editValue.setText(newValue.toString())
|
||||
updateValidity(newValue)
|
||||
}
|
||||
*/
|
||||
|
||||
fun attachRepeat(button: View, delta: Int) {
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
@@ -491,6 +491,7 @@ class SettingsFragmentPresenter(
|
||||
sl.apply {
|
||||
add(IntSetting.SWKBD_APPLET.key)
|
||||
add(BooleanSetting.AIRPLANE_MODE.key)
|
||||
add(BooleanSetting.ENABLE_OVERLAY.key)
|
||||
}
|
||||
}
|
||||
private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
|
||||
@@ -1167,9 +1168,10 @@ class SettingsFragmentPresenter(
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.USE_AUTO_STUB.key)
|
||||
add(SettingsItem.FASTMEM_COMBINED)
|
||||
|
||||
add(HeaderSetting(R.string.log))
|
||||
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
||||
add(HeaderSetting(R.string.general))
|
||||
add(IntSetting.DEBUG_KNOBS.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +145,12 @@ class GamePropertiesFragment : Fragment() {
|
||||
val seconds = playTimeSeconds % 60
|
||||
|
||||
val readablePlayTime = when {
|
||||
hours > 0 -> "${hours}h ${minutes}m ${seconds}s"
|
||||
minutes > 0 -> "${minutes}m ${seconds}s"
|
||||
else -> "${seconds}s"
|
||||
}
|
||||
hours > 0 -> "$hours${getString(R.string.hours_abbr)} $minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||
minutes > 0 -> "$minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||
else -> "$seconds${getString(R.string.seconds_abbr)}"
|
||||
}
|
||||
|
||||
append(getString(R.string.playtime))
|
||||
append(readablePlayTime)
|
||||
append(getString(R.string.playtime) + " " + readablePlayTime)
|
||||
}
|
||||
|
||||
binding.playtime.setOnClickListener {
|
||||
|
||||
@@ -53,8 +53,16 @@ import androidx.core.content.edit
|
||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import kotlin.text.compareTo
|
||||
import androidx.core.net.toUri
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.updater.APKDownloader
|
||||
import org.yuzu.yuzu_emu.updater.APKInstaller
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
@@ -186,9 +194,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
.setTitle(R.string.update_available)
|
||||
.setMessage(getString(R.string.update_available_description, version))
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val url = NativeLibrary.getUpdateUrl(version)
|
||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
startActivity(intent)
|
||||
downloadAndInstallUpdate(version)
|
||||
}
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
@@ -201,6 +207,87 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun downloadAndInstallUpdate(version: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val packageId = applicationContext.packageName
|
||||
val apkUrl = NativeLibrary.getUpdateApkUrl(version, packageId)
|
||||
val apkFile = File(cacheDir, "update-$version.apk")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
showDownloadProgressDialog()
|
||||
}
|
||||
|
||||
val downloader = APKDownloader(apkUrl, apkFile)
|
||||
downloader.download(
|
||||
onProgress = { progress ->
|
||||
runOnUiThread {
|
||||
updateDownloadProgress(progress)
|
||||
}
|
||||
},
|
||||
onComplete = { success ->
|
||||
runOnUiThread {
|
||||
dismissDownloadProgressDialog()
|
||||
if (success) {
|
||||
val installer = APKInstaller(this@MainActivity)
|
||||
installer.install(
|
||||
apkFile,
|
||||
onComplete = {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
R.string.update_installed_successfully,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
},
|
||||
onFailure = { exception ->
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.update_install_failed, exception.message),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.update_download_failed) + "\n\nURL: $apkUrl",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var progressDialog: androidx.appcompat.app.AlertDialog? = null
|
||||
private var progressBar: LinearProgressIndicator? = null
|
||||
private var progressMessage: MaterialTextView? = null
|
||||
|
||||
private fun showDownloadProgressDialog() {
|
||||
val progressView = layoutInflater.inflate(R.layout.dialog_download_progress, null)
|
||||
progressBar = progressView.findViewById(R.id.download_progress_bar)
|
||||
progressMessage = progressView.findViewById(R.id.download_progress_message)
|
||||
|
||||
progressDialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.downloading_update)
|
||||
.setView(progressView)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
progressDialog?.show()
|
||||
}
|
||||
|
||||
private fun updateDownloadProgress(progress: Int) {
|
||||
progressBar?.progress = progress
|
||||
progressMessage?.text = "$progress%"
|
||||
}
|
||||
|
||||
private fun dismissDownloadProgressDialog() {
|
||||
progressDialog?.dismiss()
|
||||
progressDialog = null
|
||||
progressBar = null
|
||||
progressMessage = null
|
||||
}
|
||||
|
||||
|
||||
fun displayMultiplayerDialog() {
|
||||
val dialog = NetPlayDialog(this)
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class APKDownloader(private val url: String, private val outputFile: File) {
|
||||
|
||||
fun download(onProgress: (Int) -> Unit, onComplete: (Boolean) -> Unit) {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder().url(url).build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
e.printStackTrace()
|
||||
onComplete(false)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
if (response.isSuccessful) {
|
||||
response.body?.let { body ->
|
||||
val contentLength = body.contentLength()
|
||||
try {
|
||||
val inputStream = body.byteStream()
|
||||
val outputStream = FileOutputStream(outputFile)
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
var totalBytesRead: Long = 0
|
||||
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
totalBytesRead += bytesRead
|
||||
val progress = (totalBytesRead * 100 / contentLength).toInt()
|
||||
onProgress(progress)
|
||||
}
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
inputStream.close()
|
||||
onComplete(true)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
onComplete(false)
|
||||
}
|
||||
} ?: run {
|
||||
onComplete(false)
|
||||
}
|
||||
} else {
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import androidx.core.content.FileProvider
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class APKInstaller(private val context: Context) {
|
||||
|
||||
fun install(apkFile: File, onComplete: () -> Unit, onFailure: (Exception) -> Unit) {
|
||||
try {
|
||||
val apkUri: Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
context.applicationContext.packageName + ".provider",
|
||||
apkFile
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent)
|
||||
|
||||
GlobalScope.launch {
|
||||
val receiver = AppInstallReceiver(onComplete, onFailure)
|
||||
context.registerReceiver(receiver, IntentFilter().apply {
|
||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||
addDataScheme("package")
|
||||
})
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
onFailure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
||||
class AppInstallReceiver(
|
||||
private val onComplete: () -> Unit,
|
||||
private val onFailure: (Exception) -> Unit
|
||||
) : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val packageName = intent.data?.schemeSpecificPart
|
||||
when (intent.action) {
|
||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REPLACED -> {
|
||||
Log.i("AppInstallReceiver", "Package installed or updated: $packageName")
|
||||
onComplete()
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
else -> {
|
||||
onFailure(Exception("Installation failed for package: $packageName"))
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1613,6 +1613,37 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
|
||||
env->ReleaseStringUTFChars(version, version_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||
JNIEnv* env,
|
||||
jobject obj,
|
||||
jstring version,
|
||||
jstring packageId) {
|
||||
const char* version_str = env->GetStringUTFChars(version, nullptr);
|
||||
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
|
||||
|
||||
std::string variant;
|
||||
std::string package_id(package_id_str);
|
||||
|
||||
if (package_id.find("dev.legacy.eden_emulator") != std::string::npos) {
|
||||
variant = "legacy";
|
||||
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
|
||||
variant = "optimized";
|
||||
} else {
|
||||
variant = "standard";
|
||||
}
|
||||
|
||||
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant);
|
||||
const std::string url = fmt::format("{}/{}/releases/download/{}/{}",
|
||||
std::string{Common::g_build_auto_update_website},
|
||||
std::string{Common::g_build_auto_update_repo},
|
||||
version_str,
|
||||
apk_filename);
|
||||
|
||||
env->ReleaseStringUTFChars(version, version_str);
|
||||
env->ReleaseStringUTFChars(packageId, package_id_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/download_progress_message"
|
||||
style="@style/TextAppearance.Material3.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="0%"
|
||||
android:textAlignment="center"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/download_progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100"
|
||||
android:progress="0"
|
||||
app:indicatorColor="?attr/colorPrimary"
|
||||
app:trackColor="?attr/colorSurfaceVariant"
|
||||
app:trackCornerRadius="4dp"
|
||||
app:trackThickness="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1016,10 +1016,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">خلفيات سوداء</string>
|
||||
|
||||
@@ -727,10 +727,13 @@
|
||||
<string name="theme_mode_dark">تاریک</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">پاشبنەمای ڕەش</string>
|
||||
|
||||
@@ -693,10 +693,13 @@
|
||||
<string name="theme_mode_dark">Tmavé</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Černá pozadí</string>
|
||||
|
||||
@@ -912,10 +912,13 @@ Wirklich fortfahren?</string>
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
|
||||
|
||||
@@ -975,10 +975,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">x2</string>
|
||||
<string name="multiplier_four">x4</string>
|
||||
<string name="multiplier_eight">x8</string>
|
||||
<string name="multiplier_sixteen">x16</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Fondos oscuros</string>
|
||||
|
||||
@@ -861,10 +861,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">پسزمینه مشکی</string>
|
||||
|
||||
@@ -988,10 +988,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Arrière-plan noir</string>
|
||||
|
||||
@@ -776,10 +776,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">רקעים שחורים</string>
|
||||
|
||||
@@ -875,10 +875,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Fekete háttér</string>
|
||||
|
||||
@@ -929,10 +929,13 @@
|
||||
<string name="cubeb">Cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Gunakan Latar Belakang Hitam</string>
|
||||
|
||||
@@ -988,10 +988,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Sfondi neri</string>
|
||||
|
||||
@@ -778,10 +778,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">完全な黒を使用</string>
|
||||
|
||||
@@ -837,10 +837,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">검정 배경</string>
|
||||
|
||||
@@ -740,10 +740,13 @@
|
||||
<string name="theme_mode_dark">Mørk</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Svart bakgrunn</string>
|
||||
|
||||
@@ -1012,10 +1012,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Czarne tła</string>
|
||||
|
||||
@@ -976,10 +976,13 @@ uma tentativa de mapeamento automático</string>
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Planos de fundo pretos</string>
|
||||
|
||||
@@ -890,10 +890,13 @@ uma tentativa de mapeamento automático</string>
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Plano de fundo preto</string>
|
||||
|
||||
@@ -1010,10 +1010,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чёрный фон</string>
|
||||
|
||||
@@ -888,10 +888,13 @@
|
||||
<string name="cubeb">Цубеб</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2к</string>
|
||||
<string name="multiplier_four">4к</string>
|
||||
<string name="multiplier_eight">8к</string>
|
||||
<string name="multiplier_sixteen">16к</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Црна позадина</string>
|
||||
|
||||
@@ -1012,10 +1012,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Чорний фон</string>
|
||||
|
||||
@@ -740,10 +740,13 @@
|
||||
<string name="theme_mode_dark">Tối</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Nền tối</string>
|
||||
|
||||
@@ -984,10 +984,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">使用黑色背景</string>
|
||||
|
||||
@@ -984,10 +984,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">黑色背景</string>
|
||||
|
||||
@@ -487,10 +487,13 @@
|
||||
<string-array name="anisoEntries">
|
||||
<item>@string/auto</item>
|
||||
<item>@string/slider_default</item>
|
||||
<item>@string/multiplier_two</item>
|
||||
<item>@string/multiplier_four</item>
|
||||
<item>@string/multiplier_eight</item>
|
||||
<item>@string/multiplier_sixteen</item>
|
||||
<item>@string/multiplier_x2</item>
|
||||
<item>@string/multiplier_x4</item>
|
||||
<item>@string/multiplier_x8</item>
|
||||
<item>@string/multiplier_x16</item>
|
||||
<item>@string/multiplier_x32</item>
|
||||
<item>@string/multiplier_x64</item>
|
||||
<item>@string/multiplier_none</item>
|
||||
</string-array>
|
||||
<integer-array name="anisoValues">
|
||||
<item>0</item>
|
||||
|
||||
@@ -135,6 +135,9 @@
|
||||
<string name="memory_layout_description">(EXPERIMENTAL) Change the emulated memory layout. This setting will not increase performance, but may help with games utilizing high resolutions via mods. Do not use on phones with 8GB of RAM or less. Only works on the Dynarmic (JIT) backend.</string>
|
||||
<string name="dma_accuracy">DMA Accuracy</string>
|
||||
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
|
||||
<string name="debug_knobs">Debug knobs</string>
|
||||
<string name="debug_knobs_description">For development use only.</string>
|
||||
<string name="debug_knobs_hint">0 to 65535</string>
|
||||
|
||||
<!-- Shader Backend -->
|
||||
<string name="shader_backend">Shader Backend</string>
|
||||
@@ -271,9 +274,14 @@
|
||||
<string name="folder">Folder</string>
|
||||
<string name="dont_show_again">Don\'t Show Again</string>
|
||||
<string name="add_directory_success">New game directory added successfully </string>
|
||||
<string name="enable_update_checks">Check for updates on app startup.</string>
|
||||
<string name="enable_update_checks">Check for Updates</string>
|
||||
<string name="enable_update_checks_description">Check for updates on launch, and optionally download and install the new update.</string>
|
||||
<string name="update_available">Update Available</string>
|
||||
<string name="update_available_description">A new version is available: %1$s\n\nWould you like to download it?</string>
|
||||
<string name="downloading_update">Downloading Update</string>
|
||||
<string name="update_download_failed">Failed to download update</string>
|
||||
<string name="update_installed_successfully">Update installed successfully</string>
|
||||
<string name="update_install_failed">Failed to install update: %1$s</string>
|
||||
<string name="home_search">Search</string>
|
||||
<string name="home_settings">Settings</string>
|
||||
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
|
||||
@@ -443,9 +451,9 @@
|
||||
<string name="user_data_import_success">User data imported successfully</string>
|
||||
<string name="user_data_export_cancelled">Export cancelled</string>
|
||||
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
|
||||
<string name="discord_link" translatable="false">https://discord.gg/kXAmGCXBGD</string>
|
||||
<string name="discord_link" translatable="false">https://discord.gg/HstXbPch7X</string>
|
||||
<string name="revolt_link" translatable="false">https://rvlt.gg/qKgFEAbH</string>
|
||||
<string name="x_link" translatable="false">https://x.com/edenemuofficial</string>
|
||||
<string name="x_link" translatable="false">https://nitter.poast.org/edenemuofficial</string>
|
||||
<string name="website_link" translatable="false">https://eden-emu.dev</string>
|
||||
<string name="github_link" translatable="false">https://git.eden-emu.dev/eden-emu</string>
|
||||
|
||||
@@ -529,6 +537,8 @@
|
||||
<string name="flush_by_line">Flush debug logs by line</string>
|
||||
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
|
||||
|
||||
<string name="general">General</string>
|
||||
|
||||
<!-- Audio settings strings -->
|
||||
<string name="audio_output_engine">Output engine</string>
|
||||
<string name="audio_volume">Volume</string>
|
||||
@@ -708,7 +718,7 @@
|
||||
<string name="copy_details">Copy details</string>
|
||||
<string name="add_ons">Add-ons</string>
|
||||
<string name="add_ons_description">Toggle mods, updates and DLC</string>
|
||||
<string name="playtime">Playtime: </string>
|
||||
<string name="playtime">Playtime:</string>
|
||||
<string name="reset_playtime">Clear Playtime</string>
|
||||
<string name="reset_playtime_description">Reset the current game\'s playtime back to 0 seconds</string>
|
||||
<string name="reset_playtime_warning_description">This will clear the current game\'s playtime data. Are you sure?</string>
|
||||
@@ -716,6 +726,9 @@
|
||||
<string name="edit_playtime">Edit Playtime</string>
|
||||
<string name="hours">Hours</string>
|
||||
<string name="minutes">Minutes</string>
|
||||
<string name="hours_abbr">h</string>
|
||||
<string name="minutes_abbr">m</string>
|
||||
<string name="seconds_abbr">s</string>
|
||||
<string name="hours_must_be_between_0_and_9999">Hours must be between 0 and 9999</string>
|
||||
<string name="minutes_must_be_between_0_and_59">Minutes must be between 0 and 59</string>
|
||||
<string name="seconds_must_be_between_0_and_59">Seconds must be between 0 and 59</string>
|
||||
@@ -1042,10 +1055,13 @@
|
||||
<string name="cubeb">cubeb</string>
|
||||
|
||||
<!-- Anisotropic filtering options -->
|
||||
<string name="multiplier_two">2x</string>
|
||||
<string name="multiplier_four">4x</string>
|
||||
<string name="multiplier_eight">8x</string>
|
||||
<string name="multiplier_sixteen">16x</string>
|
||||
<string name="multiplier_x2">x2</string>
|
||||
<string name="multiplier_x4">x4</string>
|
||||
<string name="multiplier_x8">x8</string>
|
||||
<string name="multiplier_x16">x16</string>
|
||||
<string name="multiplier_x32">x32</string>
|
||||
<string name="multiplier_x64">x64</string>
|
||||
<string name="multiplier_none">None</string>
|
||||
|
||||
<!-- Black backgrounds theme -->
|
||||
<string name="use_black_backgrounds">Black backgrounds</string>
|
||||
@@ -1109,7 +1125,7 @@
|
||||
|
||||
<!-- Applet Modes -->
|
||||
<string name="applets_menu">Applets</string>
|
||||
<string name="applets_menu_description">(WIP) Change applet frontends and settings</string>
|
||||
<string name="applets_menu_description">Change applet frontends and settings</string>
|
||||
|
||||
<string name="applet_hle">Custom Frontend</string>
|
||||
<string name="applet_lle">Real Applet</string>
|
||||
@@ -1119,6 +1135,9 @@
|
||||
<string name="airplane_mode">Airplane Mode</string>
|
||||
<string name="airplane_mode_description">Passes Airplane Mode to the Switch OS</string>
|
||||
|
||||
<string name="enable_overlay">Enable Overlay Applet</string>
|
||||
<string name="enable_overlay_description">Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 second to show it.</string>
|
||||
|
||||
<!-- Licenses screen strings -->
|
||||
<string name="licenses">Licenses</string>
|
||||
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>
|
||||
|
||||
8
src/android/app/src/main/res/xml/file_paths.xml
Normal file
8
src/android/app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- this is required to share files in the app's internal storage -->
|
||||
<cache-path name="apk_cache" path="." />
|
||||
<external-cache-path name="external_apk_cache" path="." />
|
||||
<files-path name="files" path="." />
|
||||
<external-files-path name="external_files" path="." />
|
||||
</paths>
|
||||
@@ -141,6 +141,10 @@ void LogSettings() {
|
||||
log_path("DataStorage_SDMCDir", Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir));
|
||||
}
|
||||
|
||||
bool getDebugKnobAt(u8 i) {
|
||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||
}
|
||||
|
||||
void UpdateGPUAccuracy() {
|
||||
values.current_gpu_accuracy = values.gpu_accuracy.GetValue();
|
||||
}
|
||||
|
||||
@@ -739,11 +739,22 @@ struct Values {
|
||||
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
|
||||
Setting<bool> disable_web_applet{linkage, true, "disable_web_applet", Category::Debugging};
|
||||
|
||||
SwitchableSetting<u16, true> debug_knobs{linkage,
|
||||
0,
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::Debugging,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
|
||||
// Miscellaneous
|
||||
Setting<std::string> serial_battery{linkage, std::string(), "serial_battery", Category::Miscellaneous};
|
||||
Setting<std::string> serial_unit{linkage, std::string(), "serial_unit", Category::Miscellaneous};
|
||||
Setting<std::string> log_filter{linkage, "*:Info", "log_filter", Category::Miscellaneous};
|
||||
Setting<bool> log_flush_line{linkage, false, "flush_line", Category::Miscellaneous, Specialization::Default, true, true};
|
||||
Setting<bool> censor_username{linkage, true, "censor_username", Category::Miscellaneous};
|
||||
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Miscellaneous};
|
||||
Setting<bool> first_launch{linkage, true, "first_launch", Category::Miscellaneous};
|
||||
|
||||
// Network
|
||||
@@ -763,10 +774,14 @@ struct Values {
|
||||
|
||||
// Per-game overrides
|
||||
bool use_squashed_iterated_blend;
|
||||
|
||||
Setting<bool> enable_overlay{linkage, true, "enable_overlay", Category::Core};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
bool getDebugKnobAt(u8 i);
|
||||
|
||||
void UpdateGPUAccuracy();
|
||||
bool IsGPULevelExtreme();
|
||||
bool IsGPULevelHigh();
|
||||
|
||||
@@ -126,7 +126,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
|
||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||
|
||||
@@ -129,7 +129,7 @@ protected:
|
||||
} else if constexpr (std::is_floating_point_v<Type>) {
|
||||
return fmt::format("{:f}", value_);
|
||||
} else if constexpr (std::is_enum_v<Type>) {
|
||||
return std::to_string(static_cast<u32>(value_));
|
||||
return std::to_string(u32(value_));
|
||||
} else {
|
||||
return std::to_string(value_);
|
||||
}
|
||||
@@ -192,15 +192,13 @@ public:
|
||||
if constexpr (std::is_same_v<Type, std::string>) {
|
||||
this->SetValue(input);
|
||||
} else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
|
||||
this->SetValue(static_cast<u32>(std::stoul(input)));
|
||||
this->SetValue(u32(std::stoul(input)));
|
||||
} else if constexpr (std::is_same_v<Type, bool>) {
|
||||
this->SetValue(input == "true");
|
||||
} else if constexpr (std::is_same_v<Type, float>) {
|
||||
this->SetValue(std::stof(input));
|
||||
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
|
||||
this->SetValue(ToEnum<AudioEngine>(input));
|
||||
} else {
|
||||
this->SetValue(static_cast<Type>(std::stoll(input)));
|
||||
this->SetValue(Type(std::stoll(input)));
|
||||
}
|
||||
} catch (std::invalid_argument&) {
|
||||
this->SetValue(this->GetDefault());
|
||||
|
||||
@@ -487,6 +487,10 @@ add_library(core STATIC
|
||||
hle/service/am/service/library_applet_self_accessor.h
|
||||
hle/service/am/service/lock_accessor.cpp
|
||||
hle/service/am/service/lock_accessor.h
|
||||
hle/service/am/service/overlay_functions.cpp
|
||||
hle/service/am/service/overlay_functions.h
|
||||
hle/service/am/service/overlay_applet_proxy.cpp
|
||||
hle/service/am/service/overlay_applet_proxy.h
|
||||
hle/service/am/service/process_winding_controller.cpp
|
||||
hle/service/am/service/process_winding_controller.h
|
||||
hle/service/am/service/self_controller.cpp
|
||||
@@ -1199,7 +1203,7 @@ else()
|
||||
target_link_libraries(core PUBLIC Boost::headers)
|
||||
endif()
|
||||
|
||||
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto${YUZU_STATIC_SUFFIX} MbedTLS::mbedtls${YUZU_STATIC_SUFFIX})
|
||||
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto${MBEDTLS_LIB_SUFFIX} MbedTLS::mbedtls${MBEDTLS_LIB_SUFFIX})
|
||||
# if (MINGW)
|
||||
# target_link_libraries(core PRIVATE ws2_32 mswsock wlanapi)
|
||||
# endif()
|
||||
|
||||
@@ -580,6 +580,21 @@ struct System::Impl {
|
||||
gpu_dirty_memory_managers;
|
||||
|
||||
std::deque<std::vector<u8>> user_channel;
|
||||
|
||||
std::mutex general_channel_mutex;
|
||||
std::deque<std::vector<u8>> general_channel;
|
||||
std::unique_ptr<Service::KernelHelpers::ServiceContext> general_channel_context; // lazy
|
||||
std::unique_ptr<Service::Event> general_channel_event; // lazy
|
||||
bool general_channel_initialized{false};
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (general_channel_initialized) {
|
||||
return;
|
||||
}
|
||||
general_channel_context = std::make_unique<Service::KernelHelpers::ServiceContext>(system, "GeneralChannel");
|
||||
general_channel_event = std::make_unique<Service::Event>(*general_channel_context);
|
||||
general_channel_initialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
System::System() : impl{std::make_unique<Impl>(*this)} {}
|
||||
@@ -993,6 +1008,39 @@ std::deque<std::vector<u8>>& System::GetUserChannel() {
|
||||
return impl->user_channel;
|
||||
}
|
||||
|
||||
std::deque<std::vector<u8>>& System::GetGeneralChannel() {
|
||||
return impl->general_channel;
|
||||
}
|
||||
|
||||
void System::PushGeneralChannelData(std::vector<u8>&& data) {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
impl->EnsureGeneralChannelInitialized(*this);
|
||||
const bool was_empty = impl->general_channel.empty();
|
||||
impl->general_channel.push_back(std::move(data));
|
||||
if (was_empty) {
|
||||
impl->general_channel_event->Signal();
|
||||
}
|
||||
}
|
||||
|
||||
bool System::TryPopGeneralChannel(std::vector<u8>& out_data) {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
if (!impl->general_channel_initialized || impl->general_channel.empty()) {
|
||||
return false;
|
||||
}
|
||||
out_data = std::move(impl->general_channel.back());
|
||||
impl->general_channel.pop_back();
|
||||
if (impl->general_channel.empty()) {
|
||||
impl->general_channel_event->Clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Service::Event& System::GetGeneralChannelEvent() {
|
||||
std::scoped_lock lk{impl->general_channel_mutex};
|
||||
impl->EnsureGeneralChannelInitialized(*this);
|
||||
return *impl->general_channel_event;
|
||||
}
|
||||
|
||||
void System::RegisterExitCallback(ExitCallback&& callback) {
|
||||
impl->exit_callback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
@@ -428,6 +430,11 @@ public:
|
||||
*/
|
||||
[[nodiscard]] std::deque<std::vector<u8>>& GetUserChannel();
|
||||
|
||||
[[nodiscard]] std::deque<std::vector<u8>>& GetGeneralChannel();
|
||||
void PushGeneralChannelData(std::vector<u8>&& data);
|
||||
bool TryPopGeneralChannel(std::vector<u8>& out_data);
|
||||
[[nodiscard]] Service::Event& GetGeneralChannelEvent();
|
||||
|
||||
/// Type used for the frontend to designate a callback for System to exit the application.
|
||||
using ExitCallback = std::function<void()>;
|
||||
|
||||
|
||||
@@ -640,32 +640,20 @@ KeyManager::KeyManager() {
|
||||
|
||||
void KeyManager::ReloadKeys() {
|
||||
// Initialize keys
|
||||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
if (!Common::FS::CreateDir(yuzu_keys_dir)) {
|
||||
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
if (!Common::FS::CreateDir(keys_dir))
|
||||
LOG_ERROR(Core, "Failed to create the keys directory.");
|
||||
}
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
dev_mode = true;
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "dev.keys", false);
|
||||
} else {
|
||||
dev_mode = false;
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "prod.keys", false);
|
||||
}
|
||||
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true);
|
||||
LoadFromFile(yuzu_keys_dir / "title.keys", true);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false);
|
||||
LoadFromFile(yuzu_keys_dir / "console.keys", false);
|
||||
LoadFromFile(keys_dir / "prod.keys_autogenerated", false);
|
||||
LoadFromFile(keys_dir / "prod.keys", false);
|
||||
LoadFromFile(keys_dir / "title.keys_autogenerated", true);
|
||||
LoadFromFile(keys_dir / "title.keys", true);
|
||||
LoadFromFile(keys_dir / "console.keys_autogenerated", false);
|
||||
LoadFromFile(keys_dir / "console.keys", false);
|
||||
}
|
||||
|
||||
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
|
||||
if (base.size() < begin + length) {
|
||||
if (base.size() < begin + length)
|
||||
return false;
|
||||
}
|
||||
return std::all_of(base.begin() + begin, base.begin() + begin + length,
|
||||
[](u8 c) { return std::isxdigit(c); });
|
||||
}
|
||||
@@ -850,9 +838,8 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
std::string filename = "title.keys_autogenerated";
|
||||
|
||||
if (category == KeyCategory::Standard) {
|
||||
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
|
||||
filename = "prod.keys_autogenerated";
|
||||
} else if (category == KeyCategory::Console) {
|
||||
filename = "console.keys_autogenerated";
|
||||
}
|
||||
@@ -947,17 +934,10 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
|
||||
}
|
||||
|
||||
bool KeyManager::KeyFileExists(bool title) {
|
||||
const auto yuzu_keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
|
||||
if (title) {
|
||||
return Common::FS::Exists(yuzu_keys_dir / "title.keys");
|
||||
}
|
||||
|
||||
if (Settings::values.use_dev_keys) {
|
||||
return Common::FS::Exists(yuzu_keys_dir / "dev.keys");
|
||||
}
|
||||
|
||||
return Common::FS::Exists(yuzu_keys_dir / "prod.keys");
|
||||
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
|
||||
if (title)
|
||||
return Common::FS::Exists(keys_dir / "title.keys");
|
||||
return Common::FS::Exists(keys_dir / "prod.keys");
|
||||
}
|
||||
|
||||
void KeyManager::DeriveSDSeedLazy() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -311,7 +314,6 @@ private:
|
||||
std::array<u8, 576> eticket_extended_kek{};
|
||||
RSAKeyPair<2048> eticket_rsa_keypair{};
|
||||
|
||||
bool dev_mode;
|
||||
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
|
||||
|
||||
template <size_t Size>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,6 +8,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
@@ -95,7 +95,7 @@ std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) {
|
||||
case SaveDataSpaceId::System:
|
||||
return "/system/";
|
||||
case SaveDataSpaceId::User:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
case SaveDataSpaceId::SdUser:
|
||||
return "/user/";
|
||||
case SaveDataSpaceId::Temporary:
|
||||
return "/temp/";
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace HLE::ApiVersion {
|
||||
|
||||
constexpr u8 HOS_VERSION_MAJOR = 21;
|
||||
constexpr u8 HOS_VERSION_MINOR = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 0;
|
||||
constexpr u8 HOS_VERSION_MICRO = 1;
|
||||
|
||||
// NintendoSDK version constants.
|
||||
|
||||
@@ -24,9 +24,9 @@ constexpr u8 SDK_REVISION_MAJOR = 1;
|
||||
constexpr u8 SDK_REVISION_MINOR = 0;
|
||||
|
||||
constexpr char PLATFORM_STRING[] = "NX";
|
||||
constexpr char VERSION_HASH[] = "f6b2425b6888a66590db104fc734891696e0ecb3";
|
||||
constexpr char DISPLAY_VERSION[] = "21.0.0";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 21.0.0-1.0";
|
||||
constexpr char VERSION_HASH[] = "066a75e6fab7316de34b88b60d229a0b2729e421";
|
||||
constexpr char DISPLAY_VERSION[] = "21.0.1";
|
||||
constexpr char DISPLAY_TITLE[] = "NintendoSDK Firmware for NX 21.0.1-1.0";
|
||||
|
||||
// Atmosphere version constants.
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
{220, nullptr, "SynchronizeProfileAsync"},
|
||||
{221, nullptr, "UploadProfileAsync"},
|
||||
{222, nullptr, "SynchronizaProfileAsyncIfSecondsElapsed"},
|
||||
{250, nullptr, "IsLinkedWithNintendoAccount"},
|
||||
{250, &IAdministrator::IsLinkedWithNintendoAccount, "IsLinkedWithNintendoAccount"},
|
||||
{251, nullptr, "CreateProcedureToLinkWithNintendoAccount"},
|
||||
{252, nullptr, "ResumeProcedureToLinkWithNintendoAccount"},
|
||||
{255, nullptr, "CreateProcedureToUpdateLinkageStateOfNintendoAccount"},
|
||||
@@ -236,6 +236,13 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void IsLinkedWithNintendoAccount(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(false);
|
||||
}
|
||||
};
|
||||
|
||||
class IAuthorizationRequest final : public ServiceFramework<IAuthorizationRequest> {
|
||||
@@ -496,6 +503,35 @@ protected:
|
||||
rb.Push(static_cast<u32>(buffer.size()));
|
||||
}
|
||||
|
||||
void LoadIdTokenCache(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> token_data(0x100);
|
||||
std::fill(token_data.begin(), token_data.end(), u8(0));
|
||||
|
||||
(void)ctx.WriteBuffer(token_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(static_cast<u32>(token_data.size()));
|
||||
}
|
||||
|
||||
void GetNintendoAccountUserResourceCacheForApplication(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> nas_user_base_for_application(0x68);
|
||||
(void)ctx.WriteBuffer(nas_user_base_for_application);
|
||||
|
||||
if (ctx.CanWriteBuffer(1)) {
|
||||
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
|
||||
(void)ctx.WriteBuffer(unknown_out_buffer, 1);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw<u64>(profile_manager.GetLastOpenedUser().Hash());
|
||||
}
|
||||
|
||||
void Store(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
@@ -707,7 +743,7 @@ private:
|
||||
std::vector<u8> token_data(0x100);
|
||||
std::fill(token_data.begin(), token_data.end(), u8(0));
|
||||
|
||||
ctx.WriteBuffer(token_data, 0);
|
||||
ctx.WriteBuffer(token_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -718,7 +754,7 @@ private:
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
|
||||
std::vector<u8> nas_user_base_for_application(0x68);
|
||||
ctx.WriteBuffer(nas_user_base_for_application, 0);
|
||||
ctx.WriteBuffer(nas_user_base_for_application);
|
||||
|
||||
if (ctx.CanWriteBuffer(1)) {
|
||||
std::vector<u8> unknown_out_buffer(ctx.GetWriteBufferSize(1));
|
||||
@@ -934,6 +970,7 @@ Result Module::Interface::InitializeApplicationInfoBase() {
|
||||
application_info.application_type = ApplicationType::GameCard;
|
||||
break;
|
||||
case FileSys::StorageId::Host:
|
||||
case FileSys::StorageId::NandSystem:
|
||||
case FileSys::StorageId::NandUser:
|
||||
case FileSys::StorageId::SdCard:
|
||||
case FileSys::StorageId::None: // Yuzu specific, differs from hardware
|
||||
@@ -1054,6 +1091,17 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IAdministrator>(system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
void DeleteUser(HLERequestContext& ctx);
|
||||
void SetUserPosition(HLERequestContext& ctx);
|
||||
void GetProfileEditor(HLERequestContext& ctx);
|
||||
void GetBaasAccountAdministrator(HLERequestContext &ctx);
|
||||
void ListQualifiedUsers(HLERequestContext& ctx);
|
||||
void ListOpenContextStoredUsers(HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
|
||||
|
||||
@@ -55,7 +55,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{211, nullptr, "CreateProcedureToRegisterUserWithNintendoAccount"},
|
||||
{212, nullptr, "ResumeProcedureToRegisterUserWithNintendoAccount"},
|
||||
{230, nullptr, "AuthenticateServiceAsync"},
|
||||
{250, nullptr, "GetBaasAccountAdministrator"},
|
||||
{250, &ACC_SU::GetBaasAccountAdministrator, "GetBaasAccountAdministrator"},
|
||||
{290, nullptr, "ProxyProcedureForGuestLoginWithNintendoAccount"},
|
||||
{291, nullptr, "ProxyProcedureForFloatingRegistrationWithNintendoAccount"},
|
||||
{299, nullptr, "SuspendBackgroundDaemon"},
|
||||
@@ -80,4 +80,4 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
|
||||
ACC_SU::~ACC_SU() = default;
|
||||
|
||||
} // namespace Service::Account
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,6 +18,7 @@ class FrontendApplet;
|
||||
enum class AppletType {
|
||||
Application,
|
||||
LibraryApplet,
|
||||
OverlayApplet,
|
||||
SystemApplet,
|
||||
};
|
||||
|
||||
@@ -146,6 +150,7 @@ enum class AppletMessage : u32 {
|
||||
ForceHideApplicationLogo = 57,
|
||||
FloatingApplicationDetected = 60,
|
||||
DetectShortPressingCaptureButton = 90,
|
||||
DetectLongPressingCaptureButton = 91,
|
||||
AlbumScreenShotTaken = 92,
|
||||
AlbumRecordingSaved = 93,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::AM {
|
||||
|
||||
Applet::Applet(Core::System& system, std::unique_ptr<Process> process_, bool is_application)
|
||||
: context(system, "Applet"), lifecycle_manager(system, context, is_application),
|
||||
process(std::move(process_)), hid_registration(system, *process),
|
||||
process(std::move(process_)), hid_registration(system, *process), overlay_event(context),
|
||||
gpu_error_detected_event(context), friend_invitation_storage_channel_event(context),
|
||||
notification_storage_channel_event(context), health_warning_disappeared_system_event(context),
|
||||
unknown_event(context), acquired_sleep_lock_event(context), pop_from_general_channel_event(context),
|
||||
@@ -63,7 +63,15 @@ void Applet::SetInteractibleLocked(bool interactible) {
|
||||
|
||||
is_interactible = interactible;
|
||||
|
||||
hid_registration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested());
|
||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||
const bool input_enabled = interactible && !exit_requested;
|
||||
|
||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
||||
}
|
||||
|
||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
|
||||
@@ -122,8 +122,10 @@ struct Applet {
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_in_foreground{false};
|
||||
|
||||
// Events
|
||||
Event overlay_event;
|
||||
Event gpu_error_detected_event;
|
||||
Event friend_invitation_storage_channel_event;
|
||||
Event notification_storage_channel_event;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -15,6 +18,7 @@
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include "core/hle/service/am/process_creation.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -262,6 +266,22 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
|
||||
|
||||
m_cv.wait(lk, [&] { return m_pending_process != nullptr; });
|
||||
|
||||
if (Settings::values.enable_overlay) {
|
||||
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
|
||||
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false);
|
||||
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
|
||||
overlay_applet->applet_id = AppletId::OverlayDisplay;
|
||||
overlay_applet->type = AppletType::OverlayApplet;
|
||||
overlay_applet->library_applet_mode = LibraryAppletMode::PartialForeground;
|
||||
overlay_applet->window_visible = true;
|
||||
overlay_applet->home_button_short_pressed_blocked = false;
|
||||
overlay_applet->home_button_long_pressed_blocked = false;
|
||||
m_window_system->TrackApplet(overlay_applet, false);
|
||||
overlay_applet->process->Run();
|
||||
LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially hidden, watching home button)");
|
||||
}
|
||||
}
|
||||
|
||||
const auto& params = m_pending_parameters;
|
||||
auto applet = std::make_shared<Applet>(m_system, std::move(m_pending_process),
|
||||
params.applet_id == AppletId::Application);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -46,6 +49,7 @@ public:
|
||||
|
||||
public:
|
||||
void SetWindowSystem(WindowSystem* window_system);
|
||||
[[nodiscard]] WindowSystem* GetWindowSystem() const { return m_window_system; }
|
||||
|
||||
private:
|
||||
Core::System& m_system;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
#include "core/hle/service/am/button_poller.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
@@ -19,9 +23,9 @@ ButtonPressDuration ClassifyPressDuration(std::chrono::steady_clock::time_point
|
||||
|
||||
// TODO: determine actual thresholds
|
||||
// TODO: these are likely different for each button
|
||||
if (dur < 500ms) {
|
||||
if (dur < 400ms) {
|
||||
return ButtonPressDuration::ShortPressing;
|
||||
} else if (dur < 1000ms) {
|
||||
} else if (dur < 800ms) {
|
||||
return ButtonPressDuration::MiddlePressing;
|
||||
} else {
|
||||
return ButtonPressDuration::LongPressing;
|
||||
@@ -47,14 +51,22 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system)
|
||||
m_handheld_key = m_handheld->SetCallback(engine_callback);
|
||||
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||
m_player1_key = m_player1->SetCallback(engine_callback);
|
||||
|
||||
m_thread = std::thread([this] { this->ThreadLoop(); });
|
||||
}
|
||||
|
||||
ButtonPoller::~ButtonPoller() {
|
||||
m_handheld->DeleteCallback(m_handheld_key);
|
||||
m_player1->DeleteCallback(m_player1_key);
|
||||
m_stop = true;
|
||||
m_cv.notify_all();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonPoller::OnButtonStateChanged() {
|
||||
std::lock_guard lk{m_mutex};
|
||||
const bool home_button =
|
||||
m_handheld->GetHomeButtons().home.Value() || m_player1->GetHomeButtons().home.Value();
|
||||
const bool capture_button = m_handheld->GetCaptureButtons().capture.Value() ||
|
||||
@@ -63,22 +75,54 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// Buttons pressed which were not previously pressed
|
||||
if (home_button && !m_home_button_press_start) {
|
||||
m_home_button_press_start = std::chrono::steady_clock::now();
|
||||
m_home_button_long_sent = false;
|
||||
}
|
||||
if (capture_button && !m_capture_button_press_start) {
|
||||
m_capture_button_press_start = std::chrono::steady_clock::now();
|
||||
m_capture_button_long_sent = false;
|
||||
}
|
||||
// if (power_button && !m_power_button_press_start) {
|
||||
// m_power_button_press_start = std::chrono::steady_clock::now();
|
||||
// }
|
||||
|
||||
// While buttons are held, check whether they have crossed the long-press
|
||||
// threshold and, if so, send the long-press action immediately (only once).
|
||||
if (home_button && m_home_button_press_start && !m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
m_home_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (capture_button && m_capture_button_press_start && !m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
if (duration != ButtonPressDuration::ShortPressing) {
|
||||
m_window_system.OnSystemButtonPress(SystemButtonType::CaptureButtonLongPressing);
|
||||
m_capture_button_long_sent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons released which were previously held
|
||||
if (!home_button && m_home_button_press_start) {
|
||||
m_window_system.OnHomeButtonPressed(ClassifyPressDuration(*m_home_button_press_start));
|
||||
if(!m_home_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_home_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::HomeButtonShortPressing
|
||||
: SystemButtonType::HomeButtonLongPressing);
|
||||
}
|
||||
m_home_button_press_start = std::nullopt;
|
||||
m_home_button_long_sent = false;
|
||||
}
|
||||
if (!capture_button && m_capture_button_press_start) {
|
||||
// TODO
|
||||
if (!m_capture_button_long_sent) {
|
||||
const auto duration = ClassifyPressDuration(*m_capture_button_press_start);
|
||||
m_window_system.OnSystemButtonPress(
|
||||
duration == ButtonPressDuration::ShortPressing ? SystemButtonType::CaptureButtonShortPressing
|
||||
: SystemButtonType::CaptureButtonLongPressing);
|
||||
}
|
||||
m_capture_button_press_start = std::nullopt;
|
||||
m_capture_button_long_sent = false;
|
||||
}
|
||||
// if (!power_button && m_power_button_press_start) {
|
||||
// // TODO
|
||||
@@ -86,4 +130,16 @@ void ButtonPoller::OnButtonStateChanged() {
|
||||
// }
|
||||
}
|
||||
|
||||
void ButtonPoller::ThreadLoop() {
|
||||
using namespace std::chrono_literals;
|
||||
std::unique_lock lk{m_mutex};
|
||||
while (!m_stop) {
|
||||
m_cv.wait_for(lk, 50ms);
|
||||
if (m_stop) break;
|
||||
lk.unlock();
|
||||
OnButtonStateChanged();
|
||||
lk.lock();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -26,6 +33,7 @@ public:
|
||||
|
||||
private:
|
||||
void OnButtonStateChanged();
|
||||
void ThreadLoop();
|
||||
|
||||
private:
|
||||
WindowSystem& m_window_system;
|
||||
@@ -38,6 +46,15 @@ private:
|
||||
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
|
||||
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
|
||||
|
||||
bool m_home_button_long_sent{};
|
||||
bool m_capture_button_long_sent{};
|
||||
bool m_power_button_long_sent{};
|
||||
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::condition_variable m_cv;
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -33,6 +36,10 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
m_buffer_sharing_enabled = false;
|
||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)this->IsSystemBufferSharingEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayLayerManager::Finalize() {
|
||||
@@ -69,6 +76,18 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
static constexpr s32 kOverlayBackgroundZ = -1;
|
||||
(void)m_manager_display_service->SetLayerZIndex(kOverlayBackgroundZ, *out_layer_id);
|
||||
} else {
|
||||
static constexpr s32 kOverlayZ = 3;
|
||||
(void)m_manager_display_service->SetLayerZIndex(kOverlayZ, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
m_managed_display_layers.emplace(*out_layer_id);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -105,7 +124,15 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// We succeeded, so set up remaining state.
|
||||
m_buffer_sharing_enabled = true;
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = -1;
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -128,10 +155,14 @@ void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
|
||||
if (m_manager_display_service) {
|
||||
if (m_system_shared_layer_id) {
|
||||
LOG_INFO(Service_VI, "shared_layer={} visible={} applet_id={}",
|
||||
m_system_shared_layer_id, m_visible, static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
LOG_INFO(Service_VI, "managed_layer={} visible={} applet_id={}",
|
||||
layer_id, m_visible, static_cast<u32>(m_applet_id));
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +172,22 @@ bool DisplayLayerManager::GetWindowVisibility() const {
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
if (!m_manager_display_service) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_system_shared_layer_id) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, m_system_shared_layer_id);
|
||||
LOG_INFO(Service_VI, "called, shared_layer={} z={}", m_system_shared_layer_id, z_index);
|
||||
}
|
||||
|
||||
for (const auto layer_id : m_managed_display_layers) {
|
||||
m_manager_display_service->SetLayerZIndex(z_index, layer_id);
|
||||
LOG_INFO(Service_VI, "called, managed_layer={} z={}", layer_id, z_index);
|
||||
}
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -43,6 +46,8 @@ public:
|
||||
void SetWindowVisibility(bool visible);
|
||||
bool GetWindowVisibility() const;
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,6 +10,7 @@
|
||||
#include "core/hle/service/am/service/application_proxy.h"
|
||||
#include "core/hle/service/am/service/library_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/system_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
@@ -21,7 +25,7 @@ IAllSystemAppletProxiesService::IAllSystemAppletProxiesService(Core::System& sys
|
||||
{110, D<&IAllSystemAppletProxiesService::OpenSystemAppletProxy>, "OpenSystemAppletProxyEx"},
|
||||
{200, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxyOld>, "OpenLibraryAppletProxyOld"},
|
||||
{201, D<&IAllSystemAppletProxiesService::OpenLibraryAppletProxy>, "OpenLibraryAppletProxy"},
|
||||
{300, nullptr, "OpenOverlayAppletProxy"},
|
||||
{300, D<&IAllSystemAppletProxiesService::OpenOverlayAppletProxy>, "OpenOverlayAppletProxy"},
|
||||
{350, D<&IAllSystemAppletProxiesService::OpenSystemApplicationProxy>, "OpenSystemApplicationProxy"},
|
||||
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
|
||||
{410, nullptr, "GetSystemAppletControllerForDebug"},
|
||||
@@ -67,6 +71,22 @@ Result IAllSystemAppletProxiesService::OpenLibraryAppletProxy(
|
||||
}
|
||||
}
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenOverlayAppletProxy(
|
||||
Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute) {
|
||||
LOG_WARNING(Service_AM, "called");
|
||||
|
||||
if (const auto applet = this->GetAppletFromProcessId(pid); applet) {
|
||||
*out_overlay_applet_proxy = std::make_shared<IOverlayAppletProxy>(
|
||||
system, applet, process_handle.Get(), m_window_system);
|
||||
R_SUCCEED();
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
R_THROW(ResultUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
Result IAllSystemAppletProxiesService::OpenSystemApplicationProxy(
|
||||
Out<SharedPointer<IApplicationProxy>> out_system_application_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,6 +8,7 @@
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
|
||||
namespace Service {
|
||||
|
||||
@@ -31,6 +35,9 @@ private:
|
||||
ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenOverlayAppletProxy(Out<SharedPointer<IOverlayAppletProxy>> out_overlay_applet_proxy,
|
||||
ClientProcessId pid, InCopyHandle<Kernel::KProcess> process_handle,
|
||||
InLargeData<AppletAttribute, BufferAttr_HipcMapAlias> attribute);
|
||||
Result OpenLibraryAppletProxyOld(
|
||||
Out<SharedPointer<ILibraryAppletProxy>> out_library_applet_proxy, ClientProcessId pid,
|
||||
InCopyHandle<Kernel::KProcess> process_handle);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@ IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
|
||||
{20, nullptr, "PushToAppletBoundChannel"},
|
||||
{21, nullptr, "TryPopFromAppletBoundChannel"},
|
||||
{40, nullptr, "GetDisplayLogicalResolution"},
|
||||
{42, nullptr, "SetDisplayMagnification"},
|
||||
{42, D<&IAppletCommonFunctions::SetDisplayMagnification>, "SetDisplayMagnification"},
|
||||
{50, D<&IAppletCommonFunctions::SetHomeButtonDoubleClickEnabled>, "SetHomeButtonDoubleClickEnabled"},
|
||||
{51, D<&IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled>, "GetHomeButtonDoubleClickEnabled"},
|
||||
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
|
||||
@@ -58,6 +61,14 @@ Result IAppletCommonFunctions::GetHomeButtonDoubleClickEnabled(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height) {
|
||||
LOG_DEBUG(Service_AM, "(STUBBED) called, x={}, y={}, width={}, height={}", x, y, width,
|
||||
height);
|
||||
std::scoped_lock lk{applet->lock};
|
||||
applet->display_magnification = Common::Rectangle<f32>{x, y, x + width, y + height};
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
std::scoped_lock lk{applet->lock};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,6 +21,7 @@ public:
|
||||
private:
|
||||
Result SetHomeButtonDoubleClickEnabled(bool home_button_double_click_enabled);
|
||||
Result GetHomeButtonDoubleClickEnabled(Out<bool> out_home_button_double_click_enabled);
|
||||
Result SetDisplayMagnification(f32 x, f32 y, f32 width, f32 height);
|
||||
Result SetCpuBoostRequestPriority(s32 priority);
|
||||
Result GetCurrentApplicationId(Out<u64> out_application_id);
|
||||
Result Unknown350(Out<u16> out_unknown);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -56,7 +59,7 @@ IApplicationCreator::IApplicationCreator(Core::System& system_, WindowSystem& wi
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationCreator::CreateApplication>, "CreateApplication"},
|
||||
{1, nullptr, "PopLaunchRequestedApplication"},
|
||||
{10, nullptr, "CreateSystemApplication"},
|
||||
{10, D<&IApplicationCreator::CreateSystemApplication>, "CreateSystemApplication"},
|
||||
{100, nullptr, "PopFloatingApplicationForDevelopment"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -73,4 +76,34 @@ Result IApplicationCreator::CreateApplication(
|
||||
CreateGuestApplication(out_application_accessor, system, m_window_system, application_id));
|
||||
}
|
||||
|
||||
Result IApplicationCreator::CreateSystemApplication(
|
||||
Out<SharedPointer<IApplicationAccessor>> out_application_accessor, u64 application_id) {
|
||||
|
||||
FileSys::VirtualFile nca_raw{};
|
||||
|
||||
auto& storage = system.GetContentProviderUnion();
|
||||
nca_raw = storage.GetEntryRaw(application_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
R_UNLESS(nca_raw != nullptr, ResultUnknown);
|
||||
|
||||
std::vector<u8> control;
|
||||
std::unique_ptr<Loader::AppLoader> loader;
|
||||
|
||||
auto process =
|
||||
CreateProcess(system, application_id, 1, 21);
|
||||
R_UNLESS(process != nullptr, ResultUnknown);
|
||||
|
||||
const auto applet = std::make_shared<Applet>(system, std::move(process), true);
|
||||
applet->program_id = application_id;
|
||||
applet->applet_id = AppletId::Starter;
|
||||
applet->type = AppletType::LibraryApplet;
|
||||
applet->library_applet_mode = LibraryAppletMode::AllForeground;
|
||||
|
||||
m_window_system.TrackApplet(applet, true);
|
||||
|
||||
*out_application_accessor =
|
||||
std::make_shared<IApplicationAccessor>(system, applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,6 +22,7 @@ public:
|
||||
|
||||
private:
|
||||
Result CreateApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
|
||||
Result CreateSystemApplication(Out<SharedPointer<IApplicationAccessor>>, u64 application_id);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
};
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/common_state_getter.h"
|
||||
#include "core/hle/service/am/service/lock_accessor.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/apm/apm_interface.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/pm/pm.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/vi.h"
|
||||
#include "core/hle/service/vi/vi_types.h"
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -37,7 +37,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{12, D<&ICommonStateGetter::ReleaseSleepLockTransiently>, "ReleaseSleepLockTransiently"},
|
||||
{13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"},
|
||||
{14, nullptr, "GetWakeupCount"},
|
||||
{20, nullptr, "PushToGeneralChannel"},
|
||||
{20, D<&ICommonStateGetter::PushToGeneralChannel>, "PushToGeneralChannel"},
|
||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||
{31, D<&ICommonStateGetter::GetReaderLockAccessorEx>, "GetReaderLockAccessorEx"},
|
||||
{32, D<&ICommonStateGetter::GetWriterLockAccessorEx>, "GetWriterLockAccessorEx"},
|
||||
@@ -61,7 +61,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
|
||||
{80, D<&ICommonStateGetter::PerformSystemButtonPressingIfInFocus>, "PerformSystemButtonPressingIfInFocus"},
|
||||
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
|
||||
{91, nullptr, "GetCurrentPerformanceConfiguration"},
|
||||
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{100, D<&ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{110, nullptr, "OpenMyGpuErrorHandler"},
|
||||
{120, D<&ICommonStateGetter::GetAppletLaunchedHistory>, "GetAppletLaunchedHistory"},
|
||||
{200, D<&ICommonStateGetter::GetOperationModeSystemInfo>, "GetOperationModeSystemInfo"},
|
||||
@@ -95,6 +95,9 @@ Result ICommonStateGetter::ReceiveMessage(Out<AppletMessage> out_applet_message)
|
||||
R_THROW(AM::ResultNoMessages);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, returning message={} to applet_id={}",
|
||||
static_cast<u32>(*out_applet_message), static_cast<u32>(m_applet->applet_id));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -262,7 +265,40 @@ Result ICommonStateGetter::GetBuiltInDisplayType(Out<s32> out_display_type) {
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::PerformSystemButtonPressingIfInFocus(SystemButtonType type) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, type={}", type);
|
||||
LOG_DEBUG(Service_AM, "called, type={}", type);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
|
||||
switch (type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
if (!m_applet->home_button_short_pressed_blocked) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingHomeButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing:
|
||||
if (!m_applet->home_button_long_pressed_blocked) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectLongPressingHomeButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
if (m_applet->handling_capture_button_short_pressed_message_enabled_for_applet) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingCaptureButton);
|
||||
}
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonLongPressing:
|
||||
if (m_applet->handling_capture_button_long_pressed_message_enabled_for_applet) {
|
||||
m_applet->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectLongPressingCaptureButton);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Other buttons ignored for now
|
||||
break;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -304,4 +340,18 @@ Result ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnab
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::PushToGeneralChannel(SharedPointer<IStorage> storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
system.PushGeneralChannelData(storage->GetData());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ICommonStateGetter::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={} applet_id={}", enabled, m_applet->applet_id);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->home_button_short_pressed_blocked = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class ILockAccessor;
|
||||
class IStorage;
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
public:
|
||||
@@ -60,6 +61,8 @@ private:
|
||||
OutArray<AppletId, BufferAttr_HipcMapAlias> out_applet_ids);
|
||||
Result GetSettingsPlatformRegion(Out<Set::PlatformRegion> out_settings_platform_region);
|
||||
Result SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled();
|
||||
Result PushToGeneralChannel(SharedPointer<IStorage> storage); // cmd 20
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
|
||||
void SetCpuBoostMode(HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,20 +9,21 @@
|
||||
#include "core/hle/service/am/service/home_menu_functions.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
WindowSystem& window_system)
|
||||
: ServiceFramework{system_, "IHomeMenuFunctions"}, m_window_system{window_system},
|
||||
m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"},
|
||||
m_pop_from_general_channel_event{m_context} {
|
||||
m_applet{std::move(applet)}, m_context{system, "IHomeMenuFunctions"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10, D<&IHomeMenuFunctions::RequestToGetForeground>, "RequestToGetForeground"},
|
||||
{11, D<&IHomeMenuFunctions::LockForeground>, "LockForeground"},
|
||||
{12, D<&IHomeMenuFunctions::UnlockForeground>, "UnlockForeground"},
|
||||
{20, nullptr, "PopFromGeneralChannel"},
|
||||
{20, D<&IHomeMenuFunctions::PopFromGeneralChannel>, "PopFromGeneralChannel"},
|
||||
{21, D<&IHomeMenuFunctions::GetPopFromGeneralChannelEvent>, "GetPopFromGeneralChannelEvent"},
|
||||
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
|
||||
{31, nullptr, "GetWriterLockAccessorEx"},
|
||||
@@ -57,10 +61,22 @@ Result IHomeMenuFunctions::UnlockForeground() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::PopFromGeneralChannel(Out<SharedPointer<IStorage>> out_storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
std::vector<u8> data;
|
||||
if (!system.TryPopGeneralChannel(data)) {
|
||||
R_THROW(AM::ResultNoDataInChannel);
|
||||
}
|
||||
|
||||
*out_storage = std::make_shared<IStorage>(system, std::move(data));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IHomeMenuFunctions::GetPopFromGeneralChannelEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_INFO(Service_AM, "called");
|
||||
*out_event = m_pop_from_general_channel_event.GetHandle();
|
||||
*out_event = system.GetGeneralChannelEvent().GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,6 +15,7 @@ namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class WindowSystem;
|
||||
class IStorage;
|
||||
|
||||
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
|
||||
public:
|
||||
@@ -23,6 +27,7 @@ private:
|
||||
Result RequestToGetForeground();
|
||||
Result LockForeground();
|
||||
Result UnlockForeground();
|
||||
Result PopFromGeneralChannel(Out<SharedPointer<IStorage>> out_storage);
|
||||
Result GetPopFromGeneralChannelEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||
Result IsRebootEnabled(Out<bool> out_is_reboot_enbaled);
|
||||
Result IsForceTerminateApplicationDisabledForDebug(
|
||||
@@ -31,7 +36,6 @@ private:
|
||||
WindowSystem& m_window_system;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
KernelHelpers::ServiceContext m_context;
|
||||
Event m_pop_from_general_channel_event;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
120
src/core/hle/service/am/service/overlay_applet_proxy.cpp
Normal file
120
src/core/hle/service/am/service/overlay_applet_proxy.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/service/common_state_getter.h"
|
||||
#include "core/hle/service/am/service/display_controller.h"
|
||||
#include "core/hle/service/am/service/global_state_controller.h"
|
||||
#include "core/hle/service/am/service/audio_controller.h"
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/am/service/overlay_applet_proxy.h"
|
||||
#include "core/hle/service/am/service/library_applet_creator.h"
|
||||
#include "core/hle/service/am/service/process_winding_controller.h"
|
||||
#include "core/hle/service/am/service/self_controller.h"
|
||||
#include "core/hle/service/am/service/window_controller.h"
|
||||
#include "core/hle/service/am/service/debug_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
|
||||
namespace Service::AM {
|
||||
IOverlayAppletProxy::IOverlayAppletProxy(Core::System &system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess *process, WindowSystem &window_system)
|
||||
: ServiceFramework{system_, "IOverlayAppletProxy"},
|
||||
m_window_system{window_system}, m_process{process}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayAppletProxy::GetCommonStateGetter>, "GetCommonStateGetter"},
|
||||
{1, D<&IOverlayAppletProxy::GetSelfController>, "GetSelfController"},
|
||||
{2, D<&IOverlayAppletProxy::GetWindowController>, "GetWindowController"},
|
||||
{3, D<&IOverlayAppletProxy::GetAudioController>, "GetAudioController"},
|
||||
{4, D<&IOverlayAppletProxy::GetDisplayController>, "GetDisplayController"},
|
||||
{10, D<&IOverlayAppletProxy::GetProcessWindingController>, "GetProcessWindingController"},
|
||||
{11, D<&IOverlayAppletProxy::GetLibraryAppletCreator>, "GetLibraryAppletCreator"},
|
||||
{20, D<&IOverlayAppletProxy::GetOverlayFunctions>, "GetOverlayFunctions"},
|
||||
{21, D<&IOverlayAppletProxy::GetAppletCommonFunctions>, "GetAppletCommonFunctions"},
|
||||
{23, D<&IOverlayAppletProxy::GetGlobalStateController>, "GetGlobalStateController"},
|
||||
{1000, D<&IOverlayAppletProxy::GetDebugFunctions>, "GetDebugFunctions"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IOverlayAppletProxy::~IOverlayAppletProxy() = default;
|
||||
|
||||
Result IOverlayAppletProxy::GetCommonStateGetter(
|
||||
Out<SharedPointer<ICommonStateGetter> > out_common_state_getter) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_common_state_getter = std::make_shared<ICommonStateGetter>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetSelfController(
|
||||
Out<SharedPointer<ISelfController> > out_self_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_self_controller = std::make_shared<ISelfController>(system, m_applet, m_process);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetWindowController(
|
||||
Out<SharedPointer<IWindowController> > out_window_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_window_controller = std::make_shared<IWindowController>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetAudioController(
|
||||
Out<SharedPointer<IAudioController> > out_audio_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_audio_controller = std::make_shared<IAudioController>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetDisplayController(
|
||||
Out<SharedPointer<IDisplayController> > out_display_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_display_controller = std::make_shared<IDisplayController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController> > out_process_winding_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_process_winding_controller = std::make_shared<IProcessWindingController>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator> > out_library_applet_creator) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_library_applet_creator =
|
||||
std::make_shared<ILibraryAppletCreator>(system, m_applet, m_window_system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetOverlayFunctions(
|
||||
Out<SharedPointer<IOverlayFunctions> > out_overlay_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_overlay_functions = std::make_shared<IOverlayFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions> > out_applet_common_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_applet_common_functions = std::make_shared<IAppletCommonFunctions>(system, m_applet);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController> > out_global_state_controller) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_global_state_controller = std::make_shared<IGlobalStateController>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayAppletProxy::GetDebugFunctions(
|
||||
Out<SharedPointer<IDebugFunctions> > out_debug_functions) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
*out_debug_functions = std::make_shared<IDebugFunctions>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::AM
|
||||
55
src/core/hle/service/am/service/overlay_applet_proxy.h
Normal file
55
src/core/hle/service/am/service/overlay_applet_proxy.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
struct Applet;
|
||||
class IAppletCommonFunctions;
|
||||
class IAudioController;
|
||||
class ICommonStateGetter;
|
||||
class IDebugFunctions;
|
||||
class IDisplayController;
|
||||
class IHomeMenuFunctions;
|
||||
class IGlobalStateController;
|
||||
class ILibraryAppletCreator;
|
||||
class ILibraryAppletSelfAccessor;
|
||||
class IProcessWindingController;
|
||||
class ISelfController;
|
||||
class IWindowController;
|
||||
class WindowSystem;
|
||||
|
||||
class IOverlayAppletProxy final : public ServiceFramework<IOverlayAppletProxy> {
|
||||
public:
|
||||
explicit IOverlayAppletProxy(Core::System& system_, std::shared_ptr<Applet> applet,
|
||||
Kernel::KProcess* process, WindowSystem& window_system);
|
||||
~IOverlayAppletProxy();
|
||||
|
||||
private:
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
|
||||
Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
|
||||
Result GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetOverlayFunctions(Out<SharedPointer<IOverlayFunctions>> out_overlay_functions);
|
||||
Result GetAppletCommonFunctions(
|
||||
Out<SharedPointer<IAppletCommonFunctions>> out_applet_common_functions);
|
||||
Result GetGlobalStateController(
|
||||
Out<SharedPointer<IGlobalStateController>> out_global_state_controller);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
|
||||
WindowSystem& m_window_system;
|
||||
Kernel::KProcess* const m_process;
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
100
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
100
src/core/hle/service/am/service/overlay_functions.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/overlay_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
|
||||
namespace Service::AM {
|
||||
IOverlayFunctions::IOverlayFunctions(Core::System &system_, std::shared_ptr<Applet> applet)
|
||||
: ServiceFramework{system_, "IOverlayFunctions"}, m_applet{std::move(applet)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IOverlayFunctions::BeginToWatchShortHomeButtonMessage>, "BeginToWatchShortHomeButtonMessage"},
|
||||
{1, D<&IOverlayFunctions::EndToWatchShortHomeButtonMessage>, "EndToWatchShortHomeButtonMessage"},
|
||||
{2, D<&IOverlayFunctions::GetApplicationIdForLogo>, "GetApplicationIdForLogo"},
|
||||
{3, nullptr, "SetGpuTimeSliceBoost"},
|
||||
{4, D<&IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled>, "SetAutoSleepTimeAndDimmingTimeEnabled"},
|
||||
{5, nullptr, "TerminateApplicationAndSetReason"},
|
||||
{6, nullptr, "SetScreenShotPermissionGlobally"},
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, nullptr, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
{41, nullptr, "GetApplicationStartupMovie"},
|
||||
{50, nullptr, "SetGpuTimeSliceBoostForApplication"},
|
||||
{60, nullptr, "Unknown60"},
|
||||
{70, D<&IOverlayFunctions::Unknown70>, "Unknown70"},
|
||||
{90, nullptr, "SetRequiresGpuResourceUse"},
|
||||
{101, nullptr, "BeginToObserveHidInputForDevelop"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
IOverlayFunctions::~IOverlayFunctions() = default;
|
||||
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
// Prefer explicit application_id if available, else fall back to program_id
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
u64 id = m_applet->screen_shot_identity.application_id;
|
||||
if (id == 0) {
|
||||
id = m_applet->program_id;
|
||||
}
|
||||
*out_application_id = id;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->auto_sleep_disabled = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->home_button_short_pressed_blocked = !enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::Unknown70() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::AM
|
||||
27
src/core/hle/service/am/service/overlay_functions.h
Normal file
27
src/core/hle/service/am/service/overlay_functions.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::AM {
|
||||
struct Applet;
|
||||
|
||||
class IOverlayFunctions final : public ServiceFramework<IOverlayFunctions> {
|
||||
public:
|
||||
explicit IOverlayFunctions(Core::System &system_, std::shared_ptr<Applet> applet);
|
||||
~IOverlayFunctions() override;
|
||||
|
||||
private:
|
||||
Result BeginToWatchShortHomeButtonMessage();
|
||||
Result EndToWatchShortHomeButtonMessage();
|
||||
Result GetApplicationIdForLogo(Out<u64> out_application_id);
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Applet> m_applet;
|
||||
};
|
||||
} // namespace Service::AM
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -30,14 +33,14 @@ public:
|
||||
~ISystemAppletProxy();
|
||||
|
||||
private:
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller);
|
||||
Result GetDisplayController(Out<SharedPointer<IDisplayController>> out_display_controller);
|
||||
Result GetProcessWindingController(
|
||||
Out<SharedPointer<IProcessWindingController>> out_process_winding_controller);
|
||||
Result GetDebugFunctions(Out<SharedPointer<IDebugFunctions>> out_debug_functions);
|
||||
Result GetWindowController(Out<SharedPointer<IWindowController>> out_window_controller);
|
||||
Result GetSelfController(Out<SharedPointer<ISelfController>> out_self_controller);
|
||||
Result GetCommonStateGetter(Out<SharedPointer<ICommonStateGetter>> out_common_state_getter);
|
||||
Result GetLibraryAppletCreator(
|
||||
Out<SharedPointer<ILibraryAppletCreator>> out_library_applet_creator);
|
||||
Result GetApplicationCreator(Out<SharedPointer<IApplicationCreator>> out_application_creator);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -21,9 +24,18 @@ void WindowSystem::SetEventObserver(EventObserver* observer) {
|
||||
m_system.GetAppletManager().SetWindowSystem(this);
|
||||
}
|
||||
|
||||
void WindowSystem::RequestUpdate() {
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
@@ -32,9 +44,16 @@ void WindowSystem::Update() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool overlay_blocks_input = false;
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
||||
}
|
||||
|
||||
// Recursively update each applet root.
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application);
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
||||
}
|
||||
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
@@ -43,6 +62,8 @@ void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_applicati
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
@@ -136,21 +157,66 @@ void WindowSystem::OnExitRequested() {
|
||||
}
|
||||
}
|
||||
|
||||
void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
// If we don't have a home menu, nothing to do.
|
||||
if (!m_home_menu) {
|
||||
return;
|
||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||
if (m_home_menu) {
|
||||
std::scoped_lock lk_home{m_home_menu->lock};
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_application) {
|
||||
std::scoped_lock lk_application{m_application->lock};
|
||||
m_application->lifecycle_manager.PushUnorderedMessage(message);
|
||||
}
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// Lock.
|
||||
std::scoped_lock lk2{m_home_menu->lock};
|
||||
void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
switch (type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing: {
|
||||
// Toggle overlay foreground visibility on long home press
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
||||
// Tie window visibility to foreground state so hidden when not active
|
||||
m_overlay_display->window_visible = m_overlay_display->overlay_in_foreground;
|
||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
||||
}
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||
// Force a state update after toggling overlay
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
break; }
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonLongPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingCaptureButton);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send home button press event to home menu.
|
||||
if (type == ButtonPressDuration::ShortPressing) {
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(
|
||||
AppletMessage::DetectShortPressingHomeButton);
|
||||
void WindowSystem::OnHomeButtonPressed(ButtonPressDuration type) {
|
||||
// Map duration to SystemButtonType for legacy callers
|
||||
switch (type) {
|
||||
case ButtonPressDuration::ShortPressing:
|
||||
OnSystemButtonPress(SystemButtonType::HomeButtonShortPressing);
|
||||
break;
|
||||
case ButtonPressDuration::MiddlePressing:
|
||||
case ButtonPressDuration::LongPressing:
|
||||
OnSystemButtonPress(SystemButtonType::HomeButtonLongPressing);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +274,10 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
if (applet.get() == m_overlay_display) {
|
||||
m_overlay_display = nullptr;
|
||||
}
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
@@ -258,7 +328,7 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
@@ -287,10 +357,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
}();
|
||||
|
||||
// Update visibility state.
|
||||
applet->display_layer_manager.SetWindowVisibility(is_foreground && applet->window_visible);
|
||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->window_visible
|
||||
: (is_foreground && applet->window_visible);
|
||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||
|
||||
// Update interactibility state.
|
||||
applet->SetInteractibleLocked(is_foreground && applet->window_visible);
|
||||
|
||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->overlay_in_foreground
|
||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
||||
|
||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
||||
}
|
||||
|
||||
applet->SetInteractibleLocked(should_be_interactible);
|
||||
|
||||
// Update focus state and suspension.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
@@ -306,9 +389,23 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground) {
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
||||
s32 z_index = 0;
|
||||
const bool now_foreground = inherited_foreground;
|
||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
z_index = applet->overlay_in_foreground ? 100000 : -100000;
|
||||
} else if (now_foreground && !is_obscured) {
|
||||
z_index = 2;
|
||||
} else if (now_foreground) {
|
||||
z_index = 1;
|
||||
} else {
|
||||
z_index = 0;
|
||||
}
|
||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground);
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -8,6 +11,7 @@
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -32,6 +36,7 @@ public:
|
||||
public:
|
||||
void SetEventObserver(EventObserver* event_observer);
|
||||
void Update();
|
||||
void RequestUpdate();
|
||||
|
||||
public:
|
||||
void TrackApplet(std::shared_ptr<Applet> applet, bool is_application);
|
||||
@@ -49,6 +54,7 @@ public:
|
||||
void OnOperationModeChanged();
|
||||
void OnExitRequested();
|
||||
void OnHomeButtonPressed(ButtonPressDuration type);
|
||||
void OnSystemButtonPress(SystemButtonType type);
|
||||
void OnCaptureButtonPressed(ButtonPressDuration type) {}
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
@@ -56,7 +62,8 @@ private:
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||
|
||||
private:
|
||||
// System reference.
|
||||
@@ -75,6 +82,7 @@ private:
|
||||
// Foreground roots.
|
||||
Applet* m_home_menu{};
|
||||
Applet* m_application{};
|
||||
Applet* m_overlay_display{};
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "core/hle/service/audio/audio_controller.h"
|
||||
#include "core/hle/service/audio/audio_out_manager.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/set/system_settings_server.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "common/settings.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Service::Audio {
|
||||
|
||||
@@ -17,12 +24,12 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
: ServiceFramework{system_, "audctl"}, service_context{system, "audctl"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetTargetVolume"},
|
||||
{1, nullptr, "SetTargetVolume"},
|
||||
{0, D<&IAudioController::GetTargetVolume>, "GetTargetVolume"},
|
||||
{1, D<&IAudioController::SetTargetVolume>, "SetTargetVolume"},
|
||||
{2, D<&IAudioController::GetTargetVolumeMin>, "GetTargetVolumeMin"},
|
||||
{3, D<&IAudioController::GetTargetVolumeMax>, "GetTargetVolumeMax"},
|
||||
{4, nullptr, "IsTargetMute"},
|
||||
{5, nullptr, "SetTargetMute"},
|
||||
{4, D<&IAudioController::IsTargetMute>, "IsTargetMute"},
|
||||
{5, D<&IAudioController::SetTargetMute>, "SetTargetMute"},
|
||||
{6, nullptr, "IsTargetConnected"},
|
||||
{7, nullptr, "SetDefaultTarget"},
|
||||
{8, nullptr, "GetDefaultTarget"},
|
||||
@@ -49,7 +56,7 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
{29, nullptr, "BindAudioOutputChannelCountUpdateEventForPlayReport"},
|
||||
{30, D<&IAudioController::SetSpeakerAutoMuteEnabled>, "SetSpeakerAutoMuteEnabled"},
|
||||
{31, D<&IAudioController::IsSpeakerAutoMuteEnabled>, "IsSpeakerAutoMuteEnabled"},
|
||||
{32, nullptr, "GetActiveOutputTarget"},
|
||||
{32, D<&IAudioController::GetActiveOutputTarget>, "GetActiveOutputTarget"},
|
||||
{33, nullptr, "GetTargetDeviceInfo"},
|
||||
{34, D<&IAudioController::AcquireTargetNotification>, "AcquireTargetNotification"},
|
||||
{35, nullptr, "SetHearingProtectionSafeguardTimerRemainingTimeForDebug"},
|
||||
@@ -84,6 +91,38 @@ IAudioController::IAudioController(Core::System& system_)
|
||||
m_set_sys =
|
||||
system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
|
||||
notification_event = service_context.CreateEvent("IAudioController:NotificationEvent");
|
||||
|
||||
// Probably shouldn't do this in constructor?
|
||||
try {
|
||||
const int ui_volume = Settings::values.volume.GetValue();
|
||||
const int mapped = static_cast<int>(std::lround((static_cast<double>(ui_volume) / 100.0) * 15.0));
|
||||
const auto active_idx = static_cast<size_t>(m_active_target);
|
||||
if (active_idx < m_target_volumes.size()) {
|
||||
m_target_volumes[active_idx] = std::clamp(mapped, 0, 15);
|
||||
}
|
||||
|
||||
const bool muted = Settings::values.audio_muted.GetValue();
|
||||
for (auto& m : m_target_muted) {
|
||||
m = muted;
|
||||
}
|
||||
|
||||
if (!muted && active_idx < m_target_volumes.size()) {
|
||||
const float vol_f = static_cast<float>(m_target_volumes[active_idx]) / 15.0f;
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol_f);
|
||||
sink.SetDeviceVolume(vol_f);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to apply initial sink volume from settings");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<Service::Audio::IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(static_cast<float>(m_target_volumes[active_idx]) / 15.0f);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Catch if something fails, since this is constructor
|
||||
}
|
||||
}
|
||||
|
||||
IAudioController::~IAudioController() {
|
||||
@@ -191,4 +230,120 @@ Result IAudioController::Unknown5000(Out<SharedPointer<IAudioController>> out_au
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "GetTargetVolume called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "GetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_target_volume = m_target_volumes[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume) {
|
||||
LOG_INFO(Audio, "SetTargetVolume called, target={}, volume={}", target, target_volume);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_volumes.size()) {
|
||||
LOG_ERROR(Audio, "SetTargetVolume invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
if (target_volume < 0) {
|
||||
target_volume = 0;
|
||||
} else if (target_volume > 15) {
|
||||
target_volume = 15;
|
||||
}
|
||||
|
||||
m_target_volumes[idx] = target_volume;
|
||||
|
||||
if (m_active_target == target && !m_target_muted[idx]) {
|
||||
const float vol = static_cast<float>(target_volume) / 15.0f;
|
||||
// try catch this, as we don't know how it handles it when no output is set.
|
||||
// we already have audio issues when no output is set, so catch.
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume");
|
||||
}
|
||||
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_active_target == target) {
|
||||
const int ui_volume = static_cast<int>(std::lround((static_cast<double>(target_volume) / 15.0) * 100.0));
|
||||
Settings::values.volume.SetValue(static_cast<u8>(std::clamp(ui_volume, 0, 100)));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_DEBUG(Audio, "called, target={}", target);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
*out_is_target_muted = m_target_muted[idx];
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target) {
|
||||
LOG_INFO(Audio, "called, target={}, muted={}", target, is_muted);
|
||||
|
||||
const auto idx = static_cast<size_t>(target);
|
||||
if (idx >= m_target_muted.size()) {
|
||||
LOG_ERROR(Audio, "invalid target {}", idx);
|
||||
R_THROW(ResultInvalidArgument);
|
||||
}
|
||||
|
||||
m_target_muted[idx] = is_muted;
|
||||
|
||||
if (m_active_target == target) {
|
||||
try {
|
||||
auto& sink = system.AudioCore().GetOutputSink();
|
||||
if (is_muted) {
|
||||
sink.SetSystemVolume(0.0f);
|
||||
sink.SetDeviceVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
sink.SetSystemVolume(vol);
|
||||
sink.SetDeviceVolume(vol);
|
||||
}
|
||||
} catch (...) {
|
||||
LOG_WARNING(Audio, "Failed to set sink system volume on mute change");
|
||||
}
|
||||
|
||||
// Also update any open audout sessions via the audout:u service.
|
||||
if (auto audout_mgr = system.ServiceManager().GetService<IAudioOutManager>("audout:u")) {
|
||||
if (is_muted) {
|
||||
audout_mgr->SetAllAudioOutVolume(0.0f);
|
||||
} else {
|
||||
const float vol = static_cast<float>(m_target_volumes[idx]) / 15.0f;
|
||||
audout_mgr->SetAllAudioOutVolume(vol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::values.audio_muted.SetValue(is_muted);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioController::GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target) {
|
||||
LOG_DEBUG(Audio, "GetActiveOutputTarget called");
|
||||
*out_active_target = m_active_target;
|
||||
R_SUCCEED();
|
||||
}
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -35,6 +38,11 @@ private:
|
||||
|
||||
Result GetTargetVolumeMin(Out<s32> out_target_min_volume);
|
||||
Result GetTargetVolumeMax(Out<s32> out_target_max_volume);
|
||||
Result GetTargetVolume(Out<s32> out_target_volume, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetVolume(Set::AudioOutputModeTarget target, s32 target_volume);
|
||||
Result IsTargetMute(Out<bool> out_is_target_muted, Set::AudioOutputModeTarget target);
|
||||
Result SetTargetMute(bool is_muted, Set::AudioOutputModeTarget target);
|
||||
Result GetActiveOutputTarget(Out<Set::AudioOutputModeTarget> out_active_target);
|
||||
Result GetAudioOutputMode(Out<Set::AudioOutputMode> out_output_mode,
|
||||
Set::AudioOutputModeTarget target);
|
||||
Result SetAudioOutputMode(Set::AudioOutputModeTarget target, Set::AudioOutputMode output_mode);
|
||||
@@ -55,6 +63,9 @@ private:
|
||||
|
||||
Kernel::KEvent* notification_event;
|
||||
std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
|
||||
std::array<s32, 6> m_target_volumes{{15, 15, 15, 15, 15, 15}};
|
||||
std::array<bool, 6> m_target_muted{{false, false, false, false, false, false}};
|
||||
Set::AudioOutputModeTarget m_active_target{Set::AudioOutputModeTarget::Speaker};
|
||||
};
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -98,4 +101,15 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IAudioOutManager::SetAllAudioOutVolume(f32 volume) {
|
||||
std::scoped_lock l{impl->mutex};
|
||||
for (auto& session : impl->sessions) {
|
||||
if (session) {
|
||||
session->GetSystem().SetVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Audio
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,6 +21,8 @@ public:
|
||||
explicit IAudioOutManager(Core::System& system_);
|
||||
~IAudioOutManager() override;
|
||||
|
||||
Result SetAllAudioOutVolume(f32 volume);
|
||||
|
||||
private:
|
||||
Result ListAudioOuts(OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_audio_outs,
|
||||
Out<u32> out_count);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -19,6 +22,7 @@ constexpr Result ResultInvalidAddressInfo{ErrorModule::Audio, 42};
|
||||
constexpr Result ResultNotSupported{ErrorModule::Audio, 513};
|
||||
constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
|
||||
constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
|
||||
constexpr Result ResultInvalidArgument{ErrorModule::Audio, 900};
|
||||
|
||||
constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
|
||||
constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -53,6 +56,7 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
|
||||
{8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
|
||||
{10011, nullptr, "SetInternalErrorConversionEnabled"},
|
||||
{50000, nullptr, "LoadMakerNoteInfoForDebug"},
|
||||
{50011, C<&IAlbumAccessorService::GetAlbumAccessResultForDebug>, "GetAlbumAccessResultForDebug"},
|
||||
{60002, nullptr, "OpenAccessorSession"},
|
||||
};
|
||||
// clang-format on
|
||||
@@ -197,4 +201,11 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) {
|
||||
return in_result;
|
||||
}
|
||||
|
||||
|
||||
Result IAlbumAccessorService::GetAlbumAccessResultForDebug(Out<Result> out_result) {
|
||||
LOG_WARNING(Service_Capture, "(STUBBED) called");
|
||||
*out_result = ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -52,6 +55,8 @@ private:
|
||||
|
||||
Result TranslateResult(Result in_result);
|
||||
|
||||
Result GetAlbumAccessResultForDebug(Out<Result> out_result);
|
||||
|
||||
std::shared_ptr<AlbumManager> manager = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -150,16 +153,17 @@ private:
|
||||
|
||||
class INpnsUser final : public ServiceFramework<INpnsUser> {
|
||||
public:
|
||||
explicit INpnsUser(Core::System& system_) : ServiceFramework{system_, "npns:u"} {
|
||||
explicit INpnsUser(Core::System& system_)
|
||||
: ServiceFramework{system_, "npns:u"}, service_context{system, "npns:u"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, nullptr, "ListenAll"},
|
||||
{2, nullptr, "ListenTo"},
|
||||
{3, nullptr, "Receive"},
|
||||
{4, nullptr, "ReceiveRaw"},
|
||||
{5, nullptr, "GetReceiveEvent"},
|
||||
{5, C<&INpnsUser::GetReceiveEvent>, "GetReceiveEvent"},
|
||||
{7, nullptr, "GetStateChangeEvent"},
|
||||
{8, nullptr, "ListenToByName"}, // 18.0.0+
|
||||
{8, C<&INpnsUser::ListenToByName>, "ListenToByName"}, // 18.0.0+
|
||||
{21, nullptr, "CreateToken"},
|
||||
{23, nullptr, "DestroyToken"},
|
||||
{25, nullptr, "QueryIsTokenValid"},
|
||||
@@ -178,7 +182,33 @@ public:
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
get_receive_event = service_context.CreateEvent("npns:u:GetReceiveEvent");
|
||||
}
|
||||
|
||||
~INpnsUser() override {
|
||||
service_context.CloseEvent(get_receive_event);
|
||||
}
|
||||
|
||||
private:
|
||||
Result ListenToByName(InBuffer<BufferAttr_HipcMapAlias> name_buffer) {
|
||||
const std::string name(reinterpret_cast<const char*>(name_buffer.data()), name_buffer.size());
|
||||
LOG_DEBUG(Service_NPNS, "called, name={}", name);
|
||||
|
||||
// Store the name for future use if needed
|
||||
// For now, just acknowledge the registration
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetReceiveEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_DEBUG(Service_NPNS, "called");
|
||||
|
||||
*out_event = &get_receive_event->GetReadableEvent();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* get_receive_event;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -11,6 +12,7 @@
|
||||
#include "core/hle/service/ns/application_manager_interface.h"
|
||||
#include "core/hle/service/ns/content_management_interface.h"
|
||||
#include "core/hle/service/ns/read_only_application_control_data_interface.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
|
||||
namespace Service::NS {
|
||||
|
||||
@@ -19,7 +21,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
service_context{system, "IApplicationManagerInterface"},
|
||||
record_update_system_event{service_context}, sd_card_mount_status_event{service_context},
|
||||
gamecard_update_detection_event{service_context},
|
||||
gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context} {
|
||||
gamecard_mount_status_event{service_context}, gamecard_mount_failure_event{service_context},
|
||||
gamecard_waken_ready_event{service_context}, unknown_event{service_context} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IApplicationManagerInterface::ListApplicationRecord>, "ListApplicationRecord"},
|
||||
@@ -42,7 +45,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{26, nullptr, "BeginInstallApplication"},
|
||||
{27, nullptr, "DeleteApplicationRecord"},
|
||||
{30, nullptr, "RequestApplicationUpdateInfo"},
|
||||
{31, nullptr, "Unknown31"},
|
||||
{31, nullptr, "RequestUpdateApplication"},
|
||||
{32, nullptr, "CancelApplicationDownload"},
|
||||
{33, nullptr, "ResumeApplicationDownload"},
|
||||
{35, nullptr, "UpdateVersionList"},
|
||||
@@ -138,6 +141,8 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{508, nullptr, "GetLastGameCardMountFailureResult"},
|
||||
{509, nullptr, "ListApplicationIdOnGameCard"},
|
||||
{510, nullptr, "GetGameCardPlatformRegion"},
|
||||
{511, D<&IApplicationManagerInterface::GetGameCardWakenReadyEvent>, "GetGameCardWakenReadyEvent"},
|
||||
{512, D<&IApplicationManagerInterface::IsGameCardApplicationRunning>, "IsGameCardApplicationRunning"},
|
||||
{600, nullptr, "CountApplicationContentMeta"},
|
||||
{601, nullptr, "ListApplicationContentMetaStatus"},
|
||||
{602, nullptr, "ListAvailableAddOnContent"},
|
||||
@@ -162,7 +167,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||
{901, nullptr, "GetApplicationRecordProperty"},
|
||||
{902, nullptr, "EnableApplicationAutoUpdate"},
|
||||
{903, nullptr, "DisableApplicationAutoUpdate"},
|
||||
{904, nullptr, "TouchApplication"},
|
||||
{904, D<&IApplicationManagerInterface::TouchApplication>, "TouchApplication"},
|
||||
{905, nullptr, "RequestApplicationUpdate"},
|
||||
{906, D<&IApplicationManagerInterface::IsApplicationUpdateRequested>, "IsApplicationUpdateRequested"},
|
||||
{907, nullptr, "WithdrawApplicationUpdateRequest"},
|
||||
@@ -329,7 +334,7 @@ Result IApplicationManagerInterface::GetApplicationControlData(
|
||||
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
|
||||
ApplicationControlSource application_control_source, u64 application_id) {
|
||||
LOG_DEBUG(Service_NS, "called");
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlDataOld(
|
||||
R_RETURN(IReadOnlyApplicationControlDataInterface(system).GetApplicationControlData(
|
||||
out_buffer, out_actual_size, application_control_source, application_id));
|
||||
}
|
||||
|
||||
@@ -403,6 +408,19 @@ Result IApplicationManagerInterface::GetGameCardMountFailureEvent(
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::GetGameCardWakenReadyEvent(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_event = gamecard_waken_ready_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_is_running) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_is_running = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
|
||||
Out<bool> out_is_any_application_entity_installed) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
@@ -526,6 +544,11 @@ Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_siz
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::TouchApplication(u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called. application_id={:016X}", application_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::IsApplicationUpdateRequested(Out<bool> out_update_required,
|
||||
Out<u32> out_update_version,
|
||||
u64 application_id) {
|
||||
@@ -549,14 +572,18 @@ Result IApplicationManagerInterface::GetApplicationTerminateResult(Out<Result> o
|
||||
|
||||
Result IApplicationManagerInterface::RequestDownloadApplicationControlDataInBackground(
|
||||
u64 control_source, u64 application_id) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED), control_source={} app={:016X}", control_source, application_id);
|
||||
LOG_INFO(Service_NS, "called, control_source={} app={:016X}",
|
||||
control_source, application_id);
|
||||
|
||||
unknown_event.Signal();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IApplicationManagerInterface::Unknown4022(
|
||||
OutCopyHandle<Kernel::KReadableEvent> out_event) {
|
||||
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||
*out_event = gamecard_update_detection_event.GetHandle();
|
||||
unknown_event.Signal();
|
||||
*out_event = unknown_event.GetHandle();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user