Compare commits

..

2 Commits

Author SHA1 Message Date
Mike Lothian
23e074ff14 Simplify VkResult lookup 2024-01-22 03:10:43 +00:00
Mike Lothian
f854ffd015 Add Vulkan-Utility-Libraries dependency 2024-01-22 01:30:44 +00:00
309 changed files with 2778 additions and 7996 deletions

View File

@@ -3,35 +3,38 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
shopt -s nullglob globstar
if git grep -nrI '\s$' src **/*.yml **/*.txt **/*.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop dist/*.svg dist/*.xml; then
if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dist/*.desktop \
dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting
exit 1
fi
# Default clang-format points to default 3.5 version one
CLANG_FORMAT="${CLANG_FORMAT:-clang-format-15}"
"$CLANG_FORMAT" --version
CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15}
$CLANG_FORMAT --version
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
# Get list of every file modified in this pull request
files_to_lint="$(git diff --name-only --diff-filter=ACMRTUXB $TRAVIS_COMMIT_RANGE | grep '^src/[^.]*[.]\(cpp\|h\)$' || true)"
else
# Check everything for branch pushes
files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')"
fi
# Turn off tracing for this because it's too verbose
set +x
# Check everything for branch pushes
FILES_TO_LINT="$(find src/ -name '*.cpp' -or -name '*.h')"
for f in $FILES_TO_LINT; do
echo "$f"
"$CLANG_FORMAT" -i "$f"
for f in $files_to_lint; do
d=$(diff -u "$f" <($CLANG_FORMAT "$f") || true)
if ! [ -z "$d" ]; then
echo "!!! $f not compliant to coding style, here is the fix:"
echo "$d"
fail=1
fi
done
DIFF=$(git -c core.fileMode=false diff)
set -x
if [ ! -z "$DIFF" ]; then
echo "!!! Not compliant to coding style, here is the fix:"
echo "$DIFF"
if [ "$fail" = 1 ]; then
exit 1
fi
cd src/android
./gradlew ktlintCheck

View File

@@ -9,7 +9,7 @@ chmod a+x ./.ci/scripts/linux/docker.sh
sudo chown -R 1027 ./
# The environment variables listed below:
# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
# AZURECIREPO TITLEBARFORMATIDLE TITLEBARFORMATRUNNING DISPLAYVERSION
# are requested in src/common/CMakeLists.txt and appear to be provided somewhere in Azure DevOps
docker run -e AZURECIREPO -e TITLEBARFORMATIDLE -e TITLEBARFORMATRUNNING -e DISPLAYVERSION -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"

View File

@@ -8,7 +8,17 @@ variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- job: build

View File

@@ -10,7 +10,7 @@ const CHANGE_LABEL = 'android-merge';
// how far back in time should we consider the changes are "recent"? (default: 24 hours)
const DETECTION_TIME_FRAME = (parseInt(process.env.DETECTION_TIME_FRAME)) || (24 * 3600 * 1000);
async function checkBaseChanges(github) {
async function checkBaseChanges(github, context) {
// query the commit date of the latest commit on this branch
const query = `query($owner:String!, $name:String!, $ref:String!) {
repository(name:$name, owner:$owner) {
@@ -22,8 +22,8 @@ async function checkBaseChanges(github) {
}
}`;
const variables = {
owner: 'yuzu-emu',
name: 'yuzu',
owner: context.repo.owner,
name: context.repo.repo,
ref: 'refs/heads/master',
};
const result = await github.graphql(query, variables);
@@ -38,8 +38,8 @@ async function checkBaseChanges(github) {
return false;
}
async function checkAndroidChanges(github) {
if (checkBaseChanges(github)) return true;
async function checkAndroidChanges(github, context) {
if (checkBaseChanges(github, context)) return true;
const query = `query($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) {
@@ -48,8 +48,8 @@ async function checkAndroidChanges(github) {
}
}`;
const variables = {
owner: 'yuzu-emu',
name: 'yuzu',
owner: context.repo.owner,
name: context.repo.repo,
label: CHANGE_LABEL,
};
const result = await github.graphql(query, variables);
@@ -90,8 +90,8 @@ async function tagAndPush(github, owner, repo, execa, commit=false) {
console.log(`New tag: ${newTag}`);
if (commit) {
let channelName = channel[0].toUpperCase() + channel.slice(1);
console.info(`Committing pending commit as ${channelName} ${tagNumber + 1}`);
await execa("git", ['commit', '-m', `${channelName} ${tagNumber + 1}`]);
console.info(`Committing pending commit as ${channelName} #${tagNumber + 1}`);
await execa("git", ['commit', '-m', `${channelName} #${tagNumber + 1}`]);
}
console.info('Pushing tags to GitHub ...');
await execa("git", ['tag', newTag]);
@@ -157,7 +157,7 @@ async function mergePullRequests(pulls, execa) {
process1.stdout.pipe(process.stdout);
await process1;
const process2 = execa("git", ["commit", "-m", `Merge yuzu-emu#${pr}`]);
const process2 = execa("git", ["commit", "-m", `Merge PR ${pr}`]);
process2.stdout.pipe(process.stdout);
await process2;
@@ -182,30 +182,7 @@ async function mergePullRequests(pulls, execa) {
return mergeResults;
}
async function resetBranch(execa) {
console.log("::group::Reset master branch");
let hasFailed = false;
try {
await execa("git", ["remote", "add", "source", "https://github.com/yuzu-emu/yuzu.git"]);
await execa("git", ["fetch", "source"]);
const process1 = await execa("git", ["rev-parse", "source/master"]);
const headCommit = process1.stdout;
await execa("git", ["reset", "--hard", headCommit]);
} catch (err) {
console.log(`::error title=Failed to reset master branch`);
hasFailed = true;
}
console.log("::endgroup::");
if (hasFailed) {
throw 'Failed to reset the master branch. Aborting!';
}
}
async function mergebot(github, context, execa) {
// Reset our local copy of master to what appears on yuzu-emu/yuzu - master
await resetBranch(execa);
const query = `query ($owner:String!, $name:String!, $label:String!) {
repository(name:$name, owner:$owner) {
pullRequests(labels: [$label], states: OPEN, first: 100) {
@@ -216,8 +193,8 @@ async function mergebot(github, context, execa) {
}
}`;
const variables = {
owner: 'yuzu-emu',
name: 'yuzu',
owner: context.repo.owner,
name: context.repo.repo,
label: CHANGE_LABEL,
};
const result = await github.graphql(query, variables);
@@ -232,7 +209,7 @@ async function mergebot(github, context, execa) {
await fetchPullRequests(pulls, "https://github.com/yuzu-emu/yuzu", execa);
const mergeResults = await mergePullRequests(pulls, execa);
await generateReadme(pulls, context, mergeResults, execa);
await tagAndPush(github, 'yuzu-emu', `yuzu-android`, execa, true);
await tagAndPush(github, context.repo.owner, `${context.repo.repo}-android`, execa, true);
}
module.exports.mergebot = mergebot;

View File

@@ -16,7 +16,7 @@ on:
jobs:
android:
runs-on: ubuntu-latest
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu-android' }}
if: ${{ github.event.inputs.android != 'false' && github.repository == 'yuzu-emu/yuzu' }}
steps:
# this checkout is required to make sure the GitHub Actions scripts are available
- uses: actions/checkout@v3
@@ -33,7 +33,7 @@ jobs:
script: |
if (context.payload.inputs && context.payload.inputs.android === 'true') return true;
const checkAndroidChanges = require('./.github/workflows/android-merge.js').checkAndroidChanges;
return checkAndroidChanges(github);
return checkAndroidChanges(github, context);
- run: npm install execa@5
if: ${{ steps.check-changes.outputs.result == 'true' }}
- uses: actions/checkout@v3

View File

@@ -13,15 +13,13 @@ jobs:
format:
name: 'verify format'
runs-on: ubuntu-latest
container:
image: yuzuemu/build-environments:linux-clang-format
options: -u 1001
steps:
- uses: actions/checkout@v3
with:
submodules: false
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: 'Verify Formatting'
run: bash -ex ./.ci/scripts/format/script.sh
build:

3
.gitmodules vendored
View File

@@ -64,3 +64,6 @@
[submodule "oaknut"]
path = externals/oaknut
url = https://github.com/merryhime/oaknut
[submodule "Vulkan-Utility-Libraries"]
path = externals/Vulkan-Utility-Libraries
url = https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git

View File

@@ -36,6 +36,8 @@ option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
option(YUZU_USE_EXTERNAL_VULKAN_HEADERS "Use Vulkan-Headers from externals" ON)
option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan-Utility-Libraries from externals" ON)
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
@@ -308,6 +310,10 @@ if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
find_package(Vulkan 1.3.274 REQUIRED)
endif()
if (NOT YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
find_package(VulkanUtilityLibraries REQUIRED)
endif()
if (ENABLE_LIBUSB)
find_package(libusb 1.0.24 MODULE)
endif()

View File

@@ -1,4 +1,4 @@
Copyright (c) <year> <owner>
Copyright (c) <year> <owner>
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View File

@@ -1,4 +1,4 @@
Copyright (c) <year> <owner>.
Copyright (c) <year> <owner>.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View File

@@ -35,7 +35,7 @@ Mozilla Public License Version 2.0
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"

View File

@@ -154,6 +154,11 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
add_subdirectory(Vulkan-Headers)
endif()
# Vulkan-Utility-Libraries
if (YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES)
add_subdirectory(Vulkan-Utility-Libraries)
endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)

View File

@@ -138,7 +138,7 @@ if (NOT WIN32 AND NOT ANDROID)
--cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android-
--sysroot=${SYSROOT}
--target-os=android
--extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
--extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld"
--extra-ldflags="-nostdlib"
)
endif()

View File

@@ -185,7 +185,6 @@ add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(audio_core)
add_subdirectory(video_core)
add_subdirectory(hid_core)
add_subdirectory(network)
add_subdirectory(input_common)
add_subdirectory(frontend_common)

View File

@@ -188,15 +188,8 @@ tasks.create<Delete>("ktlintReset") {
delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
}
val showFormatHelp = {
logger.lifecycle(
"If this check fails, please try running \"gradlew ktlintFormat\" for automatic " +
"codestyle fixes"
)
}
tasks.getByPath("ktlintKotlinScriptCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("ktlintMainSourceSetCheck").doFirst { showFormatHelp.invoke() }
tasks.getByPath("loadKtlintReporters").dependsOn("ktlintReset")
tasks.getByPath("preBuild").dependsOn("ktlintCheck")
ktlint {
version.set("0.47.1")
@@ -235,33 +228,71 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
fun runGitCommand(command: List<String>): String {
return try {
ProcessBuilder(command)
fun getGitVersion(): String {
var versionName = "0.0"
try {
versionName = ProcessBuilder("git", "describe", "--always", "--long")
.directory(project.rootDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start().inputStream.bufferedReader().use { it.readText() }
.trim()
} catch (e: Exception) {
logger.error("Cannot find git")
""
}
}
fun getGitVersion(): String {
val versionName = if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME") ?: ""
gitTag
} else {
runGitCommand(listOf("git", "describe", "--always", "--long"))
.replace(Regex("(-0)?-[^-]+$"), "")
} catch (e: Exception) {
logger.error("Cannot find git, defaulting to dummy version number")
}
return versionName.ifEmpty { "0.0" }
if (System.getenv("GITHUB_ACTIONS") != null) {
val gitTag = System.getenv("GIT_TAG_NAME")
versionName = gitTag ?: versionName
}
return versionName
}
fun getGitHash(): String =
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
fun getGitHash(): String {
try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--short", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
fun getBranch(): String =
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
return if (process.exitValue() == 0) {
inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}
fun getBranch(): String {
try {
val processBuilder = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
processBuilder.directory(project.rootDir)
val process = processBuilder.start()
val inputStream = process.inputStream
val errorStream = process.errorStream
process.waitFor()
return if (process.exitValue() == 0) {
inputStream.bufferedReader()
.use { it.readText().trim() } // return the value of gitHash
} else {
val errorMessage = errorStream.bufferedReader().use { it.readText().trim() }
logger.error("Error running git command: $errorMessage")
"dummy-hash" // return a dummy hash value in case of an error
}
} catch (e: Exception) {
logger.error("$e: Cannot find git, defaulting to dummy build hash")
return "dummy-hash" // return a dummy hash value in case of an error
}
}

View File

@@ -31,9 +31,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true">
<meta-data android:name="android.game_mode_config"
android:resource="@xml/game_mode_config" />
<activity
android:name="org.yuzu.yuzu_emu.ui.main.MainActivity"
android:exported="true"

View File

@@ -547,15 +547,6 @@ object NativeLibrary {
*/
external fun getSavePath(programId: String): String
/**
* Gets the root save directory for the default profile as either
* /user/save/account/<user id raw string> or /user/save/000...000/<user id>
*
* @param future If true, returns the /user/save/account/... directory
* @return Save data path that may not exist yet
*/
external fun getDefaultProfileSaveDataRoot(future: Boolean): String
/**
* Adds a file to the manual filesystem provider in our EmulationSession instance
* @param path Path to the file we're adding. Can be a string representation of a [Uri] or

View File

@@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import androidx.recyclerview.widget.RecyclerView
/**
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
* code used in every [RecyclerView].
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
*/
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem === newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
return oldItem == newItem
}
}
}

