mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-30 02:16:06 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| edc38b8230 | |||
| ffecb4af7b |
+27
-11
@@ -43,6 +43,7 @@ This guide will walk you through adding a new boolean toggle setting to Eden's c
|
|||||||
Firstly add your desired toggle:
|
Firstly add your desired toggle:
|
||||||
|
|
||||||
Example: `src/common/setting.h`
|
Example: `src/common/setting.h`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
SwitchableSetting<bool> your_setting_name{linkage, false, "your_setting_name", Category::RendererExtensions};
|
||||||
```
|
```
|
||||||
@@ -67,6 +68,7 @@ Common Categories:
|
|||||||
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
Add the toggle to the Qt UI, where you wish for it to appear and place it there.
|
||||||
|
|
||||||
Example: `src/qt_common/config/shared_translation.cpp`
|
Example: `src/qt_common/config/shared_translation.cpp`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
INSERT(Settings,
|
INSERT(Settings,
|
||||||
your_setting_name,
|
your_setting_name,
|
||||||
@@ -91,6 +93,7 @@ INSERT(Settings,
|
|||||||
Add where it should be in the settings.
|
Add where it should be in the settings.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
||||||
```
|
```
|
||||||
@@ -106,6 +109,7 @@ RENDERER_YOUR_SETTING_NAME("your_setting_name"),
|
|||||||
Add the toggle to the Kotlin (Android) UI
|
Add the toggle to the Kotlin (Android) UI
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
@@ -123,6 +127,7 @@ put(
|
|||||||
Add your setting within the right category.
|
Add your setting within the right category.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
Example: `src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt`
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
||||||
```
|
```
|
||||||
@@ -137,6 +142,7 @@ add(BooleanSetting.RENDERER_YOUR_SETTING_NAME.key)
|
|||||||
Add your setting and description in the appropriate place.
|
Add your setting and description in the appropriate place.
|
||||||
|
|
||||||
Example: `src/android/app/src/main/res/values/strings.xml`
|
Example: `src/android/app/src/main/res/values/strings.xml`
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<string name="your_setting_name">Your Setting Display Name</string>
|
<string name="your_setting_name">Your Setting Display Name</string>
|
||||||
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
<string name="your_setting_name_description">Detailed description of what this setting does. Explain any caveats, requirements, or warnings here.</string>
|
||||||
@@ -150,6 +156,7 @@ Now the UI part is done find a place in the code for the toggle,
|
|||||||
And use it to your heart's desire!
|
And use it to your heart's desire!
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const bool your_value = Settings::values.your_setting_name.GetValue();
|
const bool your_value = Settings::values.your_setting_name.GetValue();
|
||||||
|
|
||||||
@@ -196,25 +203,31 @@ Common advantages recap:
|
|||||||
|
|
||||||
#### Accessing Debug Knobs (dev side)
|
#### Accessing Debug Knobs (dev side)
|
||||||
|
|
||||||
Use the `Settings::getDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
Use the `Settings::GetDebugKnobAt(u8 i)` function to check if a specific bit is set:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
//cpp side
|
//cpp side
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
|
//To use it as a general purpose uint var:
|
||||||
|
unsigned int debug_knobs = Settings::values.debug_knobs.GetValue();
|
||||||
|
|
||||||
// Check if bit 0 is set
|
// Check if bit 0 is set
|
||||||
bool feature_enabled = Settings::getDebugKnobAt(0);
|
bool feature_enabled = Settings::GetDebugKnobAt(0);
|
||||||
|
|
||||||
// Check if bit 15 is set
|
// Check if bit 15 is set
|
||||||
bool another_feature = Settings::getDebugKnobAt(15);
|
bool another_feature = Settings::GetDebugKnobAt(15);
|
||||||
```
|
```
|
||||||
|
|
||||||
```kts
|
```kts
|
||||||
//kotlin side
|
//kotlin side
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
|
||||||
|
//To use it as a general purpose uint var
|
||||||
|
val debug_knobs: Int = UShortSetting.DEBUG_KNOBS.getInt()
|
||||||
|
|
||||||
// Check if bit x is set
|
// Check if bit x is set
|
||||||
bool feature_enabled = Settings.getDebugKnobAt(x); //x as integer from 0 to 15
|
bool feature_enabled = Settings.GetDebugKnobAt(x); //x as integer from 0 to 15
|
||||||
```
|
```
|
||||||
|
|
||||||
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
The function returns `true` if the specified bit (0-15) is set in the `debug_knobs` value, `false` otherwise.
|
||||||
@@ -247,6 +260,7 @@ There are two main confusions when talking about knobs:
|
|||||||
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
Sometimes when an user reports: knobs 1 and 2 gets better performance, dev may get confuse whether he means the knobs 1 and 2 literally, or the 1st and 2nd knobs (knobs 0 and 1).
|
||||||
|
|
||||||
Debug knobs are **zero-based**, which means:
|
Debug knobs are **zero-based**, which means:
|
||||||
|
|
||||||
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
* The first knob is the knob(0) (or knob0 henceforth), and the last one is the 15 (knob15, likewise)
|
||||||
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
* You can talk: "knob0 is enabled/disabled", "In this video i was using only knobs 0 and 2", etc.
|
||||||
|
|
||||||
@@ -259,6 +273,7 @@ Whenever you're instructing tests or reporting results, be precise about whether
|
|||||||
|
|
||||||
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
ALWAYS use the word in PLURAL (knobs), without mentioning which one, to refer to the setting, aka multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knobs=0**: no knobs enabled
|
- **knobs=0**: no knobs enabled
|
||||||
- **knobs=1**: knob0 enabled, others disabled
|
- **knobs=1**: knob0 enabled, others disabled
|
||||||
- **knobs=2**: knob1 enabled, others disabled
|
- **knobs=2**: knob1 enabled, others disabled
|
||||||
@@ -270,6 +285,7 @@ Examples:
|
|||||||
|
|
||||||
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
Use the word in SINGULAR (knob), or in plural but referring which ones, when meaning multiple knobs at once:
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- **knob0**: knob 0 enabled, others disabled
|
- **knob0**: knob 0 enabled, others disabled
|
||||||
- **knob1**: knob 1 enabled, others disabled
|
- **knob1**: knob 1 enabled, others disabled
|
||||||
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
- **knobs 0 and 1**: knobs 0 and 1 enabled, others disabled
|
||||||
@@ -282,12 +298,12 @@ Examples:
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void SomeFunction() {
|
void SomeFunction() {
|
||||||
if (Settings::getDebugKnobAt(0)) {
|
if (Settings::GetDebugKnobAt(0)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
LOG_DEBUG(Common, "Debug feature 0 is enabled");
|
||||||
// Additional debug code here
|
// Additional debug code here
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::getDebugKnobAt(1)) {
|
if (Settings::GetDebugKnobAt(1)) {
|
||||||
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
LOG_DEBUG(Common, "Debug feature 1 is enabled");
|
||||||
// Different debug behavior
|
// Different debug behavior
|
||||||
}
|
}
|
||||||
@@ -299,7 +315,7 @@ void SomeFunction() {
|
|||||||
```cpp
|
```cpp
|
||||||
bool UseOptimizedPath() {
|
bool UseOptimizedPath() {
|
||||||
// Skip optimization if debug bit 2 is set for testing
|
// Skip optimization if debug bit 2 is set for testing
|
||||||
return !Settings::getDebugKnobAt(2);
|
return !Settings::GetDebugKnobAt(2);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -308,13 +324,13 @@ bool UseOptimizedPath() {
|
|||||||
```cpp
|
```cpp
|
||||||
void ExperimentalFeature() {
|
void ExperimentalFeature() {
|
||||||
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
static constexpr u8 EXPERIMENTAL_FEATURE_BIT = 3;
|
||||||
|
|
||||||
if (!Settings::getDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
if (!Settings::GetDebugKnobAt(EXPERIMENTAL_FEATURE_BIT)) {
|
||||||
// Fallback to stable implementation
|
// Fallback to stable implementation
|
||||||
StableImplementation();
|
StableImplementation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Experimental implementation
|
// Experimental implementation
|
||||||
ExperimentalImplementation();
|
ExperimentalImplementation();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ object NativeLibrary {
|
|||||||
|
|
||||||
external fun refreshThreadPolicies()
|
external fun refreshThreadPolicies()
|
||||||
|
|
||||||
external fun getDebugKnobAt(index: Int): Boolean
|
external fun GetDebugKnobAt(index: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current speed limit to the configured turbo speed.
|
* Set the current speed limit to the configured turbo speed.
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ object Settings {
|
|||||||
fun getPlayerString(player: Int): String =
|
fun getPlayerString(player: Int): String =
|
||||||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||||
|
|
||||||
fun getDebugKnobAt(index: Int): Boolean {
|
fun GetDebugKnobAt(index: Int): Boolean {
|
||||||
return org.yuzu.yuzu_emu.NativeLibrary.getDebugKnobAt(index)
|
return org.yuzu.yuzu_emu.NativeLibrary.GetDebugKnobAt(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||||
|
|||||||
+2
-3
@@ -11,8 +11,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
|
|||||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit")
|
||||||
DEBUG_KNOBS("debug_knobs")
|
|
||||||
;
|
;
|
||||||
|
|
||||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||||
@@ -29,4 +28,4 @@ enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
|||||||
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
override fun getValueAsString(needsGlobal: Boolean): String = getShort(needsGlobal).toString()
|
||||||
|
|
||||||
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
override fun reset() = NativeConfig.setShort(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.features.settings.model
|
||||||
|
|
||||||
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
|
|
||||||
|
enum class UShortSetting(override val key: String) : AbstractIntSetting {
|
||||||
|
DEBUG_KNOBS("debug_knobs")
|
||||||
|
;
|
||||||
|
|
||||||
|
override fun getInt(needsGlobal: Boolean): Int =
|
||||||
|
NativeConfig.getUnsignedShort(key, needsGlobal)
|
||||||
|
|
||||||
|
override fun setInt(value: Int) {
|
||||||
|
if (NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
global = false
|
||||||
|
}
|
||||||
|
NativeConfig.setUnsignedShort(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val defaultValue: Int by lazy { NativeConfig.getDefaultToString(key).toInt() }
|
||||||
|
|
||||||
|
override fun getValueAsString(needsGlobal: Boolean): String = getInt(needsGlobal).toString()
|
||||||
|
|
||||||
|
override fun reset() = NativeConfig.setUnsignedShort(key, defaultValue)
|
||||||
|
}
|
||||||
+2
-1
@@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||||
import org.yuzu.yuzu_emu.network.NetDataValidators
|
import org.yuzu.yuzu_emu.network.NetDataValidators
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
@@ -1034,7 +1035,7 @@ abstract class SettingsItem(
|
|||||||
)
|
)
|
||||||
put(
|
put(
|
||||||
SpinBoxSetting(
|
SpinBoxSetting(
|
||||||
ShortSetting.DEBUG_KNOBS,
|
UShortSetting.DEBUG_KNOBS,
|
||||||
titleId = R.string.debug_knobs,
|
titleId = R.string.debug_knobs,
|
||||||
descriptionId = R.string.debug_knobs_description,
|
descriptionId = R.string.debug_knobs_description,
|
||||||
valueHint = R.string.debug_knobs_hint,
|
valueHint = R.string.debug_knobs_hint,
|
||||||
|
|||||||
+2
-1
@@ -25,6 +25,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||||
|
import org.yuzu.yuzu_emu.features.settings.model.UShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
@@ -1326,7 +1327,7 @@ class SettingsFragmentPresenter(
|
|||||||
|
|
||||||
add(HeaderSetting(R.string.general))
|
add(HeaderSetting(R.string.general))
|
||||||
|
|
||||||
add(ShortSetting.DEBUG_KNOBS.key)
|
add(UShortSetting.DEBUG_KNOBS.key)
|
||||||
add(StringSetting.PROGRAM_ARGS.key)
|
add(StringSetting.PROGRAM_ARGS.key)
|
||||||
|
|
||||||
if (!NativeConfig.isPerGameConfigLoaded()) {
|
if (!NativeConfig.isPerGameConfigLoaded()) {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import info.debatty.java.stringsimilarity.Jaccard
|
|||||||
import info.debatty.java.stringsimilarity.JaroWinkler
|
import info.debatty.java.stringsimilarity.JaroWinkler
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.view.doOnNextLayout
|
||||||
|
|
||||||
class GamesFragment : Fragment() {
|
class GamesFragment : Fragment() {
|
||||||
private var _binding: FragmentGamesBinding? = null
|
private var _binding: FragmentGamesBinding? = null
|
||||||
@@ -58,6 +59,7 @@ class GamesFragment : Fragment() {
|
|||||||
private var originalHeaderLeftMargin: Int? = null
|
private var originalHeaderLeftMargin: Int? = null
|
||||||
|
|
||||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||||
|
private var fallbackBottomInset: Int = 0
|
||||||
private var pendingPostReloadListSettle = false
|
private var pendingPostReloadListSettle = false
|
||||||
private var pendingPostReloadListSettleGeneration = 0
|
private var pendingPostReloadListSettleGeneration = 0
|
||||||
private var gameListSubmitGeneration = 0
|
private var gameListSubmitGeneration = 0
|
||||||
@@ -225,7 +227,12 @@ class GamesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||||
}
|
}
|
||||||
if (savedViewType != GameAdapter.VIEW_TYPE_CAROUSEL) {
|
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||||
|
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||||
|
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||||
|
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||||
}
|
}
|
||||||
adapter = gameAdapter
|
adapter = gameAdapter
|
||||||
@@ -583,6 +590,11 @@ class GamesFragment : Fragment() {
|
|||||||
qlaunchButton.layoutParams = mlpQLaunch
|
qlaunchButton.layoutParams = mlpQLaunch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||||
|
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||||
|
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||||
|
fallbackBottomInset = bottomInset
|
||||||
|
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||||
windowInsets
|
windowInsets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ object NativeConfig {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
external fun setShort(key: String, value: Short)
|
external fun setShort(key: String, value: Short)
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
external fun getUnsignedShort(key: String, needsGlobal: Boolean): Int
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
external fun setUnsignedShort(key: String, value: Int)
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
external fun getInt(key: String, needsGlobal: Boolean): Int
|
external fun getInt(key: String, needsGlobal: Boolean): Int
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,13 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||||
import androidx.core.view.doOnNextLayout
|
import androidx.core.view.doOnNextLayout
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
|
||||||
/**
|
/**
|
||||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||||
* It manages overlapping cards, center snapping, custom drawing order,
|
* It manages overlapping cards, center snapping, custom drawing order,
|
||||||
@@ -35,9 +32,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var overlapFactor: Float = 0f
|
private var overlapFactor: Float = 0f
|
||||||
private var overlapPx: Int = 0
|
private var overlapPx: Int = 0
|
||||||
private var bottomInset: Int = 0
|
private var bottomInset: Int = -1
|
||||||
private var latestWindowInsets: WindowInsetsCompat? = null
|
|
||||||
private var cardGeometryInitialized: Boolean = false
|
|
||||||
private var overlapDecoration: OverlappingDecoration? = null
|
private var overlapDecoration: OverlappingDecoration? = null
|
||||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||||
private var scalingScrollListener: OnScrollListener? = null
|
private var scalingScrollListener: OnScrollListener? = null
|
||||||
@@ -96,38 +91,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
setChildrenDrawingOrderEnabled(true)
|
setChildrenDrawingOrderEnabled(true)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(this) { _, insets ->
|
|
||||||
latestWindowInsets = insets
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
insets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow()
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
|
||||||
if (w != oldw || h != oldh) {
|
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
||||||
super.onLayout(changed, left, top, right, bottom)
|
|
||||||
if (isCarouselMode) updateChildScalesAndAlpha()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
||||||
super.onWindowFocusChanged(hasFocus)
|
|
||||||
if (hasFocus) {
|
|
||||||
ViewCompat.requestApplyInsets(this)
|
|
||||||
post { updateCardGeometry() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setAdapter(adapter: Adapter<*>?) {
|
override fun setAdapter(adapter: Adapter<*>?) {
|
||||||
@@ -140,8 +103,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
super.setAdapter(adapter)
|
super.setAdapter(adapter)
|
||||||
|
|
||||||
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
(adapter as? GameAdapter)?.registerAdapterDataObserver(carouselAdapterObserver)
|
||||||
updateCardGeometry()
|
|
||||||
applyCarouselPadding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
private fun calculateCenter(width: Int, paddingStart: Int, paddingEnd: Int): Int {
|
||||||
@@ -292,71 +253,40 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveBottomInset(windowInsets: WindowInsetsCompat): Int {
|
fun notifyInsetsReady(newBottomInset: Int) {
|
||||||
val navigationBottom = if (FullscreenHelper.isFullscreenEnabled(context)) {
|
if (bottomInset != newBottomInset) {
|
||||||
0
|
bottomInset = newBottomInset
|
||||||
} else {
|
}
|
||||||
windowInsets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars()).bottom
|
|
||||||
|
if (isCarouselMode) {
|
||||||
|
setupCarousel(true)
|
||||||
|
} else {
|
||||||
|
setupCarousel(false)
|
||||||
}
|
}
|
||||||
val gestureInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.systemGestures()
|
|
||||||
)
|
|
||||||
val cutoutInsets = windowInsets.getInsetsIgnoringVisibility(
|
|
||||||
WindowInsetsCompat.Type.displayCutout()
|
|
||||||
)
|
|
||||||
return maxOf(navigationBottom, gestureInsets.bottom, cutoutInsets.bottom)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCardGeometry() {
|
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||||
if (!isCarouselMode || height <= 0) return
|
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||||
|
var gameAdapter = adapter as? GameAdapter ?: return
|
||||||
|
var newCardSize = cardSize(bottomInset)
|
||||||
|
if (gameAdapter.cardSize != newCardSize) {
|
||||||
|
gameAdapter.setCardSize(newCardSize)
|
||||||
|
}
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
if (isCarouselMode) {
|
||||||
val windowInsets = latestWindowInsets ?: ViewCompat.getRootWindowInsets(this) ?: return
|
setupCarousel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cardGeometryInitialized && !hasWindowFocus()) return
|
fun cardSize(bottomInset: Int): Int {
|
||||||
|
|
||||||
val newBottomInset = resolveBottomInset(windowInsets).coerceIn(0, height)
|
|
||||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||||
0f,
|
0f,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
val screenWidth = resources.displayMetrics.widthPixels.toFloat()
|
val scaledHeight = height * userFactor
|
||||||
val screenHeight = resources.displayMetrics.heightPixels.toFloat()
|
val availableHeight = height - bottomInset
|
||||||
val aspectFactor = ((screenWidth / screenHeight) / (20f / 9f))
|
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||||
.pow(0.75f)
|
|
||||||
.coerceIn(0.5f, 1f)
|
|
||||||
val newCardSize = minOf(
|
|
||||||
(height * userFactor).toInt(),
|
|
||||||
height - newBottomInset,
|
|
||||||
(height * aspectFactor).toInt()
|
|
||||||
)
|
|
||||||
if (newCardSize <= 0) return
|
|
||||||
val insetChanged = bottomInset != newBottomInset
|
|
||||||
val cardSizeChanged = gameAdapter.cardSize != newCardSize
|
|
||||||
|
|
||||||
bottomInset = newBottomInset
|
|
||||||
cardGeometryInitialized = true
|
|
||||||
|
|
||||||
if (cardSizeChanged) gameAdapter.setCardSize(newCardSize)
|
|
||||||
if (insetChanged || cardSizeChanged) setupCarousel(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun applyCarouselPadding() {
|
|
||||||
if (!isCarouselMode) return
|
|
||||||
|
|
||||||
val gameAdapter = adapter as? GameAdapter ?: return
|
|
||||||
val cardSize = gameAdapter.cardSize
|
|
||||||
if (cardSize <= 0 || bottomInset < 0) return
|
|
||||||
|
|
||||||
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0)
|
|
||||||
val sidePadding = (width - cardSize) / 2
|
|
||||||
if (paddingLeft != sidePadding || paddingTop != topPadding ||
|
|
||||||
paddingRight != sidePadding || paddingBottom != 0
|
|
||||||
) {
|
|
||||||
setPadding(sidePadding, topPadding, sidePadding, 0)
|
|
||||||
}
|
|
||||||
clipToPadding = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setupCarousel(enabled: Boolean) {
|
fun setupCarousel(enabled: Boolean) {
|
||||||
@@ -385,6 +315,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
internalFlingMultiplier
|
internalFlingMultiplier
|
||||||
).coerceIn(1f, 5f)
|
).coerceIn(1f, 5f)
|
||||||
|
|
||||||
|
// Detach SnapHelper during setup
|
||||||
|
pagerSnapHelper?.attachToRecyclerView(null)
|
||||||
|
|
||||||
// Add overlap decoration if not present
|
// Add overlap decoration if not present
|
||||||
if (overlapDecoration == null) {
|
if (overlapDecoration == null) {
|
||||||
overlapDecoration = OverlappingDecoration(overlapPx)
|
overlapDecoration = OverlappingDecoration(overlapPx)
|
||||||
@@ -402,7 +335,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
addOnScrollListener(scalingScrollListener!!)
|
addOnScrollListener(scalingScrollListener!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCarouselPadding()
|
if (cardSize > 0) {
|
||||||
|
val topPadding = ((height - bottomInset - cardSize) / 2).coerceAtLeast(0) // Center vertically
|
||||||
|
val sidePadding = (width - cardSize) / 2 // Center first/last card
|
||||||
|
setPadding(sidePadding, topPadding, sidePadding, 0)
|
||||||
|
clipToPadding = false
|
||||||
|
}
|
||||||
|
|
||||||
if (pagerSnapHelper == null) {
|
if (pagerSnapHelper == null) {
|
||||||
pagerSnapHelper = CenterPagerSnapHelper()
|
pagerSnapHelper = CenterPagerSnapHelper()
|
||||||
@@ -424,7 +362,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
savedItemAnimator = null
|
savedItemAnimator = null
|
||||||
}
|
}
|
||||||
cardGeometryInitialized = false
|
|
||||||
useCustomDrawingOrder = false
|
useCustomDrawingOrder = false
|
||||||
// Reset padding and fling
|
// Reset padding and fling
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
|
|||||||
@@ -1300,8 +1300,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_refreshThreadPolicies(JNIEnv* env, jo
|
|||||||
Common::RefreshThreadPolicies();
|
Common::RefreshThreadPolicies();
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDebugKnobAt(JNIEnv* env, jobject jobj, jint index) {
|
||||||
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
return static_cast<jboolean>(Settings::GetDebugKnobAt(static_cast<u8>(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||||
|
|||||||
@@ -130,6 +130,25 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setShort(JNIEnv* env, jobject ob
|
|||||||
setting->SetValue(value);
|
setting->SetValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getUnsignedShort(JNIEnv* env, jobject obj,
|
||||||
|
jstring jkey,
|
||||||
|
jboolean needGlobal) {
|
||||||
|
auto setting = getSetting<u16>(env, jkey);
|
||||||
|
if (setting == nullptr) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return static_cast<jint>(setting->GetValue(static_cast<bool>(needGlobal)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setUnsignedShort(JNIEnv* env, jobject obj,
|
||||||
|
jstring jkey, jint value) {
|
||||||
|
auto setting = getSetting<u16>(env, jkey);
|
||||||
|
if (setting == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setting->SetValue(static_cast<u16>(value));
|
||||||
|
}
|
||||||
|
|
||||||
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
jint Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getInt(JNIEnv* env, jobject obj, jstring jkey,
|
||||||
jboolean needGlobal) {
|
jboolean needGlobal) {
|
||||||
auto setting = getSetting<int>(env, jkey);
|
auto setting = getSetting<int>(env, jkey);
|
||||||
|
|||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Common::Net {
|
namespace Common::Net {
|
||||||
|
|
||||||
struct Asset {
|
typedef struct {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string url;
|
std::string url;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string filename;
|
std::string filename;
|
||||||
};
|
} Asset;
|
||||||
|
|
||||||
struct Release {
|
typedef struct Release {
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string body;
|
std::string body;
|
||||||
std::string tag;
|
std::string tag;
|
||||||
@@ -39,7 +39,7 @@ struct Release {
|
|||||||
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
static std::optional<Release> FromJson(const std::string_view& json, const std::string &host, const std::string& repo);
|
||||||
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const nlohmann::json &json, const std::string &host, const std::string &repo);
|
||||||
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
static std::vector<Release> ListFromJson(const std::string_view &json, const std::string &host, const std::string &repo);
|
||||||
};
|
} Release;
|
||||||
|
|
||||||
// Make a request via httplib, and return the response body if applicable.
|
// Make a request via httplib, and return the response body if applicable.
|
||||||
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
std::optional<std::string> MakeRequest(const std::string &url, const std::string &path);
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ void LogSettings() {
|
|||||||
#undef LOG_PATH
|
#undef LOG_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getDebugKnobAt(u8 i) {
|
bool GetDebugKnobAt(u8 i) {
|
||||||
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
return (values.debug_knobs.GetValue() & (1 << (i & 0xF))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -904,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
@@ -947,7 +947,7 @@ constexpr u32 MAX_FRAME_GEN_MULTIPLIER = 4;
|
|||||||
|
|
||||||
[[nodiscard]] size_t FrameGenMaxGenerations();
|
[[nodiscard]] size_t FrameGenMaxGenerations();
|
||||||
|
|
||||||
bool getDebugKnobAt(u8 i);
|
bool GetDebugKnobAt(u8 i);
|
||||||
|
|
||||||
void UpdateGPUAccuracy();
|
void UpdateGPUAccuracy();
|
||||||
bool IsGPULevelHigh();
|
bool IsGPULevelHigh();
|
||||||
|
|||||||
+6
-34
@@ -4,7 +4,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
void SetNVDECActive(bool is_nvdec_active) {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
nvdec_active = is_nvdec_active;
|
||||||
++nvdec_active_channels[process_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
const auto it = nvdec_active_channels.find(process_id);
|
|
||||||
if (it == nvdec_active_channels.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (--it->second == 0) {
|
|
||||||
nvdec_active_channels.erase(it);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
return nvdec_active;
|
||||||
return !nvdec_active_channels.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
return nvdec_active_channels.contains(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -524,8 +505,6 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
std::mutex general_channel_mutex;
|
||||||
std::mutex nvdec_active_mutex;
|
|
||||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
|
||||||
std::atomic_bool is_paused{};
|
std::atomic_bool is_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -533,6 +512,7 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
|
bool nvdec_active : 1 = false;
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -596,22 +576,14 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||||
impl->NotifyNVDECChannelOpen(process_id);
|
impl->SetNVDECActive(is_nvdec_active);
|
||||||
}
|
|
||||||
|
|
||||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
impl->NotifyNVDECChannelClose(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
return impl->IsNVDECActiveForProcess(process_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -191,10 +191,8 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id);
|
void SetNVDECActive(bool is_nvdec_active);
|
||||||
void NotifyNVDECChannelClose(u64 process_id);
|
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
|
|||||||
@@ -227,13 +227,12 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
|
|||||||
std::string src_path(Common::FS::SanitizePath(src_path_));
|
std::string src_path(Common::FS::SanitizePath(src_path_));
|
||||||
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
std::string dest_path(Common::FS::SanitizePath(dest_path_));
|
||||||
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
auto src = GetDirectoryRelativeWrapped(backing, src_path);
|
||||||
if (src == nullptr)
|
|
||||||
return FileSys::ResultPathNotFound;
|
|
||||||
|
|
||||||
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) {
|
||||||
std::string full_src_path = backing->GetFullPath() + "/" + src_path;
|
// Use more-optimized vfs implementation rename.
|
||||||
std::string full_dest_path = backing->GetFullPath() + "/" + dest_path;
|
if (src == nullptr)
|
||||||
if (!Common::FS::RenameDir(full_src_path, full_dest_path)) {
|
return FileSys::ResultPathNotFound;
|
||||||
|
if (!src->Rename(Common::FS::GetFilename(dest_path))) {
|
||||||
|
// TODO(DarkLordZach): Find a better error code for this
|
||||||
return ResultUnknown;
|
return ResultUnknown;
|
||||||
}
|
}
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
|
|||||||
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
|
||||||
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
|
||||||
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
|
||||||
{6, D<&IFileSystem::RenameDirectory>, "RenameDirectory"},
|
{6, nullptr, "RenameDirectory"},
|
||||||
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
|
||||||
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
|
||||||
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
|
||||||
@@ -88,14 +88,6 @@ Result IFileSystem::RenameFile(
|
|||||||
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IFileSystem::RenameDirectory(
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
|
|
||||||
LOG_DEBUG(Service_FS, "called. directory '{}' to directory '{}'", old_path->str, new_path->str);
|
|
||||||
|
|
||||||
R_RETURN(backend->RenameDirectory(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
|
||||||
u32 mode) {
|
u32 mode) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -39,8 +36,6 @@ public:
|
|||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
|
||||||
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
||||||
Result RenameDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
|
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
|
|
||||||
Result OpenFile(OutInterface<IFile> out_interface,
|
Result OpenFile(OutInterface<IFile> out_interface,
|
||||||
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
|
||||||
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
Result OpenDirectory(OutInterface<IDirectory> out_interface,
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
|
||||||
#include "core/hle/service/nvdrv/core/container.h"
|
#include "core/hle/service/nvdrv/core/container.h"
|
||||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||||
@@ -72,23 +71,17 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
|||||||
|
|
||||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||||
|
system.SetNVDECActive(true);
|
||||||
sessions[fd] = session_id;
|
sessions[fd] = session_id;
|
||||||
if (const auto* session = core.GetSession(session_id);
|
|
||||||
session != nullptr && session->process != nullptr) {
|
|
||||||
system.NotifyNVDECChannelOpen(session->process->GetId());
|
|
||||||
}
|
|
||||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||||
|
system.SetNVDECActive(false);
|
||||||
auto it = sessions.find(fd);
|
auto it = sessions.find(fd);
|
||||||
if (it != sessions.end()) {
|
if (it != sessions.end()) {
|
||||||
if (const auto* session = core.GetSession(it->second);
|
|
||||||
session != nullptr && session->process != nullptr) {
|
|
||||||
system.NotifyNVDECChannelClose(session->process->GetId());
|
|
||||||
}
|
|
||||||
sessions.erase(it);
|
sessions.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,12 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
#include <string_view>
|
|
||||||
#include <thread>
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc.h"
|
#include "core/hle/ipc.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
@@ -37,7 +33,6 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
|||||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||||
, system{system_}
|
, system{system_}
|
||||||
, service_name{service_name_}
|
, service_name{service_name_}
|
||||||
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
|
||||||
, handler_invoker{handler_invoker_}
|
, handler_invoker{handler_invoker_}
|
||||||
, max_sessions{max_sessions_}
|
, max_sessions{max_sessions_}
|
||||||
{}
|
{}
|
||||||
@@ -82,22 +77,13 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||||
const auto command = ctx.GetCommand();
|
auto it = handlers.find(ctx.GetCommand());
|
||||||
auto it = handlers.find(command);
|
|
||||||
const bool is_cmd_read = command == 0;
|
|
||||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||||
if (info == nullptr || info->handler_callback == nullptr)
|
if (info == nullptr || info->handler_callback == nullptr)
|
||||||
return ReportUnimplementedFunction(ctx, info);
|
return ReportUnimplementedFunction(ctx, info);
|
||||||
|
|
||||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||||
handler_invoker(this, info->handler_callback, ctx);
|
handler_invoker(this, info->handler_callback, ctx);
|
||||||
|
|
||||||
if (is_i_storage && is_cmd_read) {
|
|
||||||
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
|
||||||
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||||
|
|||||||
@@ -107,8 +107,6 @@ protected:
|
|||||||
Core::System& system;
|
Core::System& system;
|
||||||
/// Identifier string used to connect to the service.
|
/// Identifier string used to connect to the service.
|
||||||
const char* service_name;
|
const char* service_name;
|
||||||
/// Whether this is the IStorage service.
|
|
||||||
const bool is_i_storage;
|
|
||||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||||
InvokerFn* handler_invoker;
|
InvokerFn* handler_invoker;
|
||||||
/// Maximum number of concurrent sessions that this service can handle.
|
/// Maximum number of concurrent sessions that this service can handle.
|
||||||
|
|||||||
Reference in New Issue
Block a user