View File

@@ -1,98 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of basic lists
* @param currentList The list to show initially
*/
abstract class AbstractListAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
open var currentList: List<Model>
) : RecyclerView.Adapter<Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Adds an item to [currentList] and notifies the underlying adapter of the change. If no parameter
* is passed in for position, [item] is added to the end of the list. Invokes [callback] last.
* @param item The item to add to the list
* @param position Index where [item] will be added
* @param callback Lambda that's called at the end of the list changes and has the added list
* position passed in as a parameter
*/
open fun addItem(item: Model, position: Int = -1, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
val positionToUpdate: Int
if (position == -1) {
newList.add(item)
currentList = newList
positionToUpdate = currentList.size - 1
} else {
newList.add(position, item)
currentList = newList
positionToUpdate = position
}
onItemAdded(positionToUpdate, callback)
}
protected fun onItemAdded(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemInserted(position)
callback?.invoke(position)
}
/**
* Replaces the [item] at [position] in the [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param item New list item
* @param position Index where [item] will replace the existing list item
* @param callback Lambda that's called at the end of the list changes and has the changed list
* position passed in as a parameter
*/
fun changeItem(item: Model, position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList[position] = item
currentList = newList
onItemChanged(position, callback)
}
protected fun onItemChanged(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemChanged(position)
callback?.invoke(position)
}
/**
* Removes the list item at [position] in [currentList] and notifies the underlying adapter
* of the change. Invokes [callback] last.
* @param position Index where the list item will be removed
* @param callback Lambda that's called at the end of the list changes and has the removed list
* position passed in as a parameter
*/
fun removeItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
val newList = currentList.toMutableList()
newList.removeAt(position)
currentList = newList
onItemRemoved(position, callback)
}
protected fun onItemRemoved(position: Int, callback: ((Int) -> Unit)? = null) {
notifyItemRemoved(position)
callback?.invoke(position)
}
/**
* Replaces [currentList] with [newList] and notifies the underlying adapter of the change.
* @param newList The new list to replace [currentList]
*/
@SuppressLint("NotifyDataSetChanged")
open fun replaceList(newList: List<Model>) {
currentList = newList
notifyDataSetChanged()
}
}

View File

@@ -1,105 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import org.yuzu.yuzu_emu.model.SelectableItem
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
/**
* Generic list class meant to take care of single selection UI updates
* @param currentList The list to show initially
* @param defaultSelection The default selection to use if no list items are selected by
* [SelectableItem.selected] or if the currently selected item is removed from the list
*/
abstract class AbstractSingleSelectionList<
Model : SelectableItem,
Holder : AbstractViewHolder<Model>
>(
final override var currentList: List<Model>,
private val defaultSelection: DefaultSelection = DefaultSelection.Start
) : AbstractListAdapter<Model, Holder>(currentList) {
var selectedItem = getDefaultSelection()
init {
findSelectedItem()
}
/**
* Changes the selection state of the [SelectableItem] that was selected and the previously selected
* item and notifies the underlying adapter of the change for those items. Invokes [callback] last.
* Does nothing if [position] is the same as the currently selected item.
* @param position Index of the item that was selected
* @param callback Lambda that's called at the end of the list changes and has the selected list
* position passed in as a parameter
*/
fun selectItem(position: Int, callback: ((position: Int) -> Unit)? = null) {
if (position == selectedItem) {
return
}
val previouslySelectedItem = selectedItem
selectedItem = position
if (currentList.indices.contains(selectedItem)) {
currentList[selectedItem].onSelectionStateChanged(true)
}
if (currentList.indices.contains(previouslySelectedItem)) {
currentList[previouslySelectedItem].onSelectionStateChanged(false)
}
onItemChanged(previouslySelectedItem)
onItemChanged(selectedItem)
callback?.invoke(position)
}
/**
* Removes a given item from the list and notifies the underlying adapter of the change. If the
* currently selected item was the item that was removed, the item at the position provided
* by [defaultSelection] will be made the new selection. Invokes [callback] last.
* @param position Index of the item that was removed
* @param callback Lambda that's called at the end of the list changes and has the removed and
* selected list positions passed in as parameters
*/
fun removeSelectableItem(
position: Int,
callback: ((removedPosition: Int, selectedPosition: Int) -> Unit)?
) {
removeItem(position)
if (position == selectedItem) {
selectedItem = getDefaultSelection()
currentList[selectedItem].onSelectionStateChanged(true)
onItemChanged(selectedItem)
} else if (position < selectedItem) {
selectedItem--
}
callback?.invoke(position, selectedItem)
}
override fun addItem(item: Model, position: Int, callback: ((Int) -> Unit)?) {
super.addItem(item, position, callback)
if (position <= selectedItem && position != -1) {
selectedItem++
}
}
override fun replaceList(newList: List<Model>) {
super.replaceList(newList)
findSelectedItem()
}
private fun findSelectedItem() {
for (i in currentList.indices) {
if (currentList[i].selected) {
selectedItem = i
break
}
}
}
private fun getDefaultSelection(): Int =
when (defaultSelection) {
DefaultSelection.Start -> currentList.indices.first
DefaultSelection.End -> currentList.indices.last
}
enum class DefaultSelection { Start, End }
}

View File

@@ -5,28 +5,48 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
import org.yuzu.yuzu_emu.model.Addon
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return AddonViewHolder(it) }
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
holder.bind(currentList[position])
inner class AddonViewHolder(val binding: ListItemAddonBinding) :
AbstractViewHolder<Addon>(binding) {
override fun bind(model: Addon) {
RecyclerView.ViewHolder(binding.root) {
fun bind(addon: Addon) {
binding.root.setOnClickListener {
binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
}
binding.title.text = model.title
binding.version.text = model.version
binding.title.text = addon.title
binding.version.text = addon.version
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
model.enabled = checked
addon.enabled = checked
}
binding.addonSwitch.isChecked = model.enabled
binding.addonSwitch.isChecked = addon.enabled
}
}
private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
return oldItem == newItem
}
}
}

View File

@@ -4,11 +4,13 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -17,58 +19,72 @@ import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
AbstractListAdapter<Applet, AppletAdapter.AppletViewHolder>(applets) {
class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AppletAdapter.AppletViewHolder {
CardSimpleOutlinedBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@AppletAdapter) }
.also { return AppletViewHolder(it) }
}
override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
holder.bind(applets[position])
override fun getItemCount(): Int = applets.size
override fun onClick(view: View) {
val applet = (view.tag as AppletViewHolder).applet
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
view.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
view.findNavController().navigate(action)
}
inner class AppletViewHolder(val binding: CardSimpleOutlinedBinding) :
AbstractViewHolder<Applet>(binding) {
override fun bind(model: Applet) {
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
RecyclerView.ViewHolder(binding.root) {
lateinit var applet: Applet
init {
itemView.tag = this
}
fun bind(applet: Applet) {
this.applet = applet
binding.title.setText(applet.titleId)
binding.description.setText(applet.descriptionId)
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
model.iconId,
applet.iconId,
binding.icon.context.theme
)
)
binding.root.setOnClickListener { onClick(model) }
}
fun onClick(applet: Applet) {
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
binding.root.context,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
binding.root.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
binding.root.findNavController().navigate(action)
}
}
}

View File

@@ -4,10 +4,12 @@
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -17,43 +19,54 @@ import org.yuzu.yuzu_emu.model.CabinetMode
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class CabinetLauncherDialogAdapter(val fragment: Fragment) :
AbstractListAdapter<CabinetMode, CabinetModeViewHolder>(
CabinetMode.values().copyOfRange(1, CabinetMode.entries.size).toList()
) {
RecyclerView.Adapter<CabinetModeViewHolder>(),
View.OnClickListener {
private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
.also { return CabinetModeViewHolder(it) }
}
override fun getItemCount(): Int = cabinetModes.size
override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
holder.bind(cabinetModes[position])
override fun onClick(view: View) {
val mode = (view.tag as CabinetModeViewHolder).cabinetMode
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
}
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
AbstractViewHolder<CabinetMode>(binding) {
override fun bind(model: CabinetMode) {
RecyclerView.ViewHolder(binding.root) {
lateinit var cabinetMode: CabinetMode
init {
itemView.tag = this
}
fun bind(cabinetMode: CabinetMode) {
this.cabinetMode = cabinetMode
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
model.iconId,
cabinetMode.iconId,
binding.icon.context.theme
)
)
binding.title.setText(model.titleId)
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(mode: CabinetMode) {
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
binding.title.setText(cabinetMode.titleId)
}
}
}

View File

@@ -7,39 +7,65 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) :
AbstractSingleSelectionList<Driver, DriverAdapter.DriverViewHolder>(
driverViewModel.driverList.value
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return DriverViewHolder(it) }
val binding =
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DriverViewHolder(binding)
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
holder.bind(currentList[position])
private fun onSelectDriver(position: Int) {
driverViewModel.setSelectedDriverIndex(position)
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
}
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
if (driverViewModel.selectedDriver > position) {
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
}
if (GpuDriverHelper.customDriverSettingData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
}
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
AbstractViewHolder<Driver>(binding) {
override fun bind(model: Driver) {
RecyclerView.ViewHolder(binding.root) {
private lateinit var driverData: Pair<String, GpuDriverMetadata>
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
this.driverData = driverData
val driver = driverData.second
binding.apply {
radioButton.isChecked = model.selected
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
root.setOnClickListener {
selectItem(bindingAdapterPosition) {
driverViewModel.onDriverSelected(it)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
onSelectDriver(bindingAdapterPosition)
}
buttonDelete.setOnClickListener {
removeSelectableItem(
bindingAdapterPosition
) { removedPosition: Int, selectedPosition: Int ->
driverViewModel.onDriverRemoved(removedPosition, selectedPosition)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
}
onDeleteDriver(driverData, bindingAdapterPosition)
}
// Delay marquee by 3s
@@ -54,19 +80,38 @@ class DriverAdapter(private val driverViewModel: DriverViewModel) :
},
3000
)
title.text = model.title
version.text = model.version
description.text = model.description
if (model.description.isNotEmpty()) {
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
} else {
if (driver.name == null) {
title.setText(R.string.system_gpu_driver)
description.text = ""
version.text = ""
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
} else {
title.text = driver.name
version.text = driver.version
description.text = driver.description
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
}
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
override fun areItemsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.first == newItem.first
}
override fun areContentsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.second == newItem.second
}
}
}

View File

@@ -8,14 +8,19 @@ import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() {
ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
@@ -24,11 +29,18 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
.also { return FolderViewHolder(it) }
}
override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
holder.bind(currentList[position])
inner class FolderViewHolder(val binding: CardFolderBinding) :
AbstractViewHolder<GameDir>(binding) {
override fun bind(model: GameDir) {
RecyclerView.ViewHolder(binding.root) {
private lateinit var gameDir: GameDir
fun bind(gameDir: GameDir) {
this.gameDir = gameDir
binding.apply {
path.text = Uri.parse(model.uriString).path
path.text = Uri.parse(gameDir.uriString).path
path.postDelayed(
{
path.isSelected = true
@@ -38,7 +50,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
)
buttonEdit.setOnClickListener {
GameFolderPropertiesDialogFragment.newInstance(model)
GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
.show(
activity.supportFragmentManager,
GameFolderPropertiesDialogFragment.TAG
@@ -46,9 +58,19 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
}
buttonDelete.setOnClickListener {
gamesViewModel.removeFolder(model)
gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
}
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
return oldItem == newItem
}
}
}

View File

@@ -9,6 +9,7 @@ import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
@@ -24,6 +25,10 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -31,26 +36,122 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GameAdapter(private val activity: AppCompatActivity) :
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
View.OnClickListener,
View.OnLongClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return GameViewHolder(it) }
// Create a new view.
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.cardGame.setOnClickListener(this)
binding.cardGame.setOnLongClickListener(this)
// Use that view to create a ViewHolder.
return GameViewHolder(binding)
}
override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
holder.bind(currentList[position])
override fun getItemCount(): Int = currentList.size
/**
* Launches the game that was clicked on.
*
* @param view The card representing the game the user wants to play.
*/
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(holder.game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
holder.game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(holder.game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, holder.game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
.setShortLabel(holder.game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
view.findNavController().navigate(action)
}
override fun onLongClick(view: View): Boolean {
val holder = view.tag as GameViewHolder
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
view.findNavController().navigate(action)
return true
}
inner class GameViewHolder(val binding: CardGameBinding) :
AbstractViewHolder<Game>(binding) {
override fun bind(model: Game) {
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
RecyclerView.ViewHolder(binding.root) {
lateinit var game: Game
binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
init {
binding.cardGame.tag = this
}
fun bind(game: Game) {
this.game = game
binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
binding.textGameTitle.postDelayed(
{
@@ -59,79 +160,16 @@ class GameAdapter(private val activity: AppCompatActivity) :
},
3000
)
}
}
binding.cardGame.setOnClickListener { onClick(model) }
binding.cardGame.setOnLongClickListener { onLongClick(model) }
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem == newItem
}
fun onClick(game: Game) {
val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
R.string.loader_error_file_not_found,
Toast.LENGTH_LONG
).show()
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
return
}
val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
preferences.edit()
.putLong(
game.keyLastPlayedTime,
System.currentTimeMillis()
)
.apply()
val openIntent =
Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = Uri.parse(game.path)
}
activity.lifecycleScope.launch {
withContext(Dispatchers.IO) {
val layerDrawable = ResourcesCompat.getDrawable(
YuzuApplication.appContext.resources,
R.drawable.shortcut,
null
) as LayerDrawable
layerDrawable.setDrawableByLayerId(
R.id.shortcut_foreground,
GameIconUtils.getGameIcon(activity, game)
.toDrawable(YuzuApplication.appContext.resources)
)
val inset = YuzuApplication.appContext.resources
.getDimensionPixelSize(R.dimen.icon_inset)
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
val shortcut =
ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
.setShortLabel(game.title)
.setIcon(
IconCompat.createWithAdaptiveBitmap(
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
)
)
.setIntent(openIntent)
.build()
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
}
}
val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
binding.root.findNavController().navigate(action)
}
fun onLongClick(game: Game): Boolean {
val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
binding.root.findNavController().navigate(action)
return true
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem == newItem
}
}
}

View File

@@ -12,22 +12,23 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty
import org.yuzu.yuzu_emu.model.SubmenuProperty
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class GamePropertiesAdapter(
private val viewLifecycle: LifecycleOwner,
private var properties: List<GameProperty>
) : AbstractListAdapter<GameProperty, AbstractViewHolder<GameProperty>>(properties) {
) :
RecyclerView.Adapter<GamePropertiesAdapter.GamePropertyViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AbstractViewHolder<GameProperty> {
): GamePropertyViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
PropertyType.Submenu.ordinal -> {
@@ -50,6 +51,11 @@ class GamePropertiesAdapter(
}
}
override fun getItemCount(): Int = properties.size
override fun onBindViewHolder(holder: GamePropertyViewHolder, position: Int) =
holder.bind(properties[position])
override fun getItemViewType(position: Int): Int {
return when (properties[position]) {
is SubmenuProperty -> PropertyType.Submenu.ordinal
@@ -57,10 +63,14 @@ class GamePropertiesAdapter(
}
}
sealed class GamePropertyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(property: GameProperty)
}
inner class SubmenuPropertyViewHolder(val binding: CardSimpleOutlinedBinding) :
AbstractViewHolder<GameProperty>(binding) {
override fun bind(model: GameProperty) {
val submenuProperty = model as SubmenuProperty
GamePropertyViewHolder(binding.root) {
override fun bind(property: GameProperty) {
val submenuProperty = property as SubmenuProperty
binding.root.setOnClickListener {
submenuProperty.action.invoke()
@@ -98,9 +108,9 @@ class GamePropertiesAdapter(
}
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
AbstractViewHolder<GameProperty>(binding) {
override fun bind(model: GameProperty) {
val installableProperty = model as InstallableProperty
GamePropertyViewHolder(binding.root) {
override fun bind(property: GameProperty) {
val installableProperty = property as InstallableProperty
binding.title.setText(installableProperty.titleId)
binding.description.setText(installableProperty.descriptionId)

View File

@@ -14,37 +14,69 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class HomeSettingAdapter(
private val activity: AppCompatActivity,
private val viewLifecycle: LifecycleOwner,
options: List<HomeSetting>
) : AbstractListAdapter<HomeSetting, HomeSettingAdapter.HomeOptionViewHolder>(options) {
var options: List<HomeSetting>
) :
RecyclerView.Adapter<HomeSettingAdapter.HomeOptionViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeOptionViewHolder {
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return HomeOptionViewHolder(it) }
val binding =
CardHomeOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return HomeOptionViewHolder(binding)
}
override fun getItemCount(): Int {
return options.size
}
override fun onBindViewHolder(holder: HomeOptionViewHolder, position: Int) {
holder.bind(options[position])
}
override fun onClick(view: View) {
val holder = view.tag as HomeOptionViewHolder
if (holder.option.isEnabled.invoke()) {
holder.option.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = holder.option.disabledTitleId,
descriptionId = holder.option.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
}
inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) :
AbstractViewHolder<HomeSetting>(binding) {
override fun bind(model: HomeSetting) {
binding.optionTitle.text = activity.resources.getString(model.titleId)
binding.optionDescription.text = activity.resources.getString(model.descriptionId)
RecyclerView.ViewHolder(binding.root) {
lateinit var option: HomeSetting
init {
itemView.tag = this
}
fun bind(option: HomeSetting) {
this.option = option
binding.optionTitle.text = activity.resources.getString(option.titleId)
binding.optionDescription.text = activity.resources.getString(option.descriptionId)
binding.optionIcon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
model.iconId,
option.iconId,
activity.theme
)
)
when (model.titleId) {
when (option.titleId) {
R.string.get_early_access ->
binding.optionLayout.background =
ContextCompat.getDrawable(
@@ -53,7 +85,7 @@ class HomeSettingAdapter(
)
}
if (!model.isEnabled.invoke()) {
if (!option.isEnabled.invoke()) {
binding.optionTitle.alpha = 0.5f
binding.optionDescription.alpha = 0.5f
binding.optionIcon.alpha = 0.5f
@@ -61,7 +93,7 @@ class HomeSettingAdapter(
viewLifecycle.lifecycleScope.launch {
viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
model.details.collect { updateOptionDetails(it) }
option.details.collect { updateOptionDetails(it) }
}
}
binding.optionDetail.postDelayed(
@@ -71,20 +103,6 @@ class HomeSettingAdapter(
},
3000
)
binding.root.setOnClickListener { onClick(model) }
}
private fun onClick(model: HomeSetting) {
if (model.isEnabled.invoke()) {
model.onClick.invoke()
} else {
MessageDialogFragment.newInstance(
activity,
titleId = model.disabledTitleId,
descriptionId = model.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
}
}
private fun updateOptionDetails(detailString: String) {

View File

@@ -6,33 +6,43 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class InstallableAdapter(installables: List<Installable>) :
AbstractListAdapter<Installable, InstallableAdapter.InstallableViewHolder>(installables) {
class InstallableAdapter(private val installables: List<Installable>) :
RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): InstallableAdapter.InstallableViewHolder {
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return InstallableViewHolder(it) }
val binding =
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return InstallableViewHolder(binding)
}
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
AbstractViewHolder<Installable>(binding) {
override fun bind(model: Installable) {
binding.title.setText(model.titleId)
binding.description.setText(model.descriptionId)
override fun getItemCount(): Int = installables.size
if (model.install != null) {
override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
holder.bind(installables[position])
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var installable: Installable
fun bind(installable: Installable) {
this.installable = installable
binding.title.setText(installable.titleId)
binding.description.setText(installable.descriptionId)
if (installable.install != null) {
binding.buttonInstall.visibility = View.VISIBLE
binding.buttonInstall.setOnClickListener { model.install.invoke() }
binding.buttonInstall.setOnClickListener { installable.install.invoke() }
}
if (model.export != null) {
if (installable.export != null) {
binding.buttonExport.visibility = View.VISIBLE
binding.buttonExport.setOnClickListener { model.export.invoke() }
binding.buttonExport.setOnClickListener { installable.export.invoke() }
}
}
}

View File

@@ -7,33 +7,49 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class LicenseAdapter(private val activity: AppCompatActivity, licenses: List<License>) :
AbstractListAdapter<License, LicenseAdapter.LicenseViewHolder>(licenses) {
class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) :
RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return LicenseViewHolder(it) }
val binding =
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return LicenseViewHolder(binding)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) :
AbstractViewHolder<License>(binding) {
override fun bind(model: License) {
binding.apply {
textSettingName.text = root.context.getString(model.titleId)
textSettingDescription.text = root.context.getString(model.descriptionId)
textSettingValue.visibility = View.GONE
override fun getItemCount(): Int = licenses.size
root.setOnClickListener { onClick(model) }
}
override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) {
holder.bind(licenses[position])
}
override fun onClick(view: View) {
val license = (view.tag as LicenseViewHolder).license
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
lateinit var license: License
init {
itemView.tag = this
}
private fun onClick(license: License) {
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
fun bind(license: License) {
this.license = license
val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId)
binding.textSettingValue.visibility = View.GONE
}
}
}

View File

@@ -10,6 +10,7 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton
import org.yuzu.yuzu_emu.databinding.PageSetupBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
@@ -17,19 +18,31 @@ import org.yuzu.yuzu_emu.model.SetupCallback
import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.model.StepState
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
AbstractListAdapter<SetupPage, SetupAdapter.SetupPageViewHolder>(pages) {
class SetupAdapter(val activity: AppCompatActivity, val pages: List<SetupPage>) :
RecyclerView.Adapter<SetupAdapter.SetupPageViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SetupPageViewHolder {
PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.also { return SetupPageViewHolder(it) }
val binding = PageSetupBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SetupPageViewHolder(binding)
}
override fun getItemCount(): Int = pages.size
override fun onBindViewHolder(holder: SetupPageViewHolder, position: Int) =
holder.bind(pages[position])
inner class SetupPageViewHolder(val binding: PageSetupBinding) :
AbstractViewHolder<SetupPage>(binding), SetupCallback {
override fun bind(model: SetupPage) {
if (model.stepCompleted.invoke() == StepState.COMPLETE) {
RecyclerView.ViewHolder(binding.root), SetupCallback {
lateinit var page: SetupPage
init {
itemView.tag = this
}
fun bind(page: SetupPage) {
this.page = page
if (page.stepCompleted.invoke() == StepState.COMPLETE) {
binding.buttonAction.visibility = View.INVISIBLE
binding.textConfirmation.visibility = View.VISIBLE
}
@@ -37,31 +50,31 @@ class SetupAdapter(val activity: AppCompatActivity, pages: List<SetupPage>) :
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
activity.resources,
model.iconId,
page.iconId,
activity.theme
)
)
binding.textTitle.text = activity.resources.getString(model.titleId)
binding.textTitle.text = activity.resources.getString(page.titleId)
binding.textDescription.text =
Html.fromHtml(activity.resources.getString(model.descriptionId), 0)
Html.fromHtml(activity.resources.getString(page.descriptionId), 0)
binding.buttonAction.apply {
text = activity.resources.getString(model.buttonTextId)
if (model.buttonIconId != 0) {
text = activity.resources.getString(page.buttonTextId)
if (page.buttonIconId != 0) {
icon = ResourcesCompat.getDrawable(
activity.resources,
model.buttonIconId,
page.buttonIconId,
activity.theme
)
}
iconGravity =
if (model.leftAlignedIcon) {
if (page.leftAlignedIcon) {
MaterialButton.ICON_GRAVITY_START
} else {
MaterialButton.ICON_GRAVITY_END
}
setOnClickListener {
model.buttonAction.invoke(this@SetupPageViewHolder)
page.buttonAction.invoke(this@SetupPageViewHolder)
}
}
}

View File

@@ -79,18 +79,7 @@ object Settings {
const val PREF_THEME_MODE = "ThemeMode"
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
enum class EmulationOrientation(val int: Int) {
Unspecified(0),
SensorLandscape(5),
Landscape(1),
ReverseLandscape(2),
SensorPortrait(6),
Portrait(4),
ReversePortrait(3);
companion object {
fun from(int: Int): EmulationOrientation =
entries.firstOrNull { it.int == int } ?: Unspecified
}
}
const val LayoutOption_Unspecified = 0
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5
}

View File

@@ -76,8 +76,8 @@ class AboutFragment : Fragment() {
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
}
binding.textVersionName.text = BuildConfig.VERSION_NAME
binding.textVersionName.setOnClickListener {
binding.textBuildHash.text = BuildConfig.GIT_HASH
binding.buttonBuildHash.setOnClickListener {
val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)

View File

@@ -149,7 +149,7 @@ class AddonsFragment : Fragment() {
}
val isValid = externalAddonDirectory.listFiles()
.any { AddonUtil.validAddonDirectories.contains(it.name?.lowercase()) }
.any { AddonUtil.validAddonDirectories.contains(it.name) }
val errorMessage = MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.invalid_directory,

View File

@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -14,26 +13,20 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.File
import java.io.IOException
@@ -62,43 +55,12 @@ class DriverManagerFragment : Fragment() {
return binding.root
}
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.toolbarDrivers.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_driver_clear -> {
StringSetting.DRIVER_PATH.global = true
driverViewModel.updateDriverList()
(binding.listDrivers.adapter as DriverAdapter)
.replaceList(driverViewModel.driverList.value)
driverViewModel.showClearButton(false)
true
}
else -> false
}
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
driverViewModel.showClearButton.collect {
binding.toolbarDrivers.menu
.findItem(R.id.menu_driver_clear).isVisible = it
}
}
}
}
}
if (!driverViewModel.isInteractionAllowed.value) {
DriversLoadingDialogFragment().show(
@@ -123,6 +85,25 @@ class DriverManagerFragment : Fragment() {
adapter = DriverAdapter(driverViewModel)
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
driverViewModel.driverList.collectLatest {
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
}
}
launch {
driverViewModel.newDriverInstalled.collect {
if (_binding != null && it) {
(binding.listDrivers.adapter as DriverAdapter).apply {
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
driverViewModel.setNewDriverInstalled(false)
}
}
}
}
}
setInsets()
}
@@ -179,7 +160,7 @@ class DriverManagerFragment : Fragment() {
false
) {
val driverPath =
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip
@@ -196,21 +177,12 @@ class DriverManagerFragment : Fragment() {
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
driverViewModel.driverData.firstOrNull { it.second == driverData }
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
withContext(Dispatchers.Main) {
if (_binding != null) {
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
}
}
driverViewModel.addDriver(Pair(driverPath, driverData))
driverViewModel.setNewDriverInstalled(true)
}
return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)

View File

@@ -50,7 +50,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
@@ -100,7 +99,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateOrientation()
val intentUri: Uri? = requireActivity().intent.data
var intentGame: Game? = null
@@ -460,23 +458,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
val orientationSetting =
EmulationOrientation.from(IntSetting.RENDERER_SCREEN_LAYOUT.getInt())
it.requestedOrientation = when (orientationSetting) {
EmulationOrientation.Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
EmulationOrientation.SensorLandscape ->
it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.getInt()) {
Settings.LayoutOption_MobileLandscape ->
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
EmulationOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
EmulationOrientation.ReverseLandscape ->
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
EmulationOrientation.SensorPortrait ->
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
EmulationOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
EmulationOrientation.ReversePortrait ->
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
Settings.LayoutOption_MobilePortrait ->
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
else -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
}
}
}
@@ -663,7 +651,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@SuppressLint("SourceLockedOrientationActivity")
private fun startConfiguringControls() {
// Lock the current orientation to prevent editing inconsistencies
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
emulationActivity?.let {
it.requestedOrientation =
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -681,7 +669,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) {
emulationActivity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}

View File

@@ -445,8 +445,7 @@ class GamePropertiesFragment : Fragment() {
val zipResult = FileUtil.zipFromInternalStorage(
File(saveLocation),
saveLocation.replaceAfterLast("/", ""),
BufferedOutputStream(requireContext().contentResolver.openOutputStream(result)),
compression = false
BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)

View File

@@ -7,39 +7,20 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.math.BigInteger
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class InstallableFragment : Fragment() {
private var _binding: FragmentInstallablesBinding? = null
@@ -75,17 +56,6 @@ class InstallableFragment : Fragment() {
binding.root.findNavController().popBackStack()
}
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
}
}
}
}
val installables = listOf(
Installable(
R.string.user_data,
@@ -93,43 +63,6 @@ class InstallableFragment : Fragment() {
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
export = { mainActivity.exportUserData.launch("export.zip") }
),
Installable(
R.string.manage_save_data,
R.string.manage_save_data_description,
install = {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.import_save_warning,
descriptionId = R.string.import_save_warning_description,
positiveAction = { homeViewModel.setOpenImportSaves(true) }
).show(parentFragmentManager, MessageDialogFragment.TAG)
},
export = {
val oldSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
val futureSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (!oldSaveDataFolder.exists() && !futureSaveDataFolder.exists()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.no_save_data_found,
Toast.LENGTH_SHORT
).show()
return@Installable
} else {
exportSaves.launch(
"${getString(R.string.save_data)} " +
LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
)
)
}
}
),
Installable(
R.string.install_game_content,
R.string.install_game_content_description,
@@ -188,156 +121,4 @@ class InstallableFragment : Fragment() {
windowInsets
}
private val importSaves =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val inputZip = requireContext().contentResolver.openInputStream(result)
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
cacheSaveDir.mkdir()
if (inputZip == null) {
Toast.makeText(
YuzuApplication.appContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.save_files_importing,
false
) {
try {
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
val files = cacheSaveDir.listFiles()
var successfulImports = 0
var failedImports = 0
if (files != null) {
for (file in files) {
if (file.isDirectory) {
val baseSaveDir =
NativeLibrary.getSavePath(BigInteger(file.name, 16).toString())
if (baseSaveDir.isEmpty()) {
failedImports++
continue
}
val internalSaveFolder = File(
"${DirectoryInitialization.userDirectory}/nand$baseSaveDir"
)
internalSaveFolder.deleteRecursively()
internalSaveFolder.mkdir()
file.copyRecursively(target = internalSaveFolder, overwrite = true)
successfulImports++
}
}
}
withContext(Dispatchers.Main) {
if (successfulImports == 0) {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.save_file_invalid_zip_structure,
descriptionId = R.string.save_file_invalid_zip_structure_description
).show(parentFragmentManager, MessageDialogFragment.TAG)
return@withContext
}
val successString = if (failedImports > 0) {
"""
${
requireContext().resources.getQuantityString(
R.plurals.saves_import_success,
successfulImports,
successfulImports
)
}
${
requireContext().resources.getQuantityString(
R.plurals.saves_import_failed,
failedImports,
failedImports
)
}
"""
} else {
requireContext().resources.getQuantityString(
R.plurals.saves_import_success,
successfulImports,
successfulImports
)
}
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.import_complete,
descriptionString = successString
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
cacheSaveDir.deleteRecursively()
} catch (e: Exception) {
Toast.makeText(
YuzuApplication.appContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
}
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
private val exportSaves = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/zip")
) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.save_files_exporting,
false
) {
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
cacheSaveDir.mkdir()
val oldSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
if (oldSaveDataFolder.exists()) {
oldSaveDataFolder.copyRecursively(cacheSaveDir)
}
val futureSaveDataFolder = File(
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (futureSaveDataFolder.exists()) {
futureSaveDataFolder.copyRecursively(cacheSaveDir)
}
val saveFilesTotal = cacheSaveDir.listFiles()?.size ?: 0
if (saveFilesTotal == 0) {
cacheSaveDir.deleteRecursively()
return@newInstance getString(R.string.no_save_data_found)
}
val zipResult = FileUtil.zipFromInternalStorage(
cacheSaveDir,
cacheSaveDir.path,
BufferedOutputStream(requireContext().contentResolver.openOutputStream(result))
)
cacheSaveDir.deleteRecursively()
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.export_success)
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
}
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}

View File

@@ -1,27 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
data class Driver(
override var selected: Boolean,
val title: String,
val version: String = "",
val description: String = ""
) : SelectableItem {
override fun onSelectionStateChanged(selected: Boolean) {
this.selected = selected
}
companion object {
fun GpuDriverMetadata.toDriver(selected: Boolean = false): Driver =
Driver(
selected,
this.name ?: "",
this.version ?: "",
this.description ?: ""
)
}
}

View File

@@ -9,7 +9,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -18,10 +17,11 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.io.BufferedOutputStream
import java.io.File
class DriverViewModel : ViewModel() {
@@ -38,81 +38,97 @@ class DriverViewModel : ViewModel() {
!loading && ready && !deleting
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialValue = false)
var driverData = GpuDriverHelper.getDrivers()
private val _driverList = MutableStateFlow(GpuDriverHelper.getDrivers())
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
private val _driverList = MutableStateFlow(emptyList<Driver>())
val driverList: StateFlow<List<Driver>> get() = _driverList
var previouslySelectedDriver = 0
var selectedDriver = -1
// Used for showing which driver is currently installed within the driver manager card
private val _selectedDriverTitle = MutableStateFlow("")
val selectedDriverTitle: StateFlow<String> get() = _selectedDriverTitle
private val _showClearButton = MutableStateFlow(false)
val showClearButton = _showClearButton.asStateFlow()
private val _newDriverInstalled = MutableStateFlow(false)
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
private val driversToDelete = mutableListOf<String>()
val driversToDelete = mutableListOf<String>()
init {
updateDriverList()
val currentDriverMetadata = GpuDriverHelper.installedCustomDriverData
findSelectedDriver(currentDriverMetadata)
// If a user had installed a driver before the manager was implemented, this zips
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
// be indexed and exported as expected.
if (selectedDriver == -1) {
val driverToSave =
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
driverToSave.createNewFile()
FileUtil.zipFromInternalStorage(
File(GpuDriverHelper.driverInstallationPath!!),
GpuDriverHelper.driverInstallationPath!!,
BufferedOutputStream(driverToSave.outputStream())
)
_driverList.value.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(_driverList.value.size - 1)
}
// If a user had installed a driver before the config was reworked to be multiplatform,
// we have save the path of the previously selected driver to the new setting.
if (StringSetting.DRIVER_PATH.getString(true).isEmpty() && selectedDriver > 0 &&
StringSetting.DRIVER_PATH.global
) {
StringSetting.DRIVER_PATH.setString(_driverList.value[selectedDriver].first)
NativeConfig.saveGlobalConfig()
} else {
findSelectedDriver(GpuDriverHelper.customDriverSettingData)
}
updateDriverNameForGame(null)
}
fun reloadDriverData() {
_areDriversLoading.value = true
driverData = GpuDriverHelper.getDrivers()
updateDriverList()
_areDriversLoading.value = false
fun setSelectedDriverIndex(value: Int) {
if (selectedDriver != -1) {
previouslySelectedDriver = selectedDriver
}
selectedDriver = value
}
fun updateDriverList() {
val selectedDriver = GpuDriverHelper.customDriverSettingData
val newDriverList = mutableListOf(
Driver(
selectedDriver == GpuDriverMetadata(),
YuzuApplication.appContext.getString(R.string.system_gpu_driver)
)
)
driverData.forEach {
newDriverList.add(it.second.toDriver(it.second == selectedDriver))
fun setNewDriverInstalled(value: Boolean) {
_newDriverInstalled.value = value
}
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
if (driverIndex == -1) {
_driverList.value.add(driverData)
setSelectedDriverIndex(_driverList.value.size - 1)
_selectedDriverTitle.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
}
_driverList.value = newDriverList
}
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
}
fun onOpenDriverManager(game: Game?) {
if (game != null) {
SettingsFile.loadCustomConfig(game)
}
updateDriverList()
}
fun showClearButton(value: Boolean) {
_showClearButton.value = value
}
fun onDriverSelected(position: Int) {
if (position == 0) {
StringSetting.DRIVER_PATH.setString("")
val driverPath = StringSetting.DRIVER_PATH.getString()
if (driverPath.isEmpty()) {
setSelectedDriverIndex(0)
} else {
StringSetting.DRIVER_PATH.setString(driverData[position - 1].first)
findSelectedDriver(GpuDriverHelper.getMetadataFromZip(File(driverPath)))
}
}
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
driversToDelete.add(driverData[removedPosition - 1].first)
driverData.removeAt(removedPosition - 1)
onDriverSelected(selectedPosition)
}
fun onDriverAdded(driver: Pair<String, GpuDriverMetadata>) {
if (driversToDelete.contains(driver.first)) {
driversToDelete.remove(driver.first)
}
driverData.add(driver)
onDriverSelected(driverData.size)
}
fun onCloseDriverManager(game: Game?) {
_isDeletingDrivers.value = true
StringSetting.DRIVER_PATH.setString(driverList.value[selectedDriver].first)
updateDriverNameForGame(game)
if (game == null) {
NativeConfig.saveGlobalConfig()
@@ -165,6 +181,20 @@ class DriverViewModel : ViewModel() {
}
}
private fun findSelectedDriver(currentDriverMetadata: GpuDriverMetadata) {
if (driverList.value.size == 1) {
setSelectedDriverIndex(0)
return
}
driverList.value.forEachIndexed { i: Int, driver: Pair<String, GpuDriverMetadata> ->
if (driver.second == currentDriverMetadata) {
setSelectedDriverIndex(i)
return
}
}
}
fun updateDriverNameForGame(game: Game?) {
if (!GpuDriverHelper.supportsCustomDriverLoading()) {
return
@@ -187,6 +217,7 @@ class DriverViewModel : ViewModel() {
private fun setDriverReady() {
_isDriverReady.value = true
updateName()
_selectedDriverTitle.value = GpuDriverHelper.customDriverSettingData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
}
}

View File

@@ -167,14 +167,13 @@ class GamesViewModel : ViewModel() {
}
}
fun onCloseGameFoldersFragment() {
NativeConfig.saveGlobalConfig()
fun onCloseGameFoldersFragment() =
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeConfig.saveGlobalConfig()
getGameDirs(true)
}
}
}
private fun getGameDirs(reloadList: Boolean = false) {
val gameDirs = NativeConfig.getGameDirs()

View File

@@ -1,9 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
interface SelectableItem {
var selected: Boolean
fun onSelectionStateChanged(selected: Boolean)
}

View File

@@ -30,6 +30,7 @@ import org.yuzu.yuzu_emu.NativeLibrary.StickType
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.overlay.model.OverlayControl
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
@@ -301,7 +302,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
// Persist button position by saving new place.
saveControlPosition(
OverlayControl.COMBINED_DPAD.id,
Settings.PREF_BUTTON_DPAD,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
layout

View File

@@ -41,7 +41,6 @@ import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskState
@@ -59,7 +58,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels()
private val addonViewModel: AddonViewModel by viewModels()
private val driverViewModel: DriverViewModel by viewModels()
override var themeId: Int = 0
@@ -627,8 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
File(DirectoryInitialization.userDirectory!!),
DirectoryInitialization.userDirectory!!,
BufferedOutputStream(contentResolver.openOutputStream(result)),
taskViewModel.cancelled,
compression = false
taskViewModel.cancelled
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.user_data_export_success)
@@ -691,7 +688,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false)
driverViewModel.reloadDriverData()
return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)

View File

@@ -21,7 +21,6 @@ import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
import java.util.zip.Deflater
import java.util.zip.ZipOutputStream
import kotlin.IllegalStateException
@@ -313,23 +312,15 @@ object FileUtil {
* @param inputFile File representation of the item that will be zipped
* @param rootDir Directory containing the inputFile
* @param outputStream Stream where the zip file will be output
* @param cancelled [StateFlow] that reports whether this process has been cancelled
* @param compression Disables compression if true
*/
fun zipFromInternalStorage(
inputFile: File,
rootDir: String,
outputStream: BufferedOutputStream,
cancelled: StateFlow<Boolean>? = null,
compression: Boolean = true
cancelled: StateFlow<Boolean>? = null
): TaskState {
try {
ZipOutputStream(outputStream).use { zos ->
if (!compression) {
zos.setMethod(ZipOutputStream.DEFLATED)
zos.setLevel(Deflater.NO_COMPRESSION)
}
inputFile.walkTopDown().forEach { file ->
if (cancelled?.value == true) {
return TaskState.Cancelled
@@ -347,7 +338,6 @@ object FileUtil {
}
}
} catch (e: Exception) {
Log.error("[FileUtil] Failed creating zip file - ${e.message}")
return TaskState.Failed
}
return TaskState.Completed

View File

@@ -62,6 +62,9 @@ object GpuDriverHelper {
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct()
?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers
}

View File

@@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.viewholder
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import org.yuzu.yuzu_emu.adapters.AbstractDiffAdapter
import org.yuzu.yuzu_emu.adapters.AbstractListAdapter
/**
* [RecyclerView.ViewHolder] meant to work together with a [AbstractDiffAdapter] or a
* [AbstractListAdapter] so we can run [bind] on each list item without needing a manual hookup.
*/
abstract class AbstractViewHolder<Model>(binding: ViewBinding) :
RecyclerView.ViewHolder(binding.root) {
abstract fun bind(model: Model)
}

View File

@@ -14,6 +14,12 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
}
}
AndroidConfig::~AndroidConfig() {
if (global) {
AndroidConfig::SaveAllValues();
}
}
void AndroidConfig::ReloadAllValues() {
Reload();
ReadAndroidValues();

View File

@@ -9,6 +9,7 @@ class AndroidConfig final : public Config {
public:
explicit AndroidConfig(const std::string& config_name = "config",
ConfigType config_type = ConfigType::GlobalConfig);
~AndroidConfig() override;
void ReloadAllValues() override;
void SaveAllValues() override;

View File

@@ -45,15 +45,15 @@
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "jni/android_common/android_common.h"
#include "jni/id_cache.h"
#include "jni/native.h"
@@ -410,8 +410,8 @@ void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
handheld->Disconnect();
}
}
@@ -862,9 +862,6 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
jstring jprogramId) {
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
if (program_id == 0) {
return ToJString(env, "");
}
auto& system = EmulationSession::GetInstance().System();
@@ -883,19 +880,6 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
return ToJString(env, user_save_data_path);
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultProfileSaveDataRoot(JNIEnv* env,
jobject jobj,
jboolean jfuture) {
Service::Account::ProfileManager manager;
// TODO: Pass in a selected user once we get the relevant UI working
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
ASSERT(user_id);
const auto user_save_data_root =
FileSys::SaveDataFactory::GetUserGameSaveDataRoot(user_id->AsU128(), jfuture);
return ToJString(env, user_save_data_root);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* env, jobject jobj,
jstring jpath) {
EmulationSession::GetInstance().ConfigureFilesystemProvider(GetJString(env, jpath));

View File

@@ -147,7 +147,7 @@
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_version_name"
android:id="@+id/button_build_hash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
@@ -164,7 +164,7 @@
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_version_name"
android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -148,7 +148,7 @@
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:id="@+id/button_version_name"
android:id="@+id/button_build_hash"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
@@ -165,7 +165,7 @@
android:text="@string/build" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_version_name"
android:id="@+id/text_build_hash"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_driver_clear"
android:icon="@drawable/ic_clear"
android:title="@string/clear"
app:showAsAction="always" />
</menu>

View File

@@ -118,23 +118,15 @@
</integer-array>
<string-array name="rendererScreenLayoutNames">
<item>@string/screen_layout_auto</item>
<item>@string/screen_layout_sensor_landscape</item>
<item>@string/screen_layout_landscape</item>
<item>@string/screen_layout_reverse_landscape</item>
<item>@string/screen_layout_sensor_portrait</item>
<item>@string/screen_layout_portrait</item>
<item>@string/screen_layout_reverse_portrait</item>
<item>@string/screen_layout_auto</item>
</string-array>
<integer-array name="rendererScreenLayoutValues">
<item>0</item>
<item>5</item>
<item>1</item>
<item>2</item>
<item>6</item>
<item>4</item>
<item>3</item>
<item>0</item>
</integer-array>
<string-array name="rendererAspectRatioNames">

View File

@@ -133,15 +133,6 @@
<string name="add_game_folder">Add game folder</string>
<string name="folder_already_added">This folder was already added!</string>
<string name="game_folder_properties">Game folder properties</string>
<plurals name="saves_import_failed">
<item quantity="one">Failed to import %d save</item>
<item quantity="other">Failed to import %d saves</item>
</plurals>
<plurals name="saves_import_success">
<item quantity="one">Successfully imported %d save</item>
<item quantity="other">Successfully imported %d saves</item>
</plurals>
<string name="no_save_data_found">No save data found</string>
<!-- Applet launcher strings -->
<string name="applets">Applet launcher</string>
@@ -285,7 +276,6 @@
<string name="global">Global</string>
<string name="custom">Custom</string>
<string name="notice">Notice</string>
<string name="import_complete">Import complete</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string>
@@ -473,13 +463,9 @@
<string name="anti_aliasing_smaa">SMAA</string>
<!-- Screen Layouts -->
<string name="screen_layout_auto">Auto</string>
<string name="screen_layout_sensor_landscape">Sensor landscape</string>
<string name="screen_layout_landscape">Landscape</string>
<string name="screen_layout_reverse_landscape">Reverse landscape</string>
<string name="screen_layout_sensor_portrait">Sensor portrait</string>
<string name="screen_layout_portrait">Portrait</string>
<string name="screen_layout_reverse_portrait">Reverse portrait</string>
<string name="screen_layout_auto">Auto</string>
<!-- Aspect Ratios -->
<string name="ratio_default">Default (16:9)</string>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<game-mode-config
xmlns:android="http://schemas.android.com/apk/res/android"
android:supportsBatteryGameMode="true"
android:supportsPerformanceGameMode="true"
android:allowGameDownscaling="false"
android:allowGameFpsOverride="false"/>

View File

@@ -183,6 +183,22 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/graphics_context.h
hid/emulated_console.cpp
hid/emulated_console.h
hid/emulated_controller.cpp
hid/emulated_controller.h
hid/emulated_devices.cpp
hid/emulated_devices.h
hid/hid_core.cpp
hid/hid_core.h
hid/hid_types.h
hid/input_converter.cpp
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
hle/ipc.h
hle/kernel/board/nintendo/nx/k_memory_layout.cpp
@@ -515,16 +531,90 @@ add_library(core STATIC
hle/service/hid/hid.h
hle/service/hid/hid_debug_server.cpp
hle/service/hid/hid_debug_server.h
hle/service/hid/hid_firmware_settings.cpp
hle/service/hid/hid_firmware_settings.h
hle/service/hid/hid_server.cpp
hle/service/hid/hid_server.h
hle/service/hid/hid_system_server.cpp
hle/service/hid/hid_system_server.h
hle/service/hid/hid_util.h
hle/service/hid/hidbus.cpp
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
hle/service/hid/irs_ring_lifo.h
hle/service/hid/resource_manager.cpp
hle/service/hid/resource_manager.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/errors.h
hle/service/hid/controllers/types/debug_pad_types.h
hle/service/hid/controllers/types/keyboard_types.h
hle/service/hid/controllers/types/mouse_types.h
hle/service/hid/controllers/types/npad_types.h
hle/service/hid/controllers/types/shared_memory_format.h
hle/service/hid/controllers/types/touch_types.h
hle/service/hid/controllers/applet_resource.cpp
hle/service/hid/controllers/applet_resource.h
hle/service/hid/controllers/capture_button.cpp
hle/service/hid/controllers/capture_button.h
hle/service/hid/controllers/console_six_axis.cpp
hle/service/hid/controllers/console_six_axis.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_mouse.cpp
hle/service/hid/controllers/debug_mouse.h
hle/service/hid/controllers/debug_pad.cpp
hle/service/hid/controllers/debug_pad.h
hle/service/hid/controllers/digitizer.cpp
hle/service/hid/controllers/digitizer.h
hle/service/hid/controllers/gesture.cpp
hle/service/hid/controllers/gesture.h
hle/service/hid/controllers/home_button.cpp
hle/service/hid/controllers/home_button.h
hle/service/hid/controllers/keyboard.cpp
hle/service/hid/controllers/keyboard.h
hle/service/hid/controllers/mouse.cpp
hle/service/hid/controllers/mouse.h
hle/service/hid/controllers/npad.cpp
hle/service/hid/controllers/npad.h
hle/service/hid/controllers/palma.cpp
hle/service/hid/controllers/palma.h
hle/service/hid/controllers/seven_six_axis.cpp
hle/service/hid/controllers/seven_six_axis.h
hle/service/hid/controllers/shared_memory_holder.cpp
hle/service/hid/controllers/shared_memory_holder.h
hle/service/hid/controllers/six_axis.cpp
hle/service/hid/controllers/six_axis.h
hle/service/hid/controllers/sleep_button.cpp
hle/service/hid/controllers/sleep_button.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/unique_pad.cpp
hle/service/hid/controllers/unique_pad.h
hle/service/hid/hidbus/hidbus_base.cpp
hle/service/hid/hidbus/hidbus_base.h
hle/service/hid/hidbus/ringcon.cpp
hle/service/hid/hidbus/ringcon.h
hle/service/hid/hidbus/starlink.cpp
hle/service/hid/hidbus/starlink.h
hle/service/hid/hidbus/stubbed.cpp
hle/service/hid/hidbus/stubbed.h
hle/service/hid/irsensor/clustering_processor.cpp
hle/service/hid/irsensor/clustering_processor.h
hle/service/hid/irsensor/image_transfer_processor.cpp
hle/service/hid/irsensor/image_transfer_processor.h
hle/service/hid/irsensor/ir_led_processor.cpp
hle/service/hid/irsensor/ir_led_processor.h
hle/service/hid/irsensor/moment_processor.cpp
hle/service/hid/irsensor/moment_processor.h
hle/service/hid/irsensor/pointing_processor.cpp
hle/service/hid/irsensor/pointing_processor.h
hle/service/hid/irsensor/processor_base.cpp
hle/service/hid/irsensor/processor_base.h
hle/service/hid/irsensor/tera_plugin_processor.cpp
hle/service/hid/irsensor/tera_plugin_processor.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/lan_discovery.cpp
@@ -708,24 +798,24 @@ add_library(core STATIC
hle/service/server_manager.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp
hle/service/set/set.h
hle/service/set/appln_settings.cpp
hle/service/set/appln_settings.h
hle/service/set/device_settings.cpp
hle/service/set/device_settings.h
hle/service/set/factory_settings_server.cpp
hle/service/set/factory_settings_server.h
hle/service/set/firmware_debug_settings_server.cpp
hle/service/set/firmware_debug_settings_server.h
hle/service/set/private_settings.cpp
hle/service/set/private_settings.h
hle/service/set/set_cal.cpp
hle/service/set/set_cal.h
hle/service/set/set_fd.cpp
hle/service/set/set_fd.h
hle/service/set/set_sys.cpp
hle/service/set/set_sys.h
hle/service/set/settings.cpp
hle/service/set/settings.h
hle/service/set/settings_server.cpp
hle/service/set/settings_server.h
hle/service/set/system_settings.cpp
hle/service/set/system_settings.h
hle/service/set/system_settings_server.cpp
hle/service/set/system_settings_server.h
hle/service/sm/sm.cpp
hle/service/sm/sm.h
hle/service/sm/sm_controller.cpp
@@ -865,7 +955,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})

View File

@@ -28,6 +28,7 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -51,7 +52,6 @@
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
#include "core/tools/renderdoc.h"
#include "hid_core/hid_core.h"
#include "network/network.h"
#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"

View File

@@ -64,7 +64,7 @@ public:
return [this] { ShutdownThreadFunction(); };
}
void PreemptSingleCore(bool from_running_environment = true);
void PreemptSingleCore(bool from_running_enviroment = true);
std::size_t CurrentCore() const {
return current_core.load();

View File

@@ -559,28 +559,28 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
}
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
{"----- Free ------", Kernel::Svc::MemoryState::Free},
{"Io ", Kernel::Svc::MemoryState::Io},
{"Static ", Kernel::Svc::MemoryState::Static},
{"Code ", Kernel::Svc::MemoryState::Code},
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
{"Normal ", Kernel::Svc::MemoryState::Normal},
{"Shared ", Kernel::Svc::MemoryState::Shared},
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
{"Stack ", Kernel::Svc::MemoryState::Stack},
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
{"Transferred ", Kernel::Svc::MemoryState::Transferred},
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
{"----- Free -----", Kernel::Svc::MemoryState::Free},
{"Io ", Kernel::Svc::MemoryState::Io},
{"Static ", Kernel::Svc::MemoryState::Static},
{"Code ", Kernel::Svc::MemoryState::Code},
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
{"Normal ", Kernel::Svc::MemoryState::Normal},
{"Shared ", Kernel::Svc::MemoryState::Shared},
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
{"Stack ", Kernel::Svc::MemoryState::Stack},
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
{"Transfered ", Kernel::Svc::MemoryState::Transfered},
{"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
}};
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {

View File

@@ -26,7 +26,7 @@
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/settings_server.h"
#include "core/hle/service/set/set.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/memory/cheat_engine.h"

View File

@@ -189,15 +189,6 @@ std::string SaveDataFactory::GetFullPath(Core::System& system, VirtualDir dir,
}
}
std::string SaveDataFactory::GetUserGameSaveDataRoot(u128 user_id, bool future) {
if (future) {
Common::UUID uuid;
std::memcpy(uuid.uuid.data(), user_id.data(), sizeof(Common::UUID));
return fmt::format("/user/save/account/{}", uuid.RawString());
}
return fmt::format("/user/save/{:016X}/{:016X}{:016X}", 0, user_id[1], user_id[0]);
}
SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
u128 user_id) const {
const auto path =

View File

@@ -101,7 +101,6 @@ public:
static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space);
static std::string GetFullPath(Core::System& system, VirtualDir dir, SaveDataSpaceId space,
SaveDataType type, u64 title_id, u128 user_id, u64 save_id);
static std::string GetUserGameSaveDataRoot(u128 user_id, bool future);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,

View File

@@ -6,9 +6,9 @@
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/frontend/applets/controller.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
namespace Core::Frontend {
@@ -47,7 +47,7 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
if (parameters.allow_pro_controller) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Fullkey);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->Connect(true);
} else if (parameters.allow_dual_joycons) {
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::JoyconDual);

View File

@@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/input_converter.h"
#include "core/hid/emulated_console.h"
#include "core/hid/input_converter.h"
namespace Core::HID {
EmulatedConsole::EmulatedConsole() = default;

View File

@@ -17,8 +17,8 @@
#include "common/point.h"
#include "common/quaternion.h"
#include "common/vector_math.h"
#include "hid_core/frontend/motion_input.h"
#include "hid_core/hid_types.h"
#include "core/hid/hid_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
static constexpr std::size_t MaxTouchDevices = 32;

View File

@@ -6,9 +6,9 @@
#include "common/polyfill_ranges.h"
#include "common/thread.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/frontend/input_converter.h"
#include "hid_core/hid_util.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
#include "core/hle/service/hid/hid_util.h"
namespace Core::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
@@ -27,7 +27,7 @@ EmulatedController::~EmulatedController() = default;
NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
return NpadStyleIndex::Fullkey;
return NpadStyleIndex::ProController;
case Settings::ControllerType::DualJoyconDetached:
return NpadStyleIndex::JoyconDual;
case Settings::ControllerType::LeftJoycon:
@@ -49,13 +49,13 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
case Settings::ControllerType::SegaGenesis:
return NpadStyleIndex::SegaGenesis;
default:
return NpadStyleIndex::Fullkey;
return NpadStyleIndex::ProController;
}
}
Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleIndex type) {
switch (type) {
case NpadStyleIndex::Fullkey:
case NpadStyleIndex::ProController:
return Settings::ControllerType::ProController;
case NpadStyleIndex::JoyconDual:
return Settings::ControllerType::DualJoyconDetached;
@@ -106,7 +106,7 @@ void EmulatedController::ReloadFromSettings() {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
original_npad_type = npad_type;
} else {
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
SetNpadStyleIndex(NpadStyleIndex::ProController);
original_npad_type = npad_type;
}
@@ -1073,7 +1073,7 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
.body = GetNpadColor(controller.color_values[index].body),
.button = GetNpadColor(controller.color_values[index].buttons),
};
if (npad_type == NpadStyleIndex::Fullkey) {
if (npad_type == NpadStyleIndex::ProController) {
controller.colors_state.left = {
.body = GetNpadColor(controller.color_values[index].left_grip),
.button = GetNpadColor(controller.color_values[index].buttons),
@@ -1356,7 +1356,7 @@ bool EmulatedController::HasNfc() const {
switch (npad_type) {
case NpadStyleIndex::JoyconRight:
case NpadStyleIndex::JoyconDual:
case NpadStyleIndex::Fullkey:
case NpadStyleIndex::ProController:
case NpadStyleIndex::Handheld:
break;
default:
@@ -1548,7 +1548,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
SetNpadStyleIndex(NpadStyleIndex::ProController);
Connect();
return;
}
@@ -1556,13 +1556,13 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
// Fallback Dual joycon controllers to Pro controllers
if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
SetNpadStyleIndex(NpadStyleIndex::ProController);
Connect();
return;
}
// Fallback Pro controllers to Dual joycon
if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
Connect();
@@ -1577,7 +1577,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::Fullkey:
case NpadStyleIndex::ProController:
case NpadStyleIndex::GameCube:
case NpadStyleIndex::NES:
case NpadStyleIndex::SNES:
@@ -1593,7 +1593,7 @@ bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::Fullkey:
case NpadStyleIndex::ProController:
return supported_style_tag.fullkey.As<bool>();
case NpadStyleIndex::Handheld:
return supported_style_tag.handheld.As<bool>();

View File

@@ -15,9 +15,9 @@
#include "common/param_package.h"
#include "common/settings.h"
#include "common/vector_math.h"
#include "hid_core/frontend/motion_input.h"
#include "hid_core/hid_types.h"
#include "hid_core/irsensor/irs_types.h"
#include "core/hid/hid_types.h"
#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;

View File

@@ -4,8 +4,8 @@
#include <algorithm>
#include <fmt/format.h>
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/frontend/input_converter.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/input_converter.h"
namespace Core::HID {

View File

@@ -14,7 +14,7 @@
#include "common/input.h"
#include "common/param_package.h"
#include "common/settings.h"
#include "hid_core/hid_types.h"
#include "core/hid/hid_types.h"
namespace Core::HID {
using KeyboardDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,

View File

@@ -2,11 +2,11 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "hid_core/frontend/emulated_console.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_util.h"
#include "core/hid/emulated_console.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/emulated_devices.h"
#include "core/hid/hid_core.h"
#include "core/hle/service/hid/hid_util.h"
namespace Core::HID {

View File

@@ -6,7 +6,7 @@
#include <memory>
#include "common/common_funcs.h"
#include "hid_core/hid_types.h"
#include "core/hid/hid_types.h"
namespace Core::HID {
class EmulatedConsole;

View File

@@ -220,7 +220,6 @@ enum class NpadIdType : u32 {
};
enum class NpadInterfaceType : u8 {
None = 0,
Bluetooth = 1,
Rail = 2,
Usb = 3,
@@ -230,7 +229,7 @@ enum class NpadInterfaceType : u8 {
// This is nn::hid::NpadStyleIndex
enum class NpadStyleIndex : u8 {
None = 0,
Fullkey = 3,
ProController = 3,
Handheld = 4,
HandheldNES = 4,
JoyconDual = 5,
@@ -268,7 +267,6 @@ enum class NpadStyleSet : u32 {
All = 0xFFFFFFFFU,
};
static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet)
// This is nn::hid::VibrationDevicePosition
enum class VibrationDevicePosition : u32 {

View File

@@ -5,7 +5,7 @@
#include <random>
#include "common/input.h"
#include "hid_core/frontend/input_converter.h"
#include "core/hid/input_converter.h"
namespace Core::HID {

View File

@@ -2,12 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hid/input_interpreter.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/hid/resource_manager.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/frontend/input_interpreter.h"
#include "hid_core/hid_types.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/npad/npad.h"
InputInterpreter::InputInterpreter(Core::System& system)
: npad{system.ServiceManager()

View File

@@ -5,7 +5,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "hid_core/hid_types.h"
#include "core/hid/hid_types.h"
namespace Core::IrSensor {

View File

@@ -4,7 +4,7 @@
#include <cmath>
#include "common/math_util.h"
#include "hid_core/frontend/motion_input.h"
#include "core/hid/motion_input.h"
namespace Core::HID {

View File

@@ -81,12 +81,12 @@ enum class KMemoryState : u32 {
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
Transferred = static_cast<u32>(Svc::MemoryState::Transferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransferred = static_cast<u32>(Svc::MemoryState::SharedTransferred) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped |
FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc |
@@ -130,8 +130,8 @@ static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
static_assert(static_cast<u32>(KMemoryState::Transferred) == 0x055C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransferred) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);

View File

@@ -486,8 +486,8 @@ KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const {
case Svc::MemoryState::Shared:
case Svc::MemoryState::AliasCode:
case Svc::MemoryState::AliasCodeData:
case Svc::MemoryState::Transferred:
case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::Transfered:
case Svc::MemoryState::SharedTransfered:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:
@@ -522,8 +522,8 @@ size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const {
case Svc::MemoryState::Shared:
case Svc::MemoryState::AliasCode:
case Svc::MemoryState::AliasCodeData:
case Svc::MemoryState::Transferred:
case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::Transfered:
case Svc::MemoryState::SharedTransfered:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:
@@ -564,8 +564,8 @@ bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemorySt
case Svc::MemoryState::AliasCodeData:
case Svc::MemoryState::Stack:
case Svc::MemoryState::ThreadLocal:
case Svc::MemoryState::Transferred:
case Svc::MemoryState::SharedTransferred:
case Svc::MemoryState::Transfered:
case Svc::MemoryState::SharedTransfered:
case Svc::MemoryState::SharedCode:
case Svc::MemoryState::GeneratedCode:
case Svc::MemoryState::CodeOut:

View File

@@ -1258,11 +1258,11 @@ ThreadState KThread::RequestTerminate() {
// Change the thread's priority to be higher than any system thread's.
this->IncreaseBasePriority(TerminatingThreadPriority);
// If the thread is runnable, send a termination interrupt to cores it may be running on.
// If the thread is runnable, send a termination interrupt to other cores.
if (this->GetState() == ThreadState::Runnable) {
// NOTE: We do not mask the "current core", because this code may not actually be
// executing from the thread representing the "current core".
if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) {
if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask() &
~(1ULL << GetCurrentCoreId(m_kernel));
core_mask != 0) {
Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask);
}
}

View File

@@ -76,8 +76,8 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer
// Map the memory.
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
? KMemoryState::Transferred
: KMemoryState::SharedTransferred;
? KMemoryState::Transfered
: KMemoryState::SharedTransfered;
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
address, *m_page_group, state, KMemoryPermission::UserReadWrite));
@@ -96,8 +96,8 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
// Unmap the memory.
const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
? KMemoryState::Transferred
: KMemoryState::SharedTransferred;
? KMemoryState::Transfered
: KMemoryState::SharedTransfered;
R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
// Mark ourselves as unmapped.

View File

@@ -90,7 +90,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
.CanContain(address, size, KMemoryState::Transferred),
.CanContain(address, size, KMemoryState::Transfered),
ResultInvalidMemoryRegion);
// Map the transfer memory.
@@ -117,7 +117,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a
// Verify that the mapping is in range.
R_UNLESS(GetCurrentProcess(system.Kernel())
.GetPageTable()
.CanContain(address, size, KMemoryState::Transferred),
.CanContain(address, size, KMemoryState::Transfered),
ResultInvalidMemoryRegion);
// Unmap the transfer memory.

View File

@@ -27,8 +27,8 @@ enum class MemoryState : u32 {
Ipc = 0x0A,
Stack = 0x0B,
ThreadLocal = 0x0C,
Transferred = 0x0D,
SharedTransferred = 0x0E,
Transfered = 0x0D,
SharedTransfered = 0x0E,
SharedCode = 0x0F,
Inaccessible = 0x10,
NonSecureIpc = 0x11,

View File

@@ -13,6 +13,7 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
@@ -36,6 +37,7 @@
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
@@ -46,8 +48,6 @@
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_results.h"
#include "core/memory.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/npad/npad.h"
namespace Service::AM {

View File

@@ -5,13 +5,13 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/hid/hid_core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfc/common/device.h"
#include "hid_core/hid_core.h"
namespace Service::AM::Applets {

View File

@@ -9,13 +9,13 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_controller.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/npad/npad.h"
#include "core/hle/service/hid/controllers/npad.h"
namespace Service::AM::Applets {

View File

@@ -76,7 +76,7 @@ struct UiSettingsDisplayOptions {
bool is_system_or_launcher;
bool is_registration_permitted;
bool show_skip_button;
bool additional_select;
bool aditional_select;
bool show_user_selector;
bool is_unqualified_user_selectable;
};

View File

@@ -85,7 +85,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu
}
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
ContentType content_type, s64 start_posix_time,
ContentType contex_type, s64 start_posix_time,
s64 end_posix_time, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
@@ -94,7 +94,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
std::vector<ApplicationAlbumEntry> album_entries;
const auto start_date = ConvertToAlbumDateTime(start_posix_time);
const auto end_date = ConvertToAlbumDateTime(end_posix_time);
const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid);
const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid);
if (result.IsError()) {
return result;
@@ -113,14 +113,14 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& ou
}
Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
ContentType content_type, AlbumFileDateTime start_date,
ContentType contex_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const {
if (!is_mounted) {
return ResultIsNotMounted;
}
for (auto& [file_id, path] : album_files) {
if (file_id.type != content_type) {
if (file_id.type != contex_type) {
continue;
}
if (file_id.date > start_date) {
@@ -139,7 +139,7 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en
.hash{},
.datetime = file_id.date,
.storage = file_id.storage,
.content = content_type,
.content = contex_type,
.unknown = 1,
};
out_entries.push_back(entry);

View File

@@ -45,10 +45,10 @@ public:
Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
u8 flags) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
ContentType content_type, s64 start_posix_time, s64 end_posix_time,
ContentType contex_type, s64 start_posix_time, s64 end_posix_time,
u64 aruid) const;
Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries,
ContentType content_type, AlbumFileDateTime start_date,
ContentType contex_type, AlbumFileDateTime start_date,
AlbumFileDateTime end_date, u64 aruid) const;
Result GetAutoSavingStorage(bool& out_is_autosaving) const;
Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,

View File

@@ -12,7 +12,7 @@ constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12);
constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12);
constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);

View File

@@ -131,7 +131,7 @@ private:
u8 is_favorite;
u8 same_app;
u8 same_app_played;
u8 arbitrary_app_played;
u8 arbitary_app_played;
u64 group_id;
};
static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size");

View File

@@ -3,9 +3,9 @@
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
#include "core/hle/service/hid/errors.h"
namespace Service::HID {
@@ -87,9 +87,7 @@ Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input
data_index = i;
break;
}
// TODO: Don't Handle pending delete here
if (registration_list.flag[i] == RegistrationStatus::None ||
registration_list.flag[i] == RegistrationStatus::PendingDelete) {
if (registration_list.flag[i] == RegistrationStatus::None) {
data_index = i;
break;
}
@@ -106,22 +104,30 @@ Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input
}
void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
const u64 index = GetIndexFromAruid(aruid);
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return;
if (index < AruidIndexMax) {
if (data[index].flag.is_assigned) {
data[index].shared_memory_format = nullptr;
data[index].flag.is_assigned.Assign(false);
}
}
FreeAppletResourceId(aruid);
DestroySevenSixAxisTransferMemory();
data[index].flag.raw = 0;
data[index].aruid = 0;
index = GetIndexFromAruid(aruid);
if (index < AruidIndexMax) {
DestroySevenSixAxisTransferMemory();
data[index].flag.raw = 0;
data[index].aruid = 0;
registration_list.flag[index] = RegistrationStatus::PendingDelete;
index = GetIndexFromAruid(aruid);
if (index < AruidIndexMax) {
registration_list.flag[index] = RegistrationStatus::PendingDelete;
}
}
}
void AppletResource::FreeAppletResourceId(u64 aruid) {
const u64 index = GetIndexFromAruid(aruid);
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return;
}
@@ -130,7 +136,6 @@ void AppletResource::FreeAppletResourceId(u64 aruid) {
if (aruid_data.flag.is_assigned) {
aruid_data.shared_memory_format = nullptr;
aruid_data.flag.is_assigned.Assign(false);
shared_memory_holder[index].Finalize();
}
}
@@ -139,7 +144,7 @@ u64 AppletResource::GetActiveAruid() {
}
Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
const u64 index = GetIndexFromAruid(aruid);
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return ResultAruidNotRegistered;
}
@@ -150,7 +155,7 @@ Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle,
Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
u64 aruid) {
const u64 index = GetIndexFromAruid(aruid);
u64 index = GetIndexFromAruid(aruid);
if (index >= AruidIndexMax) {
return ResultAruidNotRegistered;
}

View File

@@ -9,7 +9,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/result.h"
#include "hid_core/resources/shared_memory_holder.h"
#include "core/hle/service/hid/controllers/shared_memory_holder.h"
namespace Core {
class System;
@@ -25,7 +25,6 @@ class AppletResource;
class NPadResource;
static constexpr std::size_t AruidIndexMax = 0x20;
static constexpr u64 SystemAruid = 0;
enum class RegistrationStatus : u32 {
None,

View File

@@ -2,9 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core_timing.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"
#include "hid_core/resources/system_buttons/capture_button.h"
#include "core/hle/service/hid/controllers/applet_resource.h"
#include "core/hle/service/hid/controllers/capture_button.h"
#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
namespace Service::HID {
@@ -21,11 +21,10 @@ void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
return;
}
std::scoped_lock shared_lock{*shared_mutex};
const u64 aruid = applet_resource->GetActiveAruid();
auto* data = applet_resource->GetAruidData(aruid);
if (data == nullptr || !data->flag.is_assigned) {
if (data == nullptr) {
return;
}

View File

@@ -3,7 +3,7 @@
#pragma once
#include "hid_core/resources/controller_base.h"
#include "core/hle/service/hid/controllers/controller_base.h"
namespace Service::HID {

Some files were not shown because too many files have changed in this diff Show More