Compare commits

...

59 Commits

Author SHA1 Message Date
OpenSauce
67f6735f02
qt: Add game launch stress test functionality behind new ENABLE_DEVELOPER_OPTIONS CMake option (#1442)
* qt: Implemented game launch stress test

* qt: Hide stress test behind ENABLE_DEVELOPER_OPTIONS CMake option
2025-10-21 16:29:15 +01:00
OpenSauce04
1f483e1d33 cmake: Fixed build issues on *nix introduced by Qt 6.10.0 2025-10-21 14:45:40 +00:00
OpenSauce04
068fec0d5f ci: Updated ARM64 and universal macOS runners to macOS 26 Tahoe 2025-10-03 17:35:02 +00:00
David Griswold
f309d4881a android: Add Display Listener methods for smoother secondary display updates 2025-10-03 17:04:57 +00:00
OpenSauce04
80ba2058a9 externals: Updated fmt to 12.0.0
This fixes a build failure with Clang 21
2025-10-03 16:28:01 +01:00
huesos_96
3716f6b9b6
Android: Dual screen fixes for Handhelds that have 2 screens like Ayaneo Pocket DS (#1341)
* Prevent SecondaryDisplay from stealing focus

The SecondaryDisplay Activity was stealing focus from the main
Activity when it was launched.

Set the `FLAG_NOT_FOCUSABLE` and `FLAG_NOT_TOUCH_MODAL` window flags
to prevent the SecondaryDisplay from gaining focus.

* Implement touch controls for secondary display

This commit introduces touch input handling for the secondary display.

The following changes were made:
- Added `onSecondaryTouchEvent` and `onSecondaryTouchMoved` to `NativeLibrary.kt` and `native.cpp` to process touch events on the secondary display.
- Implemented `onTouchListener` in `SecondaryDisplay.kt` to capture touch events and forward them to the native layer.
- Handles `ACTION_DOWN`, `ACTION_POINTER_DOWN`, `ACTION_MOVE`, `ACTION_UP`, `ACTION_POINTER_UP`, and `ACTION_CANCEL` motion events.
- Tracks the active pointer to ensure correct touch event handling.

* Refactor display logic for multi-display support

This commit introduces a `DisplayHelper` class to centralize display-related logic, particularly for handling scenarios where the application might be launched on an external display.

Key changes:
- Added `DisplayHelper.kt` to manage internal and external display identification based on launch conditions.
- `MainActivity` and `EmulationActivity` now use `DisplayHelper.checkLaunchDisplay()` to determine the initial display.
- `SecondaryDisplay` now uses `DisplayHelper.getExternalDisplay()` to correctly identify the target display for the secondary presentation.
- `InputOverlay` now queries `DisplayHelper.isBottomOnPrimary()` to determine if touch input should be processed for the primary display based on the current screen layout.
- `SecondaryDisplay` now queries `DisplayHelper.isBottomOnSecondary()` to conditionally pass touch events to the native layer based on which screen (primary or secondary) is currently displaying the 3DS bottom screen.

These changes ensure that the application behaves correctly when launched on either the internal or an external display, and that touch input is routed appropriately based on the user's chosen screen layout for the dual screens.

* Removed primary-screen checks so the input overlay always forwards touch events, ensuring all touches reach the native handler even when multiple displays are active

* Remove DisplayHelper class and adjust external display logic

* Formatting adjustments

---------

Co-authored-by: DavidRGriswold <novachild@gmail.com>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-10-03 14:45:49 +01:00
OpenSauce04
3af2cd1227 ci: Update all macOS runners to macOS 15 Sequoia 2025-10-03 14:17:27 +01:00
lannoene
0be78e9550
Improve DLP and multiplayer compatability (#1375)
* Added NWM spectator mode (DLP now partially working), fixed debug assert, added applet utility cmd fallback

* Reverted AppletUtility command change

* Fixed inconsistent mac address

* Enabled DLP Child authorization

* Added the DLP module to recommended online modules

* Clean up

* Changed the returned number of words on GetProgramInfoFromCia to 7 insteead of 8, futher reverted AppletUtility function to match its original form
2025-09-20 19:59:54 +02:00
OpenSauce04
eb1197a65c cmake: Remove SYSTEM from target_link_libraries 2025-09-16 16:05:38 +01:00
OpenSauce04
a674eb8367 vk_pipeline_cache: Fix directory creation failure if shaders/vulkan/ is missing 2025-09-13 01:19:52 +01:00
OpenSauce04
118d124852 tools: Added Github cache purge script 2025-09-13 01:19:19 +01:00
OpenSauce04
34d46528eb Bump minimum Android version to Android 10 2025-09-11 01:08:32 +01:00
OpenSauce04
72c924a456 android: Rename performance overlay setting keys, strings and enums for clarity 2025-09-10 22:49:46 +01:00
OpenSauce04
f27490aeab android: Mark performance overlay toggle in settings as non-runtime-editable 2025-09-10 22:49:46 +01:00
OpenSauce04
ed451a72d9 android: Reimplemented performance overlay setting as a regular BooleanSetting 2025-09-10 22:49:46 +01:00
OpenSauce04
5c89977605 android: Removed hardcoded setting keys and default values for perf overlay 2025-09-10 22:49:46 +01:00
Briar
deda004227 renderer_opengl.cpp: Remove isPortrait check for second screen opacity 2025-09-10 18:43:44 +01:00
Briar
64f5277789 renderer_vulkan: Add second screen opacity support
& Update bottom screen opacity label in UI
2025-09-10 18:43:44 +01:00
marsia
5c5b1cdf45 android: Implement Hide All 3DS Images from Android setting
Co-Authored-By: Reg Tiangha <rtiangha@users.noreply.github.com>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-10 18:28:37 +01:00
marsia
750286ae97 android: Add second screen opacity option
Co-authored-by: Reg Tiangha <rtiangha@users.noreply.github.com>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-10 18:22:55 +01:00
Reg Tiangha
f35091eeb4 android: Implement Background Color settings UI
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-10 18:22:38 +01:00
OpenSauce04
8cd3bcc0e3 Updated compatibility list 2025-09-05 22:13:02 +01:00
OpenSauce04
e200bcbcbc tools: Updated guidance regarding translation updates 2025-09-05 21:56:02 +01:00
David Griswold
a2e6891f01 android: Re-fixed game termination bug (#1357)
* EmulationActivity and EmulationFragment clear only their own hooks

* EmulationLifecycleUtil: Rename `remove()` to `removeHook()`

* EmulationLifecycleUtil: Removed unused function `clear()`

* Corrected somewhat incorrect usage of the word "hook"

* Define `onShutdown` and `onPause` hook functions in constructors

* Formatting nitpicks

* Updated license header

* Re-added log messages for attempting to add duplicate hooks

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-05 21:36:01 +01:00
OpenSauce04
e0078b2407 Revert "Fix android termination bug (#1354)"
This reverts commit 70f9379eefc84b7651e3aababcce33987e073ed0.
2025-09-05 21:33:32 +01:00
OpenSauce04
1830290e55 SecondaryDisplay.kt: Remove redundant SurfaceTexture, preventing log spam 2025-09-05 16:44:48 +00:00
David Griswold
70f9379eef
Fix android termination bug (#1354)
* move hook additions to onCreateView

* Updated license header

* Formatting nitpick

* Added prefix to log messages

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-04 22:35:10 +01:00
OpenSauce04
b63d7841dd macos: Set UIDesignRequiresCompatibility to true 2025-09-03 23:02:08 +01:00
OpenSauce04
944cb7ab51 android: Bump Vulkan Validation Layers to SDK 1.4.313.0 2025-09-03 22:31:01 +01:00
DavidRGriswold
e5c3a0eef7 android: Prevent crash when editing a slider option with an out of bounds value
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-03 13:16:55 +01:00
OpenSauce04
16b8a78571 macos: Patch QMetalLayer.setNeedsDisplayInRect at runtime to avoid freezing on recent Qt 2025-09-03 03:15:14 +01:00
OpenSauce04
bf03cac9e1 cmake: On Windows, download MSVC 2022 Qt versions instead of MSVC 2019 2025-09-03 03:15:14 +01:00
OpenSauce04
b6be443069 cmake: Bump downloaded Qt version to 6.9.2
Also bumps aqtinstall to 3.3.0
2025-09-03 03:15:14 +01:00
OpenSauce04
10f6e1f6f7 cmake: Added check for minimum AppleClang version 2025-09-02 14:05:51 +01:00
RedBlackAka
6483b33ee1
Improve consistency of capitalization and colon usage in UI (#1306)
* frontend: Improve consistency of capitalization and : usage in UI

* Additional adjustments

* Further adjustments

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-09-01 01:43:54 +01:00
OpenSauce04
485db276e3 cmake: Corrected widespread incorrect usage of the SYSTEM property 2025-09-01 00:42:08 +01:00
OpenSauce04
20d577f9e3 Updated translations via Transifex 2025-08-20 13:58:33 +01:00
PabloMK7
48db1c1de5
am: fix save data being deleted on CIA install failure (#1319) 2025-08-20 14:48:34 +02:00
OpenSauce04
219b8dca2e Updated language translations via Transifex 2025-08-15 20:38:25 +01:00
OpenSauce
8fe05d5b74
ci: Don't cancel other builds in matrix if one fails 2025-08-11 12:33:52 +01:00
OpenSauce04
d79078c2d8 Updated language translations via Transifex 2025-08-11 12:04:03 +01:00
OpenSauce04
ff915b2fe1 Updated compatibility list 2025-08-11 12:02:01 +01:00
OpenSauce04
2f6e8b5756 android: Reimplement multitouch code correctly 2025-08-11 11:50:30 +01:00
OpenSauce04
a964e63722 Revert "android: Gross disgusting awful workaround for #1267"
This reverts commit 106e994dbf31bfae503551326a1a4b0c48647fab.
2025-08-11 11:50:30 +01:00
OpenSauce04
2087f96e90 qt: Prerelease builds now direct users to upgrade via GitHub rather than azahar-emu.org 2025-08-10 22:22:47 +01:00
OpenSauce04
68aab3e0e5 android: Fixed UI perf regression w/ OpenGL introduced by #617 2025-08-10 20:15:04 +01:00
OpenSauce04
c86830313e Updated language translations via Transifex 2025-08-08 23:19:15 +01:00
OpenSauce04
e3bfe497f2 Updated compatibility list 2025-08-08 22:58:41 +01:00
PabloMK7
dc2ab096cb loader: Fix compressed 3dsx icon reading 2025-08-08 22:46:17 +01:00
PabloMK7
4bf9161bcd android: Fix 3dsx listing 2025-08-08 22:46:17 +01:00
David Griswold
aca8b45664
android: Implement secondary display support (#617)
* Enable the SecondScreenPresentation class

* Update everything to enable second screen on android under GL and Vulkan. Still some issues!

* Some attempts to enable surface changes

* OpenGL is working on surface change, vulkan still no

* release surfaces (also fixed vulkan?)

* added and enabled layout setting

* resolve merge conflict

* rearrange switch cases to satisfy linux compiler

* openGL is working!

* several vk changes to try to fix crashes

* maybe vulkan is working?

* removing unnecessary code attempts

* Simplified secondscreen for better performance

* vk_platform.cpp: Fixed build failure caused by bad rebase

* vk_present_window.h: Removed stray newline

* Applied clang-format

* bug fix for odin 2

* Applied clang-format

* Updated license headers

* Moved SecondScreen class to org.citra.citra_emu.display

* Various formatting and readability improvements

* Added brackets where previously absent for readability

* Additional readability improvement

* RendererVulkan::NotifySurfaceChanged: Simplified condition checking

* change all references to "secondary screen" to "secondary display" to limit confusion with top screen / bottom screen

* rename main_window to main_present_window and second_window to secondary_present_window

* Reverted accidentally downgraded compatibility list submodule

* Removed unnecessary log message

* Applied clang-format

* Added a description to the Secondary Display Screen Layout setting

* Added `_ptr` suffix to `secondary_present_window`

This distinguishes it as a pointer, as `main_present_window` isn't a pointer, so there could be confusion on whether to use `.` or `->`

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-08-08 21:41:52 +01:00
OpenSauce04
2697526f34 android: Fixed new compressed format extensions not being handled correctly 2025-08-08 19:16:14 +01:00
Alex
e5838edf9f
MacOS: fix(network): fixes ArticBase UDP buffer handling on macOS (#1263)
* MacOS: fix(network): fixes ArticBase UDP buffer handling on macOS

- Set minimum 8KB UDP receive buffer size on macOS for reliable operation
- Add platform-specific buffer size handling with conditional compilation
- Enhance error logging with errno details and buffer size verification
- Maintain backward compatibility with existing buffer sizes on other platforms

Fixes ArticBase controller input reliability issues on macOS by ensuring
adequate UDP buffer capacity for real-time controller data transmission.

* Remove redundant log

---------

Co-authored-by: Alex Molina <alexmolina@MacBook-Air-de-Alex.local>
Co-authored-by: PabloMK7 <hackyglitch2@gmail.com>
2025-08-07 21:16:15 +02:00
OpenSauce04
1e9614fe4f cmake: Added SPIRV_HEADERS to LIB_VAR_LIST 2025-08-07 13:19:42 +01:00
OpenSauce
80f0c03e54 readme: Updated MacOS install instructions 2025-08-06 17:28:08 +01:00
RedBlackAka
cd333a619a ci: Distribute individual x86_64 and ARM64 macOS builds as well as universal 2025-08-06 17:22:02 +01:00
OpenSauce04
a660d9c04a cmake: Always use bundled spirv-tools, regardless of USE_SYSTEM_GLSLANG 2025-08-06 16:14:48 +01:00
OpenSauce04
f02a25f15f cmake: Added USE_SYSTEM_SPIRV_HEADERS option 2025-08-06 16:14:12 +01:00
RedBlackAka
05aa6af325
Installer: Further cleanups and high DPI support (#1234)
* Installer: Further cleanups and high DPI support

* Reverted removal of `(User)` suffix

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2025-08-04 13:59:00 +01:00
122 changed files with 6288 additions and 5295 deletions

View File

@ -25,6 +25,7 @@ jobs:
linux: linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
target: ["appimage", "fresh"] target: ["appimage", "fresh"]
container: container:
@ -61,8 +62,9 @@ jobs:
name: ${{ env.OS }}-${{ env.TARGET }} name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/ path: artifacts/
macos: macos:
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-13') || 'macos-14' }} runs-on: ${{ (matrix.target == 'x86_64' && 'macos-15-intel') || 'macos-26' }}
strategy: strategy:
fail-fast: false
matrix: matrix:
target: ["x86_64", "arm64"] target: ["x86_64", "arm64"]
env: env:
@ -87,14 +89,21 @@ jobs:
- name: Build - name: Build
run: ./.ci/macos.sh run: ./.ci/macos.sh
- name: Prepare outputs for caching - name: Prepare outputs for caching
run: mv build/bundle $OS-$TARGET run: cp -R build/bundle $OS-$TARGET
- name: Cache outputs for universal build - name: Cache outputs for universal build
uses: actions/cache/save@v4 uses: actions/cache/save@v4
with: with:
path: ${{ env.OS }}-${{ env.TARGET }} path: ${{ env.OS }}-${{ env.TARGET }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
- name: Pack
run: ./.ci/pack.sh
- name: Upload
uses: actions/upload-artifact@v4
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
macos-universal: macos-universal:
runs-on: macos-14 runs-on: macos-26
needs: macos needs: macos
env: env:
OS: macos OS: macos
@ -127,6 +136,7 @@ jobs:
windows: windows:
runs-on: windows-latest runs-on: windows-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
target: ["msvc", "msys2"] target: ["msvc", "msys2"]
defaults: defaults:

View File

@ -48,6 +48,15 @@ if (APPLE)
else() else()
# Minimum macOS 13 # Minimum macOS 13
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4") set(CMAKE_OSX_DEPLOYMENT_TARGET "13.4")
# Catch compiler issue on AppleClang versions below 15.0
# TODO: Remove this check when we drop macOS 13 Ventura
if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.0)
message(FATAL_ERROR "AppleClang 15.0 or later is required due to a compiler bug in earlier versions.\n"
"Current version: ${CMAKE_CXX_COMPILER_VERSION}\n"
"After updating, delete 'CMakeCache.txt' in the build directory.")
endif()
endif() endif()
endif() endif()
@ -112,6 +121,8 @@ option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON) option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
# Compile options # Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF) CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO}) option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
@ -299,7 +310,7 @@ find_package(Threads REQUIRED)
if (ENABLE_QT) if (ENABLE_QT)
if (NOT USE_SYSTEM_QT) if (NOT USE_SYSTEM_QT)
download_qt(6.7.2) download_qt(6.9.2)
endif() endif()
find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent) find_package(Qt6 REQUIRED COMPONENTS Widgets Multimedia Concurrent)

View File

@ -20,9 +20,9 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
set(arch_path "mingw_64") set(arch_path "mingw_64")
elseif (MSVC) elseif (MSVC)
if ("arm64" IN_LIST ARCHITECTURE) if ("arm64" IN_LIST ARCHITECTURE)
set(arch_path "msvc2019_arm64") set(arch_path "msvc2022_arm64")
elseif ("x86_64" IN_LIST ARCHITECTURE) elseif ("x86_64" IN_LIST ARCHITECTURE)
set(arch_path "msvc2019_64") set(arch_path "msvc2022_64")
else() else()
message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.") message(FATAL_ERROR "Unsupported bundled Qt architecture. Enable USE_SYSTEM_QT and provide your own.")
endif() endif()
@ -30,12 +30,13 @@ function(determine_qt_parameters target host_out type_out arch_out arch_path_out
# In case we're cross-compiling, prepare to also fetch the correct host Qt tools. # In case we're cross-compiling, prepare to also fetch the correct host Qt tools.
if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") if (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64")
set(host_arch_path "msvc2019_64") set(host_arch_path "msvc2022_64")
elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64") elseif (CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "ARM64")
# TODO: msvc2019_arm64 doesn't include some of the required tools for some reason, # TODO: msvc2019_arm64 doesn't include some of the required tools for some reason,
# TODO: so until it does, just use msvc2019_64 under x86_64 emulation. # TODO: so until it does, just use msvc2019_64 under x86_64 emulation.
# TODO: ^ Is this still true with msvc2022?
# set(host_arch_path "msvc2019_arm64") # set(host_arch_path "msvc2019_arm64")
set(host_arch_path "msvc2019_64") set(host_arch_path "msvc2022_64")
endif() endif()
set(host_arch "win64_${host_arch_path}") set(host_arch "win64_${host_arch_path}")
else() else()
@ -105,7 +106,7 @@ function(download_qt_configuration prefix_out target host type arch arch_path ba
if (NOT EXISTS "${prefix}") if (NOT EXISTS "${prefix}")
message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}") message(STATUS "Downloading Qt binaries for ${target}:${host}:${type}:${arch}:${arch_path}")
set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.1.18") set(AQT_PREBUILD_BASE_URL "https://github.com/miurahr/aqtinstall/releases/download/v3.3.0")
if (WIN32) if (WIN32)
set(aqt_path "${base_path}/aqt.exe") set(aqt_path "${base_path}/aqt.exe")
if (NOT EXISTS "${aqt_path}") if (NOT EXISTS "${aqt_path}")

View File

@ -22,9 +22,12 @@ If you are unsure of whether you want to use MSYS2 or MSVC, use MSYS2.
### MacOS ### MacOS
Download the latest release from [Releases](https://github.com/azahar-emu/azahar/releases). To download a build that will work on all Macs, you can download the `macos-universal` build on the [Releases](https://github.com/azahar-emu/azahar/releases) page.
The `macos-universal` download will work on both Intel and Apple Silicon Macs. Alternatively, if you wish to download a build specifically for your Mac, you can choose either:
- `macos-arm64` for Apple Silicon Macs
- `macos-x86_64` for Intel Macs
--- ---
### Android ### Android

View File

@ -75,6 +75,9 @@
<true/> <true/>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
<string>True</string> <string>True</string>
<key>UIDesignRequiresCompatibility</key>
<true/> <!-- Remove when Qt Liquid Glass issues are fixed upstream:
https://bugreports.qt.io/browse/QTBUG-138942 -->
<key>UIFileSharingEnabled</key> <key>UIFileSharingEnabled</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>

@ -1 +1 @@
Subproject commit 60bdec16580e370ecd019b0d4a92d07378f6b136 Subproject commit a36decbe43d0e5a570ac3d3ba9a0b226dc832a17

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

386
dist/languages/de.ts vendored

File diff suppressed because it is too large Load Diff

386
dist/languages/el.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

386
dist/languages/fi.ts vendored

File diff suppressed because it is too large Load Diff

386
dist/languages/fr.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

386
dist/languages/id.ts vendored

File diff suppressed because it is too large Load Diff

400
dist/languages/it.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

386
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load Diff

386
dist/languages/nl.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

410
dist/languages/sv.ts vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,37 @@
# Definitions for all external bundled libraries # Definitions for all external bundled libraries
# Suppress warnings from external libraries # Suppress warnings from external libraries
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if (MSVC)
add_compile_options(/W0) add_compile_options(/W0)
else() else()
add_compile_options(-w) add_compile_options(-w)
endif() endif()
function(target_disable_warnings target)
if (MSVC)
target_compile_options(${target} INTERFACE /W0)
else()
target_compile_options(${target} INTERFACE -w)
endif()
endfunction()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
include(DownloadExternals) include(DownloadExternals)
include(ExternalProject) include(ExternalProject)
# Boost # Boost
if (NOT USE_SYSTEM_BOOST) if (USE_SYSTEM_BOOST)
unset(BOOST_ROOT CACHE)
unset(Boost_INCLUDE_DIR CACHE)
set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
else()
message(STATUS "Including vendored Boost library") message(STATUS "Including vendored Boost library")
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "") set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/boost" CACHE STRING "")
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
add_library(boost INTERFACE) add_library(boost INTERFACE)
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) target_include_directories(boost INTERFACE ${Boost_INCLUDE_DIR})
target_disable_warnings(boost)
# Boost::serialization # Boost::serialization
file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp") file(GLOB boost_serialization_SRC "${CMAKE_SOURCE_DIR}/externals/boost/libs/serialization/src/*.cpp")
@ -33,11 +46,7 @@ if (NOT USE_SYSTEM_BOOST)
${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp ${CMAKE_SOURCE_DIR}/externals/boost/libs/iostreams/src/mapped_file.cpp
) )
target_link_libraries(boost_iostreams PUBLIC boost) target_link_libraries(boost_iostreams PUBLIC boost)
# Add additional boost libs here; remember to ALIAS them in the root CMakeLists! # Add additional boost libs here; remember to ALIAS them in the root CMakeLists!
else()
unset(BOOST_ROOT CACHE)
unset(Boost_INCLUDE_DIR CACHE)
set(Boost_NO_SYSTEM_PATHS OFF CACHE BOOL "" FORCE)
endif() endif()
# Catch2 # Catch2
@ -73,6 +82,7 @@ endif()
# dds-ktx # dds-ktx
add_library(dds-ktx INTERFACE) add_library(dds-ktx INTERFACE)
target_include_directories(dds-ktx INTERFACE ./dds-ktx) target_include_directories(dds-ktx INTERFACE ./dds-ktx)
target_disable_warnings(dds-ktx)
# fmt and Xbyak need to be added before dynarmic # fmt and Xbyak need to be added before dynarmic
# libfmt # libfmt
@ -137,7 +147,8 @@ endif()
# MicroProfile # MicroProfile
add_library(microprofile INTERFACE) add_library(microprofile INTERFACE)
target_include_directories(microprofile SYSTEM INTERFACE ./microprofile) target_include_directories(microprofile INTERFACE ./microprofile)
target_disable_warnings(microprofile)
if (ENABLE_MICROPROFILE) if (ENABLE_MICROPROFILE)
target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1) target_compile_definitions(microprofile INTERFACE MICROPROFILE_ENABLED=1)
else() else()
@ -146,10 +157,11 @@ endif()
# Nihstro # Nihstro
add_library(nihstro-headers INTERFACE) add_library(nihstro-headers INTERFACE)
target_include_directories(nihstro-headers SYSTEM INTERFACE ./nihstro/include) target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
if (MSVC) target_disable_warnings(nihstro-headers)
# TODO: For some reason MSVC still applies this warning even with /W0 for externals. if (NOT MSVC)
target_compile_options(nihstro-headers INTERFACE /wd4715) # TODO: For some reason MSYS2 still applied this warnin even with -w
target_compile_options(nihstro-headers INTERFACE -Wno-invalid-specialization)
endif() endif()
# Open Source Archives # Open Source Archives
@ -173,7 +185,8 @@ if (USE_SYSTEM_FFMPEG_HEADERS)
endif() endif()
if (NOT FOUND_FFMPEG_HEADERS) if (NOT FOUND_FFMPEG_HEADERS)
message(STATUS "Using bundled ffmpeg headers.") message(STATUS "Using bundled ffmpeg headers.")
target_include_directories(library-headers SYSTEM INTERFACE ./library-headers/ffmpeg/include) target_include_directories(library-headers INTERFACE ./library-headers/ffmpeg/include)
target_disable_warnings(library-headers)
endif() endif()
# SoundTouch # SoundTouch
@ -294,7 +307,8 @@ if (USE_SYSTEM_JSON)
# Citra uses "#include <json.hpp>" so we have to add this manually # Citra uses "#include <json.hpp>" so we have to add this manually
target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann") target_include_directories(json-headers SYSTEM INTERFACE "${NLOHMANN_PREFIX}/nlohmann")
else() else()
target_include_directories(json-headers SYSTEM INTERFACE ./json) target_include_directories(json-headers INTERFACE ./json)
target_disable_warnings(json-headers)
endif() endif()
# OpenSSL # OpenSSL
@ -310,7 +324,8 @@ if (NOT OPENSSL_FOUND)
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/") set(OPENSSLDIR "/etc/ssl/")
add_subdirectory(libressl EXCLUDE_FROM_ALL) add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl SYSTEM INTERFACE ./libressl/include) target_include_directories(ssl INTERFACE ./libressl/include)
target_disable_warnings(ssl)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP) target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
get_directory_property(OPENSSL_LIBRARIES get_directory_property(OPENSSL_LIBRARIES
DIRECTORY libressl DIRECTORY libressl
@ -327,17 +342,20 @@ if(USE_SYSTEM_CPP_HTTPLIB)
get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES) get_target_property(HTTP_LIBS httplib::httplib INTERFACE_LINK_LIBRARIES)
if(HTTP_LIBS) if(HTTP_LIBS)
message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...") message(WARNING "Shared cpp-http (${HTTP_LIBS}) not supported. Falling back to bundled...")
target_include_directories(httplib SYSTEM INTERFACE ./httplib) target_include_directories(httplib INTERFACE ./httplib)
target_disable_warnings(httplib)
else() else()
if(CppHttp_FOUND) if(CppHttp_FOUND)
target_link_libraries(httplib INTERFACE httplib::httplib) target_link_libraries(httplib INTERFACE httplib::httplib)
else() else()
message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...") message(STATUS "Cpp-httplib not found or not suitable version! Falling back to bundled...")
target_include_directories(httplib SYSTEM INTERFACE ./httplib) target_include_directories(httplib INTERFACE ./httplib)
target_disable_warnings(httplib)
endif() endif()
endif() endif()
else() else()
target_include_directories(httplib SYSTEM INTERFACE ./httplib) target_include_directories(httplib INTERFACE ./httplib)
target_disable_warnings(httplib)
endif() endif()
target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT) target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES}) target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
@ -354,7 +372,8 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt) target_link_libraries(cpp-jwt INTERFACE cpp-jwt::cpp-jwt)
else() else()
add_library(cpp-jwt INTERFACE) add_library(cpp-jwt INTERFACE)
target_include_directories(cpp-jwt SYSTEM INTERFACE ./cpp-jwt/include) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include)
target_disable_warnings(cpp-jwt)
target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON) target_compile_definitions(cpp-jwt INTERFACE CPP_JWT_USE_VENDORED_NLOHMANN_JSON)
endif() endif()
endif() endif()
@ -402,6 +421,24 @@ endif()
# Vulkan dependencies # Vulkan dependencies
if (ENABLE_VULKAN) if (ENABLE_VULKAN)
# spirv-headers
if(USE_SYSTEM_SPIRV_HEADERS)
find_package(SPIRV-Headers REQUIRED)
if(TARGET SPIRV-Headers::SPIRV-Headers)
message(STATUS "Found SPIRV headers")
get_target_property(SPIRV-Headers_SOURCE_DIR SPIRV-Headers::SPIRV-Headers INTERFACE_INCLUDE_DIRECTORIES)
set(SPIRV-Headers_SOURCE_DIR "${SPIRV-Headers_SOURCE_DIR}/../") # Not sure why this is necessary
endif()
else()
set(SPIRV-Headers_SOURCE_DIR "${CMAKE_SOURCE_DIR}/externals/spirv-headers")
add_subdirectory(spirv-headers EXCLUDE_FROM_ALL)
endif()
# spirv-tools
# TODO: Implement USE_SYSTEM_SPIRV_TOOLS -OS
set(SPIRV_SKIP_EXECUTABLES ON)
add_subdirectory(spirv-tools EXCLUDE_FROM_ALL)
# glslang # glslang
if(USE_SYSTEM_GLSLANG) if(USE_SYSTEM_GLSLANG)
find_package(glslang REQUIRED) find_package(glslang REQUIRED)
@ -413,10 +450,6 @@ if (ENABLE_VULKAN)
get_target_property(GLSLANG_PREFIX glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES) get_target_property(GLSLANG_PREFIX glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(SPIRV SYSTEM INTERFACE "${GLSLANG_PREFIX}/glslang") target_include_directories(SPIRV SYSTEM INTERFACE "${GLSLANG_PREFIX}/glslang")
else() else()
set(SPIRV-Headers_SOURCE_DIR "${CMAKE_SOURCE_DIR}/externals/spirv-headers")
add_subdirectory(spirv-headers EXCLUDE_FROM_ALL)
set(SPIRV_SKIP_EXECUTABLES ON)
add_subdirectory(spirv-tools EXCLUDE_FROM_ALL)
set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "") set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "")
set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "") set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "")
set(ENABLE_SPVREMAPPER OFF CACHE BOOL "") set(ENABLE_SPVREMAPPER OFF CACHE BOOL "")
@ -439,7 +472,8 @@ if (ENABLE_VULKAN)
endif() endif()
else() else()
add_library(vma INTERFACE) add_library(vma INTERFACE)
target_include_directories(vma SYSTEM INTERFACE ./vma/include) target_include_directories(vma INTERFACE ./vma/include)
target_disable_warnings(vma)
endif() endif()
# vulkan-headers # vulkan-headers
@ -451,7 +485,8 @@ if (ENABLE_VULKAN)
target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers) target_link_libraries(vulkan-headers INTERFACE Vulkan::Headers)
endif() endif()
else() else()
target_include_directories(vulkan-headers SYSTEM INTERFACE ./vulkan-headers/include) target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
target_disable_warnings(vulkan-headers)
endif() endif()
# adrenotools # adrenotools

View File

@ -25,6 +25,7 @@ option(USE_SYSTEM_LODEPNG "Use the system lodepng (instead of the bundled one)"
option(USE_SYSTEM_OPENAL "Use the system OpenAL (instead of the bundled one)" OFF) option(USE_SYSTEM_OPENAL "Use the system OpenAL (instead of the bundled one)" OFF)
option(USE_SYSTEM_VMA "Use the system VulkanMemoryAllocator (instead of the bundled one)" OFF) option(USE_SYSTEM_VMA "Use the system VulkanMemoryAllocator (instead of the bundled one)" OFF)
option(USE_SYSTEM_VULKAN_HEADERS "Use the system Vulkan headers (instead of the bundled ones)" OFF) option(USE_SYSTEM_VULKAN_HEADERS "Use the system Vulkan headers (instead of the bundled ones)" OFF)
option(USE_SYSTEM_SPIRV_HEADERS "Use the system SPIRV headers (instead of the bundled ones)" OFF)
option(USE_SYSTEM_CATCH2 "Use the system Catch2 (instead of the bundled one)" OFF) option(USE_SYSTEM_CATCH2 "Use the system Catch2 (instead of the bundled one)" OFF)
# Qt and MoltenVK are handled separately # Qt and MoltenVK are handled separately
@ -50,6 +51,7 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_LODEPNG "Disable system lodepng" OFF "USE_
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OPENAL "Disable system OpenAL" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OPENAL "Disable system OpenAL" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VMA "Disable system VulkanMemoryAllocator" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VMA "Disable system VulkanMemoryAllocator" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VULKAN_HEADERS "Disable system Vulkan headers" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VULKAN_HEADERS "Disable system Vulkan headers" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_SPIRV_HEADERS "Disable system SPIRV headers" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_CATCH2 "Disable system Catch2" OFF "USE_SYSTEM_LIBS" OFF) CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_CATCH2 "Disable system Catch2" OFF "USE_SYSTEM_LIBS" OFF)
set(LIB_VAR_LIST set(LIB_VAR_LIST
@ -75,6 +77,7 @@ set(LIB_VAR_LIST
OPENAL OPENAL
VMA VMA
VULKAN_HEADERS VULKAN_HEADERS
SPIRV_HEADERS
CATCH2 CATCH2
) )

2
externals/fmt vendored

@ -1 +1 @@
Subproject commit 123913715afeb8a437e6388b4473fcc4753e1c9a Subproject commit e424e3f2e607da02742f73db84873b8084fc714c

View File

@ -174,6 +174,9 @@ endif()
if(ENABLE_VULKAN) if(ENABLE_VULKAN)
add_compile_definitions(ENABLE_VULKAN) add_compile_definitions(ENABLE_VULKAN)
endif() endif()
if(ENABLE_DEVELOPER_OPTIONS)
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
endif()
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(core) add_subdirectory(core)

View File

@ -64,7 +64,7 @@ android {
// The application ID refers to Lime3DS to allow for // The application ID refers to Lime3DS to allow for
// the Play Store listing, which was originally set up for Lime3DS, to still be used. // the Play Store listing, which was originally set up for Lime3DS, to still be used.
applicationId = "io.github.lime3ds.android" applicationId = "io.github.lime3ds.android"
minSdk = 28 minSdk = 29
targetSdk = 35 targetSdk = 35
versionCode = autoVersion versionCode = autoVersion
versionName = getGitVersion() versionName = getGitVersion()
@ -186,7 +186,7 @@ dependencies {
// Download Vulkan Validation Layers from the KhronosGroup GitHub. // Download Vulkan Validation Layers from the KhronosGroup GitHub.
val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") { val downloadVulkanValidationLayers = tasks.register<Download>("downloadVulkanValidationLayers") {
src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.304.1/android-binaries-1.4.304.1.zip") src("https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download/vulkan-sdk-1.4.313.0/android-binaries-1.4.313.0.zip")
dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip")) dest(file("${layout.buildDirectory.get().asFile.path}/tmp/Vulkan-ValidationLayers.zip"))
onlyIfModified(true) onlyIfModified(true)
} }

View File

@ -96,6 +96,24 @@ object NativeLibrary {
*/ */
external fun onTouchMoved(xAxis: Float, yAxis: Float) external fun onTouchMoved(xAxis: Float, yAxis: Float)
/**
* Handles touch events on the secondary display.
*
* @param xAxis The value of the x-axis.
* @param yAxis The value of the y-axis.
* @param pressed To identify if the touch held down or released.
* @return true if the pointer is within the touchscreen
*/
external fun onSecondaryTouchEvent(xAxis: Float, yAxis: Float, pressed: Boolean): Boolean
/**
* Handles touch movement on the secondary display.
*
* @param xAxis The value of the instantaneous x-axis.
* @param yAxis The value of the instantaneous y-axis.
*/
external fun onSecondaryTouchMoved(xAxis: Float, yAxis: Float)
external fun reloadSettings() external fun reloadSettings()
external fun getTitleId(filename: String): Long external fun getTitleId(filename: String): Long
@ -124,6 +142,10 @@ object NativeLibrary {
external fun surfaceDestroyed() external fun surfaceDestroyed()
external fun doFrame() external fun doFrame()
// Second window
external fun secondarySurfaceChanged(secondary_surface: Surface)
external fun secondarySurfaceDestroyed()
/** /**
* Unpauses emulation from a paused state. * Unpauses emulation from a paused state.
*/ */

View File

@ -33,6 +33,7 @@ import org.citra.citra_emu.camera.StillImageCameraHelper.OnFilePickerResult
import org.citra.citra_emu.contracts.OpenFileResultContract import org.citra.citra_emu.contracts.OpenFileResultContract
import org.citra.citra_emu.databinding.ActivityEmulationBinding import org.citra.citra_emu.databinding.ActivityEmulationBinding
import org.citra.citra_emu.display.ScreenAdjustmentUtil import org.citra.citra_emu.display.ScreenAdjustmentUtil
import org.citra.citra_emu.display.SecondaryDisplay
import org.citra.citra_emu.features.hotkeys.HotkeyUtility import org.citra.citra_emu.features.hotkeys.HotkeyUtility
import org.citra.citra_emu.features.settings.model.BooleanSetting import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.IntSetting
@ -59,6 +60,15 @@ class EmulationActivity : AppCompatActivity() {
private lateinit var binding: ActivityEmulationBinding private lateinit var binding: ActivityEmulationBinding
private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil private lateinit var screenAdjustmentUtil: ScreenAdjustmentUtil
private lateinit var hotkeyUtility: HotkeyUtility private lateinit var hotkeyUtility: HotkeyUtility
private lateinit var secondaryDisplay: SecondaryDisplay
private val onShutdown = Runnable {
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
finishAffinity()
} else {
this.finish()
}
}
private val emulationFragment: EmulationFragment private val emulationFragment: EmulationFragment
get() { get() {
@ -73,10 +83,10 @@ class EmulationActivity : AppCompatActivity() {
requestWindowFeature(Window.FEATURE_NO_TITLE) requestWindowFeature(Window.FEATURE_NO_TITLE)
ThemeUtil.setTheme(this) ThemeUtil.setTheme(this)
settingsViewModel.settings.loadSettings() settingsViewModel.settings.loadSettings()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
secondaryDisplay = SecondaryDisplay(this)
secondaryDisplay.updateDisplay()
binding = ActivityEmulationBinding.inflate(layoutInflater) binding = ActivityEmulationBinding.inflate(layoutInflater)
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings) screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
@ -99,13 +109,7 @@ class EmulationActivity : AppCompatActivity() {
windowManager.defaultDisplay.rotation windowManager.defaultDisplay.rotation
) )
EmulationLifecycleUtil.addShutdownHook(hook = { EmulationLifecycleUtil.addShutdownHook(onShutdown)
if (intent.getBooleanExtra("launched_from_shortcut", false)) {
finishAffinity()
} else {
this.finish()
}
})
isEmulationRunning = true isEmulationRunning = true
instance = this instance = this
@ -136,6 +140,11 @@ class EmulationActivity : AppCompatActivity() {
applyOrientationSettings() // Check for orientation settings changes on runtime applyOrientationSettings() // Check for orientation settings changes on runtime
} }
override fun onStop() {
secondaryDisplay.releasePresentation()
super.onStop()
}
override fun onWindowFocusChanged(hasFocus: Boolean) { override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus) super.onWindowFocusChanged(hasFocus)
enableFullscreenImmersive() enableFullscreenImmersive()
@ -143,6 +152,7 @@ class EmulationActivity : AppCompatActivity() {
public override fun onRestart() { public override fun onRestart() {
super.onRestart() super.onRestart()
secondaryDisplay.updateDisplay()
NativeLibrary.reloadCameraDevices() NativeLibrary.reloadCameraDevices()
} }
@ -157,10 +167,13 @@ class EmulationActivity : AppCompatActivity() {
} }
override fun onDestroy() { override fun onDestroy() {
EmulationLifecycleUtil.clear() EmulationLifecycleUtil.removeHook(onShutdown)
NativeLibrary.playTimeManagerStop() NativeLibrary.playTimeManagerStop()
isEmulationRunning = false isEmulationRunning = false
instance = null instance = null
secondaryDisplay.releasePresentation()
secondaryDisplay.releaseVD()
super.onDestroy() super.onDestroy()
} }

View File

@ -50,3 +50,17 @@ enum class PortraitScreenLayout(val int: Int) {
} }
} }
} }
enum class SecondaryDisplayLayout(val int: Int) {
// These must match what is defined in src/common/settings.h
NONE(0),
TOP_SCREEN(1),
BOTTOM_SCREEN(2),
SIDE_BY_SIDE(3);
companion object {
fun from(int: Int): SecondaryDisplayLayout {
return entries.firstOrNull { it.int == int } ?: NONE
}
}
}

View File

@ -0,0 +1,169 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
package org.citra.citra_emu.display
import android.app.Presentation
import android.content.Context
import android.graphics.SurfaceTexture
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Bundle
import android.view.Display
import android.view.MotionEvent
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.WindowManager
import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.display.SecondaryDisplayLayout
import org.citra.citra_emu.NativeLibrary
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
private var pres: SecondaryDisplayPresentation? = null
private val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
private val vd: VirtualDisplay
init {
vd = displayManager.createVirtualDisplay(
"HiddenDisplay",
1920,
1080,
320,
null,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION
)
displayManager.registerDisplayListener(this, null)
}
fun updateSurface() {
NativeLibrary.secondarySurfaceChanged(pres!!.getSurfaceHolder().surface)
}
fun destroySurface() {
NativeLibrary.secondarySurfaceDestroyed()
}
private fun getExternalDisplay(context: Context): Display? {
val dm = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val internalId = context.display.displayId ?: Display.DEFAULT_DISPLAY
val displays = dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION)
return displays.firstOrNull { it.displayId != internalId && it.name != "HiddenDisplay" }
}
fun updateDisplay() {
// decide if we are going to the external display or the internal one
var display = getExternalDisplay(context)
if (display == null ||
IntSetting.SECONDARY_DISPLAY_LAYOUT.int == SecondaryDisplayLayout.NONE.int) {
display = vd.display
}
// if our presentation is already on the right display, ignore
if (pres?.display == display) return
// otherwise, make a new presentation
releasePresentation()
pres = SecondaryDisplayPresentation(context, display!!, this)
pres?.show()
}
fun releasePresentation() {
pres?.dismiss()
pres = null
}
fun releaseVD() {
displayManager.unregisterDisplayListener(this)
vd.release()
}
override fun onDisplayAdded(displayId: Int) {
updateDisplay()
}
override fun onDisplayRemoved(displayId: Int) {
updateDisplay()
}
override fun onDisplayChanged(displayId: Int) {
updateDisplay()
}
}
class SecondaryDisplayPresentation(
context: Context, display: Display, val parent: SecondaryDisplay
) : Presentation(context, display) {
private lateinit var surfaceView: SurfaceView
private var touchscreenPointerId = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window?.setFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
)
// Initialize SurfaceView
surfaceView = SurfaceView(context)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
}
override fun surfaceChanged(
holder: SurfaceHolder, format: Int, width: Int, height: Int
) {
parent.updateSurface()
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
parent.destroySurface()
}
})
this.surfaceView.setOnTouchListener { _, event ->
val pointerIndex = event.actionIndex
val pointerId = event.getPointerId(pointerIndex)
when (event.actionMasked) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
if (touchscreenPointerId == -1) {
touchscreenPointerId = pointerId
NativeLibrary.onSecondaryTouchEvent(
event.getX(pointerIndex),
event.getY(pointerIndex),
true
)
}
}
MotionEvent.ACTION_MOVE -> {
val index = event.findPointerIndex(touchscreenPointerId)
if (index != -1) {
NativeLibrary.onSecondaryTouchMoved(
event.getX(index),
event.getY(index)
)
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_CANCEL -> {
if (pointerId == touchscreenPointerId) {
NativeLibrary.onSecondaryTouchEvent(0f, 0f, false)
touchscreenPointerId = -1
}
}
}
true
}
setContentView(surfaceView) // Set SurfaceView as content
}
// Publicly accessible method to get the SurfaceHolder
fun getSurfaceHolder(): SurfaceHolder {
return surfaceView.holder
}
}

View File

@ -19,13 +19,14 @@ enum class BooleanSetting(
INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false), INSTANT_DEBUG_LOG("instant_debug_log", Settings.SECTION_DEBUG, false),
ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false), ENABLE_RPC_SERVER("enable_rpc_server", Settings.SECTION_DEBUG, false),
CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false), CUSTOM_LAYOUT("custom_layout",Settings.SECTION_LAYOUT,false),
OVERLAY_SHOW_FPS("overlay_show_fps", Settings.SECTION_LAYOUT, true), PERF_OVERLAY_ENABLE("performance_overlay_enable", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_FRAMETIME("overlay_show_frame_time", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_FPS("performance_overlay_show_fps", Settings.SECTION_LAYOUT, true),
OVERLAY_SHOW_SPEED("overlay_show_speed", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_FRAMETIME("performance_overlay_show_frame_time", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_APP_RAM_USAGE("overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_SPEED("performance_overlay_show_speed", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_AVAILABLE_RAM("overlay_show_available_ram", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_APP_RAM_USAGE("performance_overlay_show_app_ram_usage", Settings.SECTION_LAYOUT, false),
OVERLAY_SHOW_BATTERY_TEMP("overlay_show_battery_temp", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_AVAILABLE_RAM("performance_overlay_show_available_ram", Settings.SECTION_LAYOUT, false),
OVERLAY_BACKGROUND("overlay_background", Settings.SECTION_LAYOUT, false), PERF_OVERLAY_SHOW_BATTERY_TEMP("performance_overlay_show_battery_temp", Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_BACKGROUND("performance_overlay_background", Settings.SECTION_LAYOUT, false),
DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true), DELAY_START_LLE_MODULES("delay_start_for_lle_modules", Settings.SECTION_DEBUG, true),
DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false), DETERMINISTIC_ASYNC_OPERATIONS("deterministic_async_operations", Settings.SECTION_DEBUG, false),
REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false), REQUIRED_ONLINE_LLE_MODULES("enable_required_online_lle_modules", Settings.SECTION_SYSTEM, false),
@ -49,7 +50,8 @@ enum class BooleanSetting(
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false), DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, false),
USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false), USE_ARTIC_BASE_CONTROLLER("use_artic_base_controller", Settings.SECTION_CONTROLS, false),
UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false), UPRIGHT_SCREEN("upright_screen", Settings.SECTION_LAYOUT, false),
COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false); COMPRESS_INSTALLED_CIA_CONTENT("compress_cia_installs", Settings.SECTION_STORAGE, false),
ANDROID_HIDE_IMAGES("android_hide_images", Settings.SECTION_CORE, false);
override var boolean: Boolean = defaultValue override var boolean: Boolean = defaultValue
@ -83,6 +85,8 @@ enum class BooleanSetting(
SHADERS_ACCURATE_MUL, SHADERS_ACCURATE_MUL,
USE_ARTIC_BASE_CONTROLLER, USE_ARTIC_BASE_CONTROLLER,
COMPRESS_INSTALLED_CIA_CONTENT, COMPRESS_INSTALLED_CIA_CONTENT,
ANDROID_HIDE_IMAGES,
PERF_OVERLAY_ENABLE // Works in overlay options, but not from the settings menu
) )
fun from(key: String): BooleanSetting? = fun from(key: String): BooleanSetting? =

View File

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Lime3DS Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -10,6 +10,10 @@ enum class FloatSetting(
override val defaultValue: Float override val defaultValue: Float
) : AbstractFloatSetting { ) : AbstractFloatSetting {
LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f), LARGE_SCREEN_PROPORTION("large_screen_proportion",Settings.SECTION_LAYOUT,2.25f),
SECOND_SCREEN_OPACITY("custom_second_layer_opacity", Settings.SECTION_RENDERER, 100f),
BACKGROUND_RED("bg_red", Settings.SECTION_RENDERER, 0f),
BACKGROUND_BLUE("bg_blue", Settings.SECTION_RENDERER, 0f),
BACKGROUND_GREEN("bg_green", Settings.SECTION_RENDERER, 0f),
EMPTY_SETTING("", "", 0.0f); EMPTY_SETTING("", "", 0.0f);
override var float: Float = defaultValue override var float: Float = defaultValue

View File

@ -35,6 +35,7 @@ enum class IntSetting(
LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480), LANDSCAPE_BOTTOM_HEIGHT("custom_bottom_height",Settings.SECTION_LAYOUT,480),
SCREEN_GAP("screen_gap",Settings.SECTION_LAYOUT,0), SCREEN_GAP("screen_gap",Settings.SECTION_LAYOUT,0),
PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0), PORTRAIT_SCREEN_LAYOUT("portrait_layout_option",Settings.SECTION_LAYOUT,0),
SECONDARY_DISPLAY_LAYOUT("secondary_display_layout",Settings.SECTION_LAYOUT,0),
PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_X("custom_portrait_top_x",Settings.SECTION_LAYOUT,0),
PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0), PORTRAIT_TOP_Y("custom_portrait_top_y",Settings.SECTION_LAYOUT,0),
PORTRAIT_TOP_WIDTH("custom_portrait_top_width",Settings.SECTION_LAYOUT,800), PORTRAIT_TOP_WIDTH("custom_portrait_top_width",Settings.SECTION_LAYOUT,800),

View File

@ -1,4 +1,4 @@
// Copyright Citra Emulator Project / Lime3DS Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -10,7 +10,6 @@ import org.citra.citra_emu.features.settings.model.AbstractSetting
import org.citra.citra_emu.features.settings.model.FloatSetting import org.citra.citra_emu.features.settings.model.FloatSetting
import org.citra.citra_emu.features.settings.model.ScaledFloatSetting import org.citra.citra_emu.features.settings.model.ScaledFloatSetting
import org.citra.citra_emu.utils.Log import org.citra.citra_emu.utils.Log
import kotlin.math.roundToInt
class SliderSetting( class SliderSetting(
setting: AbstractSetting?, setting: AbstractSetting?,
@ -27,7 +26,8 @@ class SliderSetting(
val selectedFloat: Float val selectedFloat: Float
get() { get() {
val setting = setting ?: return defaultValue!!.toFloat() val setting = setting ?: return defaultValue!!.toFloat()
return when (setting) {
val ret = when (setting) {
is AbstractIntSetting -> setting.int.toFloat() is AbstractIntSetting -> setting.int.toFloat()
is FloatSetting -> setting.float is FloatSetting -> setting.float
is ScaledFloatSetting -> setting.float is ScaledFloatSetting -> setting.float
@ -36,8 +36,8 @@ class SliderSetting(
-1f -1f
} }
} }
return ret.coerceIn(min.toFloat(), max.toFloat())
} }
/** /**
* Write a value to the backing int. If that int was previously null, * Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap. * initializes a new one and returns it, so it can be added to the Hashmap.

View File

@ -4,14 +4,19 @@
package org.citra.citra_emu.features.settings.ui package org.citra.citra_emu.features.settings.ui
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import androidx.documentfile.provider.DocumentFile
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.Settings import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.utils.SystemSaveGame import org.citra.citra_emu.utils.SystemSaveGame
import org.citra.citra_emu.utils.DirectoryInitialization import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.Log import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.utils.PermissionsHandler
import org.citra.citra_emu.utils.TurboHelper import org.citra.citra_emu.utils.TurboHelper
class SettingsActivityPresenter(private val activityView: SettingsActivityView) { class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
@ -60,6 +65,32 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
loadSettingsUI() loadSettingsUI()
} }
private fun updateAndroidImageVisibility() {
val dataDirTreeUri: Uri
val dataDirDocument: DocumentFile
val nomediaFileDocument: DocumentFile?
val nomediaFileExists: Boolean
try {
dataDirTreeUri = PermissionsHandler.citraDirectory
dataDirDocument = DocumentFile.fromTreeUri(CitraApplication.appContext, dataDirTreeUri)!!
nomediaFileDocument = dataDirDocument.findFile(".nomedia")
nomediaFileExists = (nomediaFileDocument != null)
} catch (e: Exception) {
Log.error("[SettingsActivity]: Error occurred while trying to find .nomedia, error: " + e.message)
return
}
if (BooleanSetting.ANDROID_HIDE_IMAGES.boolean) {
if (!nomediaFileExists) {
Log.info("[SettingsActivity]: Attempting to create .nomedia in user data directory")
FileUtil.createFile(dataDirTreeUri.toString(), ".nomedia")
}
} else if (nomediaFileExists) {
Log.info("[SettingsActivity]: Attempting to delete .nomedia in user data directory")
nomediaFileDocument!!.delete()
}
}
fun onStop(finishing: Boolean) { fun onStop(finishing: Boolean) {
if (finishing && shouldSave) { if (finishing && shouldSave) {
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
@ -67,6 +98,7 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
//added to ensure that layout changes take effect as soon as settings window closes //added to ensure that layout changes take effect as soon as settings window closes
NativeLibrary.reloadSettings() NativeLibrary.reloadSettings()
NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode) NativeLibrary.updateFramebuffer(NativeLibrary.isPortraitMode)
updateAndroidImageVisibility()
TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS TurboHelper.reloadTurbo(false) // TODO: Can this go somewhere else? -OS
} }
NativeLibrary.reloadSettings() NativeLibrary.reloadSettings()

View File

@ -14,11 +14,8 @@ import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlin.math.min
import org.citra.citra_emu.CitraApplication import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.R import org.citra.citra_emu.R
import org.citra.citra_emu.display.PortraitScreenLayout
import org.citra.citra_emu.display.ScreenLayout
import org.citra.citra_emu.features.settings.model.AbstractBooleanSetting import org.citra.citra_emu.features.settings.model.AbstractBooleanSetting
import org.citra.citra_emu.features.settings.model.AbstractIntSetting import org.citra.citra_emu.features.settings.model.AbstractIntSetting
import org.citra.citra_emu.features.settings.model.AbstractSetting import org.citra.citra_emu.features.settings.model.AbstractSetting
@ -251,6 +248,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.TURBO_LIMIT.defaultValue.toFloat() IntSetting.TURBO_LIMIT.defaultValue.toFloat()
) )
) )
add(
SwitchSetting(
BooleanSetting.ANDROID_HIDE_IMAGES,
R.string.android_hide_images,
R.string.android_hide_images_description,
BooleanSetting.ANDROID_HIDE_IMAGES.key,
BooleanSetting.ANDROID_HIDE_IMAGES.defaultValue
)
)
} }
} }
@ -1111,6 +1117,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue
) )
) )
add(
SingleChoiceSetting(
IntSetting.SECONDARY_DISPLAY_LAYOUT,
R.string.emulation_switch_secondary_layout,
R.string.emulation_switch_secondary_layout_description,
R.array.secondaryLayouts,
R.array.secondaryLayoutValues,
IntSetting.SECONDARY_DISPLAY_LAYOUT.key,
IntSetting.SECONDARY_DISPLAY_LAYOUT.defaultValue
)
)
add( add(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.ASPECT_RATIO, IntSetting.ASPECT_RATIO,
@ -1158,6 +1175,89 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue FloatSetting.LARGE_SCREEN_PROPORTION.defaultValue
) )
) )
add(
SliderSetting(
FloatSetting.SECOND_SCREEN_OPACITY,
R.string.second_screen_opacity,
R.string.second_screen_opacity_description,
0,
100,
"%",
FloatSetting.SECOND_SCREEN_OPACITY.key,
FloatSetting.SECOND_SCREEN_OPACITY.defaultValue,
isEnabled = IntSetting.SCREEN_LAYOUT.int == 5
)
)
add(HeaderSetting(R.string.bg_color, R.string.bg_color_description))
val bgRedSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_RED.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_RED.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_RED, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
}
add(
SliderSetting(
bgRedSetting,
R.string.bg_red,
0,
0,
255,
""
)
)
val bgGreenSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_GREEN.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_GREEN.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_GREEN, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
}
add(
SliderSetting(
bgGreenSetting,
R.string.bg_green,
0,
0,
255,
""
)
)
val bgBlueSetting = object : AbstractIntSetting {
override var int: Int
get() = (FloatSetting.BACKGROUND_BLUE.float * 255).toInt()
set(value) {
FloatSetting.BACKGROUND_BLUE.float = value.toFloat() / 255
settings.saveSetting(FloatSetting.BACKGROUND_BLUE, SettingsFile.FILE_NAME_CONFIG)
}
override val key = null
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
}
add(
SliderSetting(
bgBlueSetting,
R.string.bg_blue,
0,
0,
255,
""
)
)
add( add(
SubmenuSetting( SubmenuSetting(
R.string.performance_overlay_options, R.string.performance_overlay_options,
@ -1193,38 +1293,29 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add( add(
SwitchSetting( SwitchSetting(
object : AbstractBooleanSetting { BooleanSetting.PERF_OVERLAY_ENABLE,
override val key = "EmulationMenuSettings_showPerfPerformanceOverlay"
override val section = Settings.SECTION_LAYOUT
override val defaultValue = false
override var boolean: Boolean
get() = EmulationMenuSettings.showPerformanceOverlay
set(value) { EmulationMenuSettings.showPerformanceOverlay = value }
override val isRuntimeEditable = true
override val valueAsString: String get() = boolean.toString()
},
R.string.performance_overlay_enable, R.string.performance_overlay_enable,
0, 0,
"EmulationMenuSettings_showPerfPerformanceOverlay", BooleanSetting.PERF_OVERLAY_ENABLE.key,
false BooleanSetting.PERF_OVERLAY_ENABLE.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_BACKGROUND, BooleanSetting.PERF_OVERLAY_BACKGROUND,
R.string.overlay_background, R.string.performance_overlay_background,
R.string.overlay_background_description, R.string.performance_overlay_background_description,
"overlay_background", BooleanSetting.PERF_OVERLAY_BACKGROUND.key,
false BooleanSetting.PERF_OVERLAY_BACKGROUND.defaultValue
) )
) )
add( add(
SingleChoiceSetting( SingleChoiceSetting(
IntSetting.PERFORMANCE_OVERLAY_POSITION, IntSetting.PERFORMANCE_OVERLAY_POSITION,
R.string.overlay_position, R.string.performance_overlay_position,
R.string.overlay_position_description, R.string.performance_overlay_position_description,
R.array.statsPosition, R.array.statsPosition,
R.array.statsPositionValues, R.array.statsPositionValues,
) )
@ -1235,61 +1326,61 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FPS, BooleanSetting.PERF_OVERLAY_SHOW_FPS,
R.string.overlay_show_fps, R.string.performance_overlay_show_fps,
R.string.overlay_show_fps_description, R.string.performance_overlay_show_fps_description,
"overlay_show_fps", BooleanSetting.PERF_OVERLAY_SHOW_FPS.key,
true BooleanSetting.PERF_OVERLAY_SHOW_FPS.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_FRAMETIME, BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME,
R.string.overlay_show_frametime, R.string.performance_overlay_show_frametime,
R.string.overlay_show_frametime_description, R.string.performance_overlay_show_frametime_description,
"overlay_show_frame_time", BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.key,
true BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_SPEED, BooleanSetting.PERF_OVERLAY_SHOW_SPEED,
R.string.overlay_show_speed, R.string.performance_overlay_show_speed,
R.string.overlay_show_speed_description, R.string.performance_overlay_show_speed_description,
"overlay_show_speed", BooleanSetting.PERF_OVERLAY_SHOW_SPEED.key,
false BooleanSetting.PERF_OVERLAY_SHOW_SPEED.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE, BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE,
R.string.overlay_show_app_ram_usage, R.string.performance_overlay_show_app_ram_usage,
R.string.overlay_show_app_ram_usage_description, R.string.performance_overlay_show_app_ram_usage_description,
"overlay_show_app_ram_usage", BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.key,
false BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM, BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM,
R.string.overlay_show_available_ram, R.string.performance_overlay_show_available_ram,
R.string.overlay_show_available_ram_description, R.string.performance_overlay_show_available_ram_description,
"overlay_show_available_ram", BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.key,
false BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.defaultValue
) )
) )
add( add(
SwitchSetting( SwitchSetting(
BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP, BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP,
R.string.overlay_show_battery_temp, R.string.performance_overlay_show_battery_temp,
R.string.overlay_show_battery_temp_description, R.string.performance_overlay_show_battery_temp_description,
"overlay_show_battery_temp", BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.key,
false BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.defaultValue
) )
) )
} }

View File

@ -66,6 +66,7 @@ import org.citra.citra_emu.display.ScreenAdjustmentUtil
import org.citra.citra_emu.display.ScreenLayout import org.citra.citra_emu.display.ScreenLayout
import org.citra.citra_emu.features.settings.model.BooleanSetting import org.citra.citra_emu.features.settings.model.BooleanSetting
import org.citra.citra_emu.features.settings.model.IntSetting import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.features.settings.model.SettingsViewModel import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile import org.citra.citra_emu.features.settings.utils.SettingsFile
@ -100,6 +101,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private val emulationViewModel: EmulationViewModel by activityViewModels() private val emulationViewModel: EmulationViewModel by activityViewModels()
private val settingsViewModel: SettingsViewModel by viewModels() private val settingsViewModel: SettingsViewModel by viewModels()
private val settings get() = settingsViewModel.settings
private val onPause = Runnable{ togglePause() }
private val onShutdown = Runnable{ emulationState.stop() }
override fun onAttach(context: Context) { override fun onAttach(context: Context) {
super.onAttach(context) super.onAttach(context)
@ -155,9 +160,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
retainInstance = true retainInstance = true
emulationState = EmulationState(game.path) emulationState = EmulationState(game.path)
emulationActivity = requireActivity() as EmulationActivity emulationActivity = requireActivity() as EmulationActivity
screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settingsViewModel.settings) screenAdjustmentUtil = ScreenAdjustmentUtil(requireContext(), requireActivity().windowManager, settings)
EmulationLifecycleUtil.addShutdownHook(hook = { emulationState.stop() }) EmulationLifecycleUtil.addPauseResumeHook(onPause)
EmulationLifecycleUtil.addPauseResumeHook(hook = { togglePause() }) EmulationLifecycleUtil.addShutdownHook(onShutdown)
} }
override fun onCreateView( override fun onCreateView(
@ -507,6 +512,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onDetach() super.onDetach()
} }
override fun onDestroy() {
EmulationLifecycleUtil.removeHook(onPause)
EmulationLifecycleUtil.removeHook(onShutdown)
super.onDestroy()
}
private fun setupCitraDirectoriesThenStartEmulation() { private fun setupCitraDirectoriesThenStartEmulation() {
val directoryInitializationState = DirectoryInitialization.start() val directoryInitializationState = DirectoryInitialization.start()
if (directoryInitializationState === if (directoryInitializationState ===
@ -662,7 +673,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.menu.apply { popupMenu.menu.apply {
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_performance_overlay_show).isChecked = findItem(R.id.menu_performance_overlay_show).isChecked =
EmulationMenuSettings.showPerformanceOverlay BooleanSetting.PERF_OVERLAY_ENABLE.boolean
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_emulation_joystick_rel_center).isChecked = findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
EmulationMenuSettings.joystickRelCenter EmulationMenuSettings.joystickRelCenter
@ -679,7 +690,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
} }
R.id.menu_performance_overlay_show -> { R.id.menu_performance_overlay_show -> {
EmulationMenuSettings.showPerformanceOverlay = !EmulationMenuSettings.showPerformanceOverlay BooleanSetting.PERF_OVERLAY_ENABLE.boolean = !BooleanSetting.PERF_OVERLAY_ENABLE.boolean
settings.saveSetting(BooleanSetting.PERF_OVERLAY_ENABLE, SettingsFile.FILE_NAME_CONFIG)
updateShowPerformanceOverlay() updateShowPerformanceOverlay()
true true
} }
@ -1202,7 +1214,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!) perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
} }
if (EmulationMenuSettings.showPerformanceOverlay) { if (BooleanSetting.PERF_OVERLAY_ENABLE.boolean) {
val SYSTEM_FPS = 0 val SYSTEM_FPS = 0
val FPS = 1 val FPS = 1
val SPEED = 2 val SPEED = 2
@ -1217,11 +1229,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val dividerString = "\u00A0\u2502 " val dividerString = "\u00A0\u2502 "
if (perfStats[FPS] > 0) { if (perfStats[FPS] > 0) {
if (BooleanSetting.OVERLAY_SHOW_FPS.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_FPS.boolean) {
sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt())) sb.append(String.format("FPS:\u00A0%d", (perfStats[FPS] + 0.5).toInt()))
} }
if (BooleanSetting.OVERLAY_SHOW_FRAMETIME.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_FRAMETIME.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString) if (sb.isNotEmpty()) sb.append(dividerString)
sb.append( sb.append(
String.format( String.format(
@ -1236,7 +1248,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
) )
} }
if (BooleanSetting.OVERLAY_SHOW_SPEED.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_SPEED.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString) if (sb.isNotEmpty()) sb.append(dividerString)
sb.append( sb.append(
String.format( String.format(
@ -1246,14 +1258,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
) )
} }
if (BooleanSetting.OVERLAY_SHOW_APP_RAM_USAGE.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_APP_RAM_USAGE.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString) if (sb.isNotEmpty()) sb.append(dividerString)
val appRamUsage = val appRamUsage =
File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000 File("/proc/self/statm").readLines()[0].split(' ')[1].toLong() * 4096 / 1000000
sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB") sb.append("Process\u00A0RAM:\u00A0$appRamUsage\u00A0MB")
} }
if (BooleanSetting.OVERLAY_SHOW_AVAILABLE_RAM.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_AVAILABLE_RAM.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString) if (sb.isNotEmpty()) sb.append(dividerString)
context?.let { ctx -> context?.let { ctx ->
val activityManager = val activityManager =
@ -1266,14 +1278,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
} }
} }
if (BooleanSetting.OVERLAY_SHOW_BATTERY_TEMP.boolean) { if (BooleanSetting.PERF_OVERLAY_SHOW_BATTERY_TEMP.boolean) {
if (sb.isNotEmpty()) sb.append(dividerString) if (sb.isNotEmpty()) sb.append(dividerString)
val batteryTemp = getBatteryTemperature() val batteryTemp = getBatteryTemperature()
val tempF = celsiusToFahrenheit(batteryTemp) val tempF = celsiusToFahrenheit(batteryTemp)
sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF)) sb.append(String.format("%.1f°C/%.1f°F", batteryTemp, tempF))
} }
if (BooleanSetting.OVERLAY_BACKGROUND.boolean) { if (BooleanSetting.PERF_OVERLAY_BACKGROUND.boolean) {
binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black) binding.performanceOverlayShowText.setBackgroundResource(R.color.citra_transparent_black)
} else { } else {
binding.performanceOverlayShowText.setBackgroundResource(0) binding.performanceOverlayShowText.setBackgroundResource(0)

View File

@ -64,7 +64,7 @@ class Game(
val allExtensions: Set<String> get() = extensions + badExtensions val allExtensions: Set<String> get() = extensions + badExtensions
val extensions: Set<String> = HashSet( val extensions: Set<String> = HashSet(
listOf("3dsx", "elf", "axf", "cci", "cxi", "app") listOf("3dsx", "app", "axf", "cci", "cxi", "elf", "z3dsx", "zcci", "zcxi")
) )
val badExtensions: Set<String> = HashSet( val badExtensions: Set<String> = HashSet(

View File

@ -97,11 +97,6 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
return onTouchWhileEditing(event) return onTouchWhileEditing(event)
} }
val pointerIndex = event.actionIndex
val pointerId = event.getPointerId(pointerIndex)
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
val motionEvent = event.action and MotionEvent.ACTION_MASK val motionEvent = event.action and MotionEvent.ACTION_MASK
val isActionDown = val isActionDown =
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
@ -109,70 +104,70 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
val isActionUp = val isActionUp =
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
val pointerList = (0 until event.pointerCount).toMutableList()
// Move the pointer that triggered the most recent event to the front
// of the list so that it is processed first
val currentActionPointer = event.actionIndex
pointerList.remove(pointerList.indexOf(currentActionPointer))
pointerList.add(0, currentActionPointer)
// Set up a loop for if we need to check touches other than the most recent one
// (Only happens if we're dragging the touch)
for (pointerIndex in pointerList) {
val pointerId = event.getPointerId(pointerIndex)
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
var hasActiveButtons = false var hasActiveButtons = false
var isAnyButtonSliding = false
for (button in overlayButtons) { for (button in overlayButtons) {
if (button.trackId == pointerId) { if (button.trackId == pointerId) {
hasActiveButtons = true hasActiveButtons = true
} break
if (button.isButtonSliding) {
if (isActionUp) {
// A touch event ended, so tell any buttons tracking sliding to stop
button.isButtonSliding = false
} else {
isAnyButtonSliding = true
}
} }
} }
var hasActiveDpad = false var hasActiveDpad = false
if (!hasActiveButtons) {
for (dpad in overlayDpads) { for (dpad in overlayDpads) {
if (dpad.trackId == pointerId) { if (dpad.trackId == pointerId) {
hasActiveDpad = true hasActiveDpad = true
break break
} }
} }
}
var hasActiveJoystick = false var hasActiveJoystick = false
if(!hasActiveButtons && !hasActiveDpad){
for (joystick in overlayJoysticks) { for (joystick in overlayJoysticks) {
if (joystick.trackId == pointerId) { if (joystick.trackId == pointerId) {
hasActiveJoystick = true hasActiveJoystick = true
break break
} }
} }
var shouldUpdateView = false
if (!preferences.getBoolean("isTouchEnabled", true)) {
return true
} }
val hasActiveOverlay = hasActiveButtons || hasActiveDpad || hasActiveJoystick
if (preferences.getBoolean("isTouchEnabled", true) && !hasActiveOverlay) {
if (isActionMove) { if (isActionMove) {
var noneConsumed = true
for (i in 0 until event.pointerCount) {
val fingerId = event.getPointerId(i)
if (isTouchInputConsumed(fingerId)) {
noneConsumed = false
continue
}
NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat()) NativeLibrary.onTouchMoved(xPosition.toFloat(), yPosition.toFloat())
} continue
} else if (isActionUp) {
if (noneConsumed && !isAnyButtonSliding) {
return true
}
}
if (isActionUp && !isTouchInputConsumed(pointerId)) {
NativeLibrary.onTouchEvent(0f, 0f, false) NativeLibrary.onTouchEvent(0f, 0f, false)
break // Up and down actions shouldn't loop
}
} }
var anyStateChanged = false var anyOverlayStateChanged = false
var shouldUpdateView = false
if(!hasActiveDpad && !hasActiveJoystick) { if(!hasActiveDpad && !hasActiveJoystick) {
for (button in overlayButtons) { for (button in overlayButtons) {
val stateChanged = button.updateStatus(event, hasActiveButtons, this) val stateChanged = button.updateStatus(event, pointerIndex, hasActiveButtons, this)
if (!stateChanged) { if (!stateChanged) {
continue continue
} }
anyStateChanged = true anyOverlayStateChanged = true
if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP && button.status == NativeLibrary.ButtonState.PRESSED) { if (button.id == NativeLibrary.ButtonType.BUTTON_SWAP && button.status == NativeLibrary.ButtonState.PRESSED) {
swapScreen() swapScreen()
@ -195,6 +190,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
for (dpad in overlayDpads) { for (dpad in overlayDpads) {
val stateChanged = dpad.updateStatus( val stateChanged = dpad.updateStatus(
event, event,
pointerIndex,
hasActiveDpad, hasActiveDpad,
EmulationMenuSettings.dpadSlide, EmulationMenuSettings.dpadSlide,
this this
@ -202,7 +198,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
if (!stateChanged) { if (!stateChanged) {
continue continue
} }
anyStateChanged = true anyOverlayStateChanged = true
NativeLibrary.onGamePadEvent( NativeLibrary.onGamePadEvent(
NativeLibrary.TouchScreenDevice, NativeLibrary.TouchScreenDevice,
@ -229,12 +225,13 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
} }
} }
if(!hasActiveDpad && !hasActiveButtons) {
for (joystick in overlayJoysticks) { for (joystick in overlayJoysticks) {
val stateChanged = joystick.updateStatus(event, true, this) val stateChanged = joystick.updateStatus(event, pointerIndex, hasActiveJoystick, this)
if (!stateChanged) { if (!stateChanged) {
continue continue
} }
anyStateChanged = true anyOverlayStateChanged = true
val axisID = joystick.joystickId val axisID = joystick.joystickId
NativeLibrary.onGamePadMoveEvent( NativeLibrary.onGamePadMoveEvent(
@ -246,36 +243,48 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
shouldUpdateView = true shouldUpdateView = true
} }
}
if (shouldUpdateView) { if (shouldUpdateView) {
invalidate() invalidate()
} }
if (!anyStateChanged && isActionDown && !isTouchInputConsumed(pointerId)) { if (preferences.getBoolean("isTouchEnabled", true) &&
isActionDown &&
!anyOverlayStateChanged
) {
// These need to be recalculated because touching the area
// right in the middle of the dpad (between the "buttons") or
// tapping a joystick in a certain way both don't cause
// `anyOverlayStateChanged` to be set to true
var isDpadPressed = false
for (dpad in overlayDpads) {
if (dpad.trackId == pointerId) {
isDpadPressed = true
break
}
}
var isJoystickPressed = false
for (joystick in overlayJoysticks) {
if (joystick.trackId == pointerId) {
isJoystickPressed = true
break
}
}
if (!isDpadPressed && !isJoystickPressed) {
NativeLibrary.onTouchEvent(xPosition.toFloat(), yPosition.toFloat(), true) NativeLibrary.onTouchEvent(xPosition.toFloat(), yPosition.toFloat(), true)
} }
return true
} }
private fun isTouchInputConsumed(trackId: Int): Boolean { // We should only loop here if touch is being dragged
overlayButtons.forEach { if (!isActionMove) {
if (it.trackId == trackId) { break
}
}
return true return true
} }
}
overlayDpads.forEach {
if (it.trackId == trackId) {
return true
}
}
overlayJoysticks.forEach {
if (it.trackId == trackId) {
return true
}
}
return false
}
fun onTouchWhileEditing(event: MotionEvent): Boolean { fun onTouchWhileEditing(event: MotionEvent): Boolean {
val pointerIndex = event.actionIndex val pointerIndex = event.actionIndex

View File

@ -44,7 +44,6 @@ class InputOverlayDrawableButton(
val opacity: Int val opacity: Int
) { ) {
var trackId: Int var trackId: Int
var isButtonSliding: Boolean
private var isMotionFirstButton = false // mark the first activated button with the current motion private var isMotionFirstButton = false // mark the first activated button with the current motion
@ -62,7 +61,6 @@ class InputOverlayDrawableButton(
this.defaultStateBitmap = BitmapDrawable(res, defaultStateBitmap) this.defaultStateBitmap = BitmapDrawable(res, defaultStateBitmap)
this.pressedStateBitmap = BitmapDrawable(res, pressedStateBitmap) this.pressedStateBitmap = BitmapDrawable(res, pressedStateBitmap)
trackId = -1 trackId = -1
isButtonSliding = false
width = this.defaultStateBitmap.intrinsicWidth width = this.defaultStateBitmap.intrinsicWidth
height = this.defaultStateBitmap.intrinsicHeight height = this.defaultStateBitmap.intrinsicHeight
} }
@ -72,9 +70,8 @@ class InputOverlayDrawableButton(
* *
* @return true if value was changed * @return true if value was changed
*/ */
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean { fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
val buttonSliding = EmulationMenuSettings.buttonSlide val buttonSliding = EmulationMenuSettings.buttonSlide
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt() val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt() val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex) val pointerId = event.getPointerId(pointerIndex)
@ -107,11 +104,14 @@ class InputOverlayDrawableButton(
if (inside || trackId != pointerId) { if (inside || trackId != pointerId) {
return false return false
} }
// prevent the first (directly pressed) button to deactivate when sliding off // prevent the first (directly pressed) button to deactivate when sliding off
if (buttonSliding == ButtonSlidingMode.Alternative.int && isMotionFirstButton) { if (buttonSliding == ButtonSlidingMode.Alternative.int && isMotionFirstButton) {
return false return false
} }
buttonUp(overlay, true)
val preserveTrackId = (buttonSliding != ButtonSlidingMode.Disabled.int)
buttonUp(overlay, preserveTrackId)
return true return true
} else { } else {
// button was not yet pressed // button was not yet pressed
@ -134,11 +134,12 @@ class InputOverlayDrawableButton(
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
} }
private fun buttonUp(overlay: InputOverlay, _isButtonSliding: Boolean) { private fun buttonUp(overlay: InputOverlay, preserveTrackId: Boolean) {
pressedState = false pressedState = false
isMotionFirstButton = false isMotionFirstButton = false
if (!preserveTrackId) {
trackId = -1 trackId = -1
isButtonSliding = _isButtonSliding }
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE) overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
} }

View File

@ -63,9 +63,8 @@ class InputOverlayDrawableDpad(
trackId = -1 trackId = -1
} }
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, dpadSlide: Boolean, overlay: InputOverlay): Boolean { fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, dpadSlide: Boolean, overlay: InputOverlay): Boolean {
var isDown = false var isDown = false
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt() val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt() val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex) val pointerId = event.getPointerId(pointerIndex)

View File

@ -93,8 +93,7 @@ class InputOverlayDrawableJoystick(
currentStateBitmapDrawable.draw(canvas) currentStateBitmapDrawable.draw(canvas)
} }
fun updateStatus(event: MotionEvent, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean { fun updateStatus(event: MotionEvent, pointerIndex: Int, hasActiveButtons: Boolean, overlay: InputOverlay): Boolean {
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt() val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt() val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex) val pointerId = event.getPointerId(pointerIndex)

View File

@ -332,7 +332,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} }
val selectedFiles = val selectedFiles =
FileBrowserHelper.getSelectedFiles(result, applicationContext, listOf("cia")) FileBrowserHelper.getSelectedFiles(result, applicationContext, listOf("cia", "zcia"))
if (selectedFiles == null) { if (selectedFiles == null) {
Toast.makeText(applicationContext, R.string.cia_file_not_found, Toast.LENGTH_LONG) Toast.makeText(applicationContext, R.string.cia_file_not_found, Toast.LENGTH_LONG)
.show() .show()

View File

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -18,15 +18,27 @@ object EmulationLifecycleUtil {
} }
fun addShutdownHook(hook: Runnable) { fun addShutdownHook(hook: Runnable) {
if (shutdownHooks.contains(hook)) {
Log.warning("[EmulationLifecycleUtil] Tried to add shutdown hook for function that already existed. Skipping.")
} else {
shutdownHooks.add(hook) shutdownHooks.add(hook)
} }
}
fun addPauseResumeHook(hook: Runnable) { fun addPauseResumeHook(hook: Runnable) {
if (pauseResumeHooks.contains(hook)) {
Log.warning("[EmulationLifecycleUtil] Tried to add pause resume hook for function that already existed. Skipping.")
} else {
pauseResumeHooks.add(hook) pauseResumeHooks.add(hook)
} }
}
fun clear() { fun removeHook(hook: Runnable) {
pauseResumeHooks.clear() if (pauseResumeHooks.contains(hook)) {
shutdownHooks.clear() pauseResumeHooks.remove(hook)
}
if (shutdownHooks.contains(hook)) {
shutdownHooks.remove(hook)
}
} }
} }

View File

@ -35,13 +35,6 @@ object EmulationMenuSettings {
.apply() .apply()
} }
var showPerformanceOverlay: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_showPerformanceOverlay", false)
set(value) {
preferences.edit()
.putBoolean("EmulationMenuSettings_showPerformanceOverlay", value)
.apply()
}
var hapticFeedback: Boolean var hapticFeedback: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true) get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
set(value) { set(value) {

View File

@ -172,6 +172,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_red); ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green); ReadSetting("Renderer", Settings::values.bg_green);
ReadSetting("Renderer", Settings::values.bg_blue); ReadSetting("Renderer", Settings::values.bg_blue);
ReadSetting("Renderer", Settings::values.custom_second_layer_opacity);
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us); ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
ReadSetting("Renderer", Settings::values.disable_right_eye_render); ReadSetting("Renderer", Settings::values.disable_right_eye_render);
@ -209,6 +210,9 @@ void Config::ReadValues() {
static_cast<Settings::PortraitLayoutOption>(sdl2_config->GetInteger( static_cast<Settings::PortraitLayoutOption>(sdl2_config->GetInteger(
"Layout", "portrait_layout_option", "Layout", "portrait_layout_option",
static_cast<int>(Settings::PortraitLayoutOption::PortraitTopFullWidth))); static_cast<int>(Settings::PortraitLayoutOption::PortraitTopFullWidth)));
Settings::values.secondary_display_layout = static_cast<Settings::SecondaryDisplayLayout>(
sdl2_config->GetInteger("Layout", "secondary_display_layout",
static_cast<int>(Settings::SecondaryDisplayLayout::None)));
ReadSetting("Layout", Settings::values.custom_portrait_top_x); ReadSetting("Layout", Settings::values.custom_portrait_top_x);
ReadSetting("Layout", Settings::values.custom_portrait_top_y); ReadSetting("Layout", Settings::values.custom_portrait_top_y);
ReadSetting("Layout", Settings::values.custom_portrait_top_width); ReadSetting("Layout", Settings::values.custom_portrait_top_width);

View File

@ -170,6 +170,9 @@ bg_red =
bg_blue = bg_blue =
bg_green = bg_green =
# Opacity of second layer when using custom layout option (bottom screen unless swapped). Useful if positioning on top of the first layer.
custom_second_layer_opacity =
# Whether and how Stereoscopic 3D should be rendered # Whether and how Stereoscopic 3D should be rendered
# 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR # 0 (default): Off, 1: Side by Side, 2: Reverse Side by Side, 3: Anaglyph, 4: Interlaced, 5: Reverse Interlaced, 6: Cardboard VR
render_3d = render_3d =
@ -288,6 +291,15 @@ swap_screen =
# 0 (default): Off, 1: On # 0 (default): Off, 1: On
expand_to_cutout_area = expand_to_cutout_area =
# Secondary Display Layout
# What the game should do if a secondary display is connected physically or using
# Miracast / Chromecast screen mirroring
# 0 (default) - Use System Default (mirror)
# 1 - Show Top Screen Only
# 2 - Show Bottom Screen Only
# 3 - Show both screens side by side
secondary_display_layout =
# Screen placement settings when using Cardboard VR (render3d = 4) # Screen placement settings when using Cardboard VR (render3d = 4)
# 30 - 100: Screen size as a percentage of the viewport. 85 (default) # 30 - 100: Screen size as a percentage of the viewport. 85 (default)
cardboard_screen_size = cardboard_screen_size =

View File

@ -1,4 +1,4 @@
// Copyright 2019 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -49,16 +49,16 @@ void EmuWindow_Android::OnFramebufferSizeChanged() {
const int bigger{window_width > window_height ? window_width : window_height}; const int bigger{window_width > window_height ? window_width : window_height};
const int smaller{window_width < window_height ? window_width : window_height}; const int smaller{window_width < window_height ? window_width : window_height};
if (is_portrait_mode) { if (is_portrait_mode && !is_secondary) {
UpdateCurrentFramebufferLayout(smaller, bigger, is_portrait_mode); UpdateCurrentFramebufferLayout(smaller, bigger, is_portrait_mode);
} else { } else {
UpdateCurrentFramebufferLayout(bigger, smaller, is_portrait_mode); UpdateCurrentFramebufferLayout(bigger, smaller, is_portrait_mode);
} }
} }
EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface) : host_window{surface} { EmuWindow_Android::EmuWindow_Android(ANativeWindow* surface, bool is_secondary)
: EmuWindow{is_secondary}, host_window(surface) {
LOG_DEBUG(Frontend, "Initializing EmuWindow_Android"); LOG_DEBUG(Frontend, "Initializing EmuWindow_Android");
if (!surface) { if (!surface) {
LOG_CRITICAL(Frontend, "surface is nullptr"); LOG_CRITICAL(Frontend, "surface is nullptr");
return; return;

View File

@ -1,10 +1,11 @@
// Copyright 2019 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include <vector> #include <vector>
#include <EGL/egl.h>
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
namespace Core { namespace Core {
@ -13,7 +14,7 @@ class System;
class EmuWindow_Android : public Frontend::EmuWindow { class EmuWindow_Android : public Frontend::EmuWindow {
public: public:
EmuWindow_Android(ANativeWindow* surface); EmuWindow_Android(ANativeWindow* surface, bool is_secondary = false);
~EmuWindow_Android(); ~EmuWindow_Android();
/// Called by the onSurfaceChanges() method to change the surface /// Called by the onSurfaceChanges() method to change the surface
@ -30,7 +31,12 @@ public:
void DoneCurrent() override; void DoneCurrent() override;
virtual void TryPresenting() {} virtual void TryPresenting() {}
// EGL Context must be shared
// could probably use the existing
// SharedContext for this instead, this is maybe temporary
virtual EGLContext* GetEGLContext() {
return nullptr;
}
virtual void StopPresenting() {} virtual void StopPresenting() {}
protected: protected:

View File

@ -1,4 +1,4 @@
// Copyright 2019 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -72,8 +72,9 @@ private:
EGLContext egl_context{}; EGLContext egl_context{};
}; };
EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativeWindow* surface) EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativeWindow* surface,
: EmuWindow_Android{surface}, system{system_} { bool is_secondary, EGLContext* sharedContext)
: EmuWindow_Android{surface, is_secondary}, system{system_} {
if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) { if (egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); egl_display == EGL_NO_DISPLAY) {
LOG_CRITICAL(Frontend, "eglGetDisplay() failed"); LOG_CRITICAL(Frontend, "eglGetDisplay() failed");
return; return;
@ -96,8 +97,10 @@ EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativ
if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) { if (eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &window_height) != EGL_TRUE) {
return; return;
} }
if (sharedContext) {
if (egl_context = eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data()); egl_context = *sharedContext;
} else if (egl_context =
eglCreateContext(egl_display, egl_config, 0, egl_context_attribs.data());
egl_context == EGL_NO_CONTEXT) { egl_context == EGL_NO_CONTEXT) {
LOG_CRITICAL(Frontend, "eglCreateContext() failed"); LOG_CRITICAL(Frontend, "eglCreateContext() failed");
return; return;
@ -127,6 +130,10 @@ EmuWindow_Android_OpenGL::EmuWindow_Android_OpenGL(Core::System& system_, ANativ
OnFramebufferSizeChanged(); OnFramebufferSizeChanged();
} }
EGLContext* EmuWindow_Android_OpenGL::GetEGLContext() {
return &egl_context;
}
bool EmuWindow_Android_OpenGL::CreateWindowSurface() { bool EmuWindow_Android_OpenGL::CreateWindowSurface() {
if (!host_window) { if (!host_window) {
return true; return true;
@ -204,14 +211,14 @@ void EmuWindow_Android_OpenGL::TryPresenting() {
return; return;
} }
if (presenting_state == PresentingState::Initial) [[unlikely]] { if (presenting_state == PresentingState::Initial) [[unlikely]] {
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
presenting_state = PresentingState::Running; presenting_state = PresentingState::Running;
} }
if (presenting_state != PresentingState::Running) [[unlikely]] { if (presenting_state != PresentingState::Running) [[unlikely]] {
return; return;
} }
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0); eglSwapInterval(egl_display, Settings::values.use_vsync_new ? 1 : 0);
system.GPU().Renderer().TryPresent(0); system.GPU().Renderer().TryPresent(0, is_secondary);
eglSwapBuffers(egl_display, egl_surface); eglSwapBuffers(egl_display, egl_surface);
} }

View File

@ -1,4 +1,4 @@
// Copyright 2019 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -19,13 +19,14 @@ struct ANativeWindow;
class EmuWindow_Android_OpenGL : public EmuWindow_Android { class EmuWindow_Android_OpenGL : public EmuWindow_Android {
public: public:
EmuWindow_Android_OpenGL(Core::System& system, ANativeWindow* surface); EmuWindow_Android_OpenGL(Core::System& system, ANativeWindow* surface, bool is_secondary,
EGLContext* sharedContext = NULL);
~EmuWindow_Android_OpenGL() override = default; ~EmuWindow_Android_OpenGL() override = default;
void TryPresenting() override; void TryPresenting() override;
void StopPresenting() override; void StopPresenting() override;
void PollEvents() override; void PollEvents() override;
EGLContext* GetEGLContext() override;
std::unique_ptr<GraphicsContext> CreateSharedContext() const override; std::unique_ptr<GraphicsContext> CreateSharedContext() const override;
private: private:

View File

@ -1,4 +1,4 @@
// Copyright 2019 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -24,8 +24,9 @@ private:
}; };
EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan( EmuWindow_Android_Vulkan::EmuWindow_Android_Vulkan(
ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_) ANativeWindow* surface, std::shared_ptr<Common::DynamicLibrary> driver_library_,
: EmuWindow_Android{surface}, driver_library{driver_library_} { bool is_secondary)
: EmuWindow_Android{surface, is_secondary}, driver_library{driver_library_} {
CreateWindowSurface(); CreateWindowSurface();
if (core_context = CreateSharedContext(); !core_context) { if (core_context = CreateSharedContext(); !core_context) {

View File

@ -1,4 +1,4 @@
// Copyright 2022 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -11,7 +11,8 @@ struct ANativeWindow;
class EmuWindow_Android_Vulkan : public EmuWindow_Android { class EmuWindow_Android_Vulkan : public EmuWindow_Android {
public: public:
EmuWindow_Android_Vulkan(ANativeWindow* surface, EmuWindow_Android_Vulkan(ANativeWindow* surface,
std::shared_ptr<Common::DynamicLibrary> driver_library); std::shared_ptr<Common::DynamicLibrary> driver_library,
bool is_secondary);
~EmuWindow_Android_Vulkan() override = default; ~EmuWindow_Android_Vulkan() override = default;
void PollEvents() override {} void PollEvents() override {}

View File

@ -31,8 +31,17 @@ GameInfoData* GetNewGameInfoData(const std::string& path) {
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(path); std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(path);
u64 program_id = 0; u64 program_id = 0;
bool is_encrypted = false; bool is_encrypted = false;
Loader::ResultStatus result{};
if (loader) {
result = loader->ReadProgramId(program_id);
if (result == Loader::ResultStatus::ErrorNotImplemented) {
// This can happen for 3DSX and ELF files.
program_id = 0;
result = Loader::ResultStatus::Success;
}
}
if (!loader || loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) { if (!loader || result != Loader::ResultStatus::Success) {
GameInfoData* gid = new GameInfoData(); GameInfoData* gid = new GameInfoData();
memset(&gid->smdh, 0, sizeof(Loader::SMDH)); memset(&gid->smdh, 0, sizeof(Loader::SMDH));
return gid; return gid;

View File

@ -16,11 +16,13 @@
#include <core/hle/service/cfg/cfg.h> #include <core/hle/service/cfg/cfg.h>
#include "audio_core/dsp_interface.h" #include "audio_core/dsp_interface.h"
#include "common/arch.h" #include "common/arch.h"
#if CITRA_ARCH(arm64) #if CITRA_ARCH(arm64)
#include "common/aarch64/cpu_detect.h" #include "common/aarch64/cpu_detect.h"
#elif CITRA_ARCH(x86_64) #elif CITRA_ARCH(x86_64)
#include "common/x64/cpu_detect.h" #include "common/x64/cpu_detect.h"
#endif #endif
#include "common/common_paths.h" #include "common/common_paths.h"
#include "common/dynamic_library/dynamic_library.h" #include "common/dynamic_library/dynamic_library.h"
#include "common/file_util.h" #include "common/file_util.h"
@ -47,12 +49,18 @@
#include "jni/camera/ndk_camera.h" #include "jni/camera/ndk_camera.h"
#include "jni/camera/still_image_camera.h" #include "jni/camera/still_image_camera.h"
#include "jni/config.h" #include "jni/config.h"
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
#include "jni/emu_window/emu_window_gl.h" #include "jni/emu_window/emu_window_gl.h"
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN
#include "jni/emu_window/emu_window_vk.h" #include "jni/emu_window/emu_window_vk.h"
#if CITRA_ARCH(arm64)
#include <adrenotools/driver.h>
#endif #endif
#endif
#include "jni/id_cache.h" #include "jni/id_cache.h"
#include "jni/input_manager.h" #include "jni/input_manager.h"
#include "jni/ndk_motion.h" #include "jni/ndk_motion.h"
@ -61,16 +69,14 @@
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#if defined(ENABLE_VULKAN) && CITRA_ARCH(arm64)
#include <adrenotools/driver.h>
#endif
namespace { namespace {
ANativeWindow* s_surf; ANativeWindow* s_surface;
ANativeWindow* s_secondary_surface;
std::shared_ptr<Common::DynamicLibrary> vulkan_library{}; std::shared_ptr<Common::DynamicLibrary> vulkan_library{};
std::unique_ptr<EmuWindow_Android> window; std::unique_ptr<EmuWindow_Android> window;
std::unique_ptr<EmuWindow_Android> secondary_window;
std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager; std::unique_ptr<PlayTime::PlayTimeManager> play_time_manager;
jlong ptm_current_title_id = std::numeric_limits<jlong>::max(); // Arbitrary default value jlong ptm_current_title_id = std::numeric_limits<jlong>::max(); // Arbitrary default value
@ -124,8 +130,17 @@ static void TryShutdown() {
} }
window->DoneCurrent(); window->DoneCurrent();
if (secondary_window) {
secondary_window->DoneCurrent();
}
Core::System::GetInstance().Shutdown(); Core::System::GetInstance().Shutdown();
window.reset(); window.reset();
if (secondary_window) {
secondary_window.reset();
}
InputManager::Shutdown(); InputManager::Shutdown();
MicroProfileShutdown(); MicroProfileShutdown();
} }
@ -151,15 +166,21 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
Core::System& system{Core::System::GetInstance()}; Core::System& system{Core::System::GetInstance()};
const auto graphics_api = Settings::values.graphics_api.GetValue(); const auto graphics_api = Settings::values.graphics_api.GetValue();
EGLContext* shared_context;
switch (graphics_api) { switch (graphics_api) {
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
case Settings::GraphicsAPI::OpenGL: case Settings::GraphicsAPI::OpenGL:
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf); window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surface, false);
shared_context = window->GetEGLContext();
secondary_window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_secondary_surface,
true, shared_context);
break; break;
#endif #endif
#ifdef ENABLE_VULKAN #ifdef ENABLE_VULKAN
case Settings::GraphicsAPI::Vulkan: case Settings::GraphicsAPI::Vulkan:
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library); window = std::make_unique<EmuWindow_Android_Vulkan>(s_surface, vulkan_library, false);
secondary_window =
std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true);
break; break;
#endif #endif
default: default:
@ -167,11 +188,17 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
"Unknown or unsupported graphics API {}, falling back to available default", "Unknown or unsupported graphics API {}, falling back to available default",
graphics_api); graphics_api);
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surf); window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_surface, false);
shared_context = window->GetEGLContext();
secondary_window = std::make_unique<EmuWindow_Android_OpenGL>(system, s_secondary_surface,
true, shared_context);
#elif ENABLE_VULKAN #elif ENABLE_VULKAN
window = std::make_unique<EmuWindow_Android_Vulkan>(s_surf, vulkan_library); window = std::make_unique<EmuWindow_Android_Vulkan>(s_surface, vulkan_library);
secondary_window =
std::make_unique<EmuWindow_Android_Vulkan>(s_secondary_surface, vulkan_library, true);
#else #else
// TODO: Add a null renderer backend for this, perhaps. // TODO: Add a null renderer backend for this, perhaps.
#error "At least one renderer must be enabled." #error "At least one renderer must be enabled."
#endif #endif
break; break;
@ -208,7 +235,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
InputManager::Init(); InputManager::Init();
window->MakeCurrent(); window->MakeCurrent();
const Core::System::ResultStatus load_result{system.Load(*window, filepath)}; const Core::System::ResultStatus load_result{
system.Load(*window, filepath, secondary_window.get())};
if (load_result != Core::System::ResultStatus::Success) { if (load_result != Core::System::ResultStatus::Success) {
return load_result; return load_result;
} }
@ -304,28 +332,68 @@ extern "C" {
void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
[[maybe_unused]] jobject obj, [[maybe_unused]] jobject obj,
jobject surf) { jobject surf) {
s_surf = ANativeWindow_fromSurface(env, surf); s_surface = ANativeWindow_fromSurface(env, surf);
bool notify = false; bool notify = false;
if (window) { if (window) {
notify = window->OnSurfaceChanged(s_surf); notify = window->OnSurfaceChanged(s_surface);
} }
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
if (notify && system.IsPoweredOn()) { if (notify && system.IsPoweredOn()) {
system.GPU().Renderer().NotifySurfaceChanged(); system.GPU().Renderer().NotifySurfaceChanged(false);
} }
LOG_INFO(Frontend, "Surface changed"); LOG_INFO(Frontend, "Surface changed");
} }
void Java_org_citra_citra_1emu_NativeLibrary_secondarySurfaceChanged(JNIEnv* env,
[[maybe_unused]] jobject obj,
jobject surf) {
auto& system = Core::System::GetInstance();
if (s_secondary_surface) {
ANativeWindow_release(s_secondary_surface);
s_secondary_surface = nullptr;
}
s_secondary_surface = ANativeWindow_fromSurface(env, surf);
if (!s_secondary_surface) {
return;
}
bool notify = false;
if (secondary_window) {
// Second window already created, so update it
notify = secondary_window->OnSurfaceChanged(s_secondary_surface);
} else {
LOG_WARNING(Frontend,
"Second Window does not exist in native.cpp but surface changed. Ignoring.");
}
if (notify && system.IsPoweredOn()) {
system.GPU().Renderer().NotifySurfaceChanged(true);
}
LOG_INFO(Frontend, "Secondary Surface changed");
}
void Java_org_citra_citra_1emu_NativeLibrary_secondarySurfaceDestroyed(
JNIEnv* env, [[maybe_unused]] jobject obj) {
if (s_secondary_surface != nullptr) {
ANativeWindow_release(s_secondary_surface);
s_secondary_surface = nullptr;
}
LOG_INFO(Frontend, "Secondary Surface Destroyed");
}
void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] JNIEnv* env, void Java_org_citra_citra_1emu_NativeLibrary_surfaceDestroyed([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jobject obj) { [[maybe_unused]] jobject obj) {
if (s_surf != nullptr) { if (s_surface != nullptr) {
ANativeWindow_release(s_surf); ANativeWindow_release(s_surface);
s_surf = nullptr; s_surface = nullptr;
if (window) { if (window) {
window->OnSurfaceChanged(s_surf); window->OnSurfaceChanged(s_surface);
} }
} }
} }
@ -338,6 +406,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_doFrame([[maybe_unused]] JNIEnv* en
if (window) { if (window) {
window->TryPresenting(); window->TryPresenting();
} }
if (secondary_window) {
secondary_window->TryPresenting();
}
} }
void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver( void JNICALL Java_org_citra_citra_1emu_NativeLibrary_initializeGpuDriver(
@ -514,6 +585,9 @@ void Java_org_citra_citra_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIE
stop_run = true; stop_run = true;
pause_emulation = false; pause_emulation = false;
window->StopPresenting(); window->StopPresenting();
if (secondary_window) {
secondary_window->StopPresenting();
}
running_cv.notify_all(); running_cv.notify_all();
} }
@ -583,6 +657,25 @@ void Java_org_citra_citra_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEn
window->OnTouchMoved((int)x, (int)y); window->OnTouchMoved((int)x, (int)y);
} }
jboolean Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jobject obj,
jfloat x, jfloat y,
jboolean pressed) {
if (!secondary_window) {
return JNI_FALSE;
}
return static_cast<jboolean>(secondary_window->OnTouchEvent(
static_cast<int>(x + 0.5), static_cast<int>(y + 0.5), pressed));
}
void Java_org_citra_citra_1emu_NativeLibrary_onSecondaryTouchMoved([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jobject obj,
jfloat x, jfloat y) {
if (secondary_window) {
secondary_window->OnTouchMoved((int)x, (int)y);
}
}
jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj, jlong Java_org_citra_citra_1emu_NativeLibrary_getTitleId(JNIEnv* env, [[maybe_unused]] jobject obj,
jstring j_filename) { jstring j_filename) {
std::string filepath = GetJString(env, j_filename); std::string filepath = GetJString(env, j_filename);

View File

@ -193,6 +193,10 @@
<string name="region_mismatch">Advertiment Regió No Vàlida</string> <string name="region_mismatch">Advertiment Regió No Vàlida</string>
<string name="region_mismatch_emulated">La configuració del país no és vàlida per a la regió emulada seleccionada.</string> <string name="region_mismatch_emulated">La configuració del país no és vàlida per a la regió emulada seleccionada.</string>
<string name="region_mismatch_console">La configuració del país no és vàlida per a la consola vinculada actual.</string> <string name="region_mismatch_console">La configuració del país no és vàlida per a la consola vinculada actual.</string>
<string name="storage">Emmagatzematge</string>
<string name="compress_cia_installs">Comprimir el contingut de CIAs instal·lats</string>
<string name="compress_cia_installs_description">Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.</string>
<!-- Camera settings strings --> <!-- Camera settings strings -->
<string name="inner_camera">Càmera interior</string> <string name="inner_camera">Càmera interior</string>
<string name="outer_left_camera">Càmera esquerra externa</string> <string name="outer_left_camera">Càmera esquerra externa</string>
@ -397,6 +401,10 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="emulation_configure_controls">Configurar Controls</string> <string name="emulation_configure_controls">Configurar Controls</string>
<string name="emulation_edit_layout">Editar Estil</string> <string name="emulation_edit_layout">Editar Estil</string>
<string name="emulation_done">Fet</string> <string name="emulation_done">Fet</string>
<string name="emulation_button_sliding">Lliscament de botons</string>
<string name="emulation_button_sliding_disabled">Mantindre el botó pressionat originalment</string>
<string name="emulation_button_sliding_enabled">Mantindre el botó pressionat actualment</string>
<string name="emulation_button_sliding_alternative">Mantindre el botó original i actualment pressionat</string>
<string name="emulation_toggle_controls">Activar Controls</string> <string name="emulation_toggle_controls">Activar Controls</string>
<string name="emulation_control_scale">Ajustar Escala</string> <string name="emulation_control_scale">Ajustar Escala</string>
<string name="emulation_control_scale_global">Escala Global</string> <string name="emulation_control_scale_global">Escala Global</string>
@ -409,6 +417,8 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="emulation_aspect_ratio">Relació d\'Aspecte</string> <string name="emulation_aspect_ratio">Relació d\'Aspecte</string>
<string name="emulation_switch_screen_layout">Estil de Pantalla Apaïsada</string> <string name="emulation_switch_screen_layout">Estil de Pantalla Apaïsada</string>
<string name="emulation_switch_portrait_layout">Estil de Pantalla de Perfil</string> <string name="emulation_switch_portrait_layout">Estil de Pantalla de Perfil</string>
<string name="emulation_switch_secondary_layout">Estil de Pantalla Secundària</string>
<string name="emulation_switch_secondary_layout_description">La disposició de la pantalla secundària connectada, amb cable o sense fil (Chromecast, Miracast)</string>
<string name="emulation_screen_layout_largescreen">Pantalla amplia</string> <string name="emulation_screen_layout_largescreen">Pantalla amplia</string>
<string name="emulation_screen_layout_portrait">Vertical</string> <string name="emulation_screen_layout_portrait">Vertical</string>
<string name="emulation_screen_layout_single">Pantalla Única</string> <string name="emulation_screen_layout_single">Pantalla Única</string>
@ -416,6 +426,7 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="emulation_screen_layout_hybrid">Pantalles híbrides</string> <string name="emulation_screen_layout_hybrid">Pantalles híbrides</string>
<string name="emulation_screen_layout_original">Original</string> <string name="emulation_screen_layout_original">Original</string>
<string name="emulation_portrait_layout_top_full">Per omissió</string> <string name="emulation_portrait_layout_top_full">Per omissió</string>
<string name="emulation_secondary_display_default">Per defecte del sistema (espill)</string>
<string name="emulation_screen_layout_custom">Estil Personalitzat</string> <string name="emulation_screen_layout_custom">Estil Personalitzat</string>
<string name="emulation_small_screen_position">Posició de Pantalla Xicoteta</string> <string name="emulation_small_screen_position">Posició de Pantalla Xicoteta</string>
<string name="small_screen_position_description">On hauria d\'aparéixer la pantalla xicoteta en relació amb la gran en Proporció de Pantalla Gran?</string> <string name="small_screen_position_description">On hauria d\'aparéixer la pantalla xicoteta en relació amb la gran en Proporció de Pantalla Gran?</string>
@ -536,6 +547,10 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="create_shortcut">Crear drecera</string> <string name="create_shortcut">Crear drecera</string>
<string name="shortcut_name_empty">El nom de la drecera no pot estar buit</string> <string name="shortcut_name_empty">El nom de la drecera no pot estar buit</string>
<string name="shortcut_image_stretch_toggle">Allargar per a ajustar la imatge</string> <string name="shortcut_image_stretch_toggle">Allargar per a ajustar la imatge</string>
<string name="game_context_id">ID:</string>
<string name="game_context_file">Fitxer:</string>
<string name="game_context_type">Tipus:</string>
<!-- Performance Overlay settings --> <!-- Performance Overlay settings -->
<string name="performance_overlay_show">Mostrar informació de rendiment</string> <string name="performance_overlay_show">Mostrar informació de rendiment</string>
<string name="performance_overlay_options">Informació de rendiment</string> <string name="performance_overlay_options">Informació de rendiment</string>

View File

@ -194,7 +194,9 @@
<string name="region_mismatch_emulated">La configuración del país no es válida para la región emulada seleccionada.</string> <string name="region_mismatch_emulated">La configuración del país no es válida para la región emulada seleccionada.</string>
<string name="region_mismatch_console">La configuración del país no es válida para la consola vinculada actual.</string> <string name="region_mismatch_console">La configuración del país no es válida para la consola vinculada actual.</string>
<string name="storage">Almacenamiento</string> <string name="storage">Almacenamiento</string>
<string name="compress_cia_installs">Comprimir el contenido CIA instalado</string> <string name="compress_cia_installs">Comprimir el contenido de CIAs instalados</string>
<string name="compress_cia_installs_description">Comprime el contenido de archivos CIA cuando son instalados a la SD emulada. Solo afecta contenido CIA instalado con esta opción activada.</string>
<!-- Camera settings strings --> <!-- Camera settings strings -->
<string name="inner_camera">Cámara interior</string> <string name="inner_camera">Cámara interior</string>
<string name="outer_left_camera">Cámara izquierda externa</string> <string name="outer_left_camera">Cámara izquierda externa</string>
@ -400,6 +402,9 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
<string name="emulation_edit_layout">Editar Estilo</string> <string name="emulation_edit_layout">Editar Estilo</string>
<string name="emulation_done">Hecho</string> <string name="emulation_done">Hecho</string>
<string name="emulation_button_sliding">Deslizamiento de botones</string> <string name="emulation_button_sliding">Deslizamiento de botones</string>
<string name="emulation_button_sliding_disabled">Mantener el botón presionado originalmente</string>
<string name="emulation_button_sliding_enabled">Mantener el botón presionado actualmente</string>
<string name="emulation_button_sliding_alternative">Mantener el botón original y actualmente presionado</string>
<string name="emulation_toggle_controls">Activar Controles</string> <string name="emulation_toggle_controls">Activar Controles</string>
<string name="emulation_control_scale">Ajustar Escala</string> <string name="emulation_control_scale">Ajustar Escala</string>
<string name="emulation_control_scale_global">Escala Global</string> <string name="emulation_control_scale_global">Escala Global</string>
@ -412,6 +417,8 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
<string name="emulation_aspect_ratio">Relación de Aspecto</string> <string name="emulation_aspect_ratio">Relación de Aspecto</string>
<string name="emulation_switch_screen_layout">Estilo de Pantalla Apaisada</string> <string name="emulation_switch_screen_layout">Estilo de Pantalla Apaisada</string>
<string name="emulation_switch_portrait_layout">Estilo de Pantalla de Perfil</string> <string name="emulation_switch_portrait_layout">Estilo de Pantalla de Perfil</string>
<string name="emulation_switch_secondary_layout">Estilo de Pantalla Secundaria</string>
<string name="emulation_switch_secondary_layout_description">El estilo de la pantalla secundaria conectada, con cable o inalámbrica (Chromecast, Miracast)</string>
<string name="emulation_screen_layout_largescreen">Pantalla amplia</string> <string name="emulation_screen_layout_largescreen">Pantalla amplia</string>
<string name="emulation_screen_layout_portrait">Retrato</string> <string name="emulation_screen_layout_portrait">Retrato</string>
<string name="emulation_screen_layout_single">Pantalla Única</string> <string name="emulation_screen_layout_single">Pantalla Única</string>
@ -419,6 +426,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
<string name="emulation_screen_layout_hybrid">Pantallas híbridas</string> <string name="emulation_screen_layout_hybrid">Pantallas híbridas</string>
<string name="emulation_screen_layout_original">Original</string> <string name="emulation_screen_layout_original">Original</string>
<string name="emulation_portrait_layout_top_full">Por defecto</string> <string name="emulation_portrait_layout_top_full">Por defecto</string>
<string name="emulation_secondary_display_default">Por defecto del sistema (espejo)</string>
<string name="emulation_screen_layout_custom">Estilo personalizado</string> <string name="emulation_screen_layout_custom">Estilo personalizado</string>
<string name="emulation_small_screen_position">Posición Pantalla Pequeña</string> <string name="emulation_small_screen_position">Posición Pantalla Pequeña</string>
<string name="small_screen_position_description">¿Dónde debería aparecer la pantalla pequeña en relación con la grande en Disposicion de Pantalla Grande?</string> <string name="small_screen_position_description">¿Dónde debería aparecer la pantalla pequeña en relación con la grande en Disposicion de Pantalla Grande?</string>

View File

@ -416,6 +416,8 @@
<string name="emulation_aspect_ratio">Proporcje obrazu</string> <string name="emulation_aspect_ratio">Proporcje obrazu</string>
<string name="emulation_switch_screen_layout">Geometryczny Układ Ekranu</string> <string name="emulation_switch_screen_layout">Geometryczny Układ Ekranu</string>
<string name="emulation_switch_portrait_layout">Pionowy Układ Ekranu</string> <string name="emulation_switch_portrait_layout">Pionowy Układ Ekranu</string>
<string name="emulation_switch_secondary_layout">Układ ekranu wyświetlacza dodatkowego</string>
<string name="emulation_switch_secondary_layout_description">Układ używany przez podłączony dodatkowy ekran, przewodowy lub bezprzewodowy (Chromecast, Miracast)</string>
<string name="emulation_screen_layout_largescreen">Duży Ekran</string> <string name="emulation_screen_layout_largescreen">Duży Ekran</string>
<string name="emulation_screen_layout_portrait">Ekran</string> <string name="emulation_screen_layout_portrait">Ekran</string>
<string name="emulation_screen_layout_single">Pojedynczy ekran</string> <string name="emulation_screen_layout_single">Pojedynczy ekran</string>
@ -423,6 +425,7 @@
<string name="emulation_screen_layout_hybrid">Hybrydowy Ekran</string> <string name="emulation_screen_layout_hybrid">Hybrydowy Ekran</string>
<string name="emulation_screen_layout_original">Oryginalny</string> <string name="emulation_screen_layout_original">Oryginalny</string>
<string name="emulation_portrait_layout_top_full">Domyślny</string> <string name="emulation_portrait_layout_top_full">Domyślny</string>
<string name="emulation_secondary_display_default">Ustawienia domyślne systemu (mirror)</string>
<string name="emulation_screen_layout_custom">Niestandardowy Układ</string> <string name="emulation_screen_layout_custom">Niestandardowy Układ</string>
<string name="emulation_small_screen_position">Pozycja małego ekranu</string> <string name="emulation_small_screen_position">Pozycja małego ekranu</string>
<string name="small_screen_position_description">Gdzie powinien być wyświetlany mały ekran względem dużego w układzie dużego ekranu?</string> <string name="small_screen_position_description">Gdzie powinien być wyświetlany mały ekran względem dużego w układzie dużego ekranu?</string>

View File

@ -416,6 +416,8 @@
<string name="emulation_aspect_ratio">宽高比</string> <string name="emulation_aspect_ratio">宽高比</string>
<string name="emulation_switch_screen_layout">屏幕布局</string> <string name="emulation_switch_screen_layout">屏幕布局</string>
<string name="emulation_switch_portrait_layout">纵向屏幕布局</string> <string name="emulation_switch_portrait_layout">纵向屏幕布局</string>
<string name="emulation_switch_secondary_layout">副屏幕布局</string>
<string name="emulation_switch_secondary_layout_description">有线或无线连接的辅助屏幕使用布局Chromecast, Miracast</string>
<string name="emulation_screen_layout_largescreen">大屏幕</string> <string name="emulation_screen_layout_largescreen">大屏幕</string>
<string name="emulation_screen_layout_portrait">纵向</string> <string name="emulation_screen_layout_portrait">纵向</string>
<string name="emulation_screen_layout_single">单个屏幕</string> <string name="emulation_screen_layout_single">单个屏幕</string>
@ -423,6 +425,7 @@
<string name="emulation_screen_layout_hybrid">混合式屏幕</string> <string name="emulation_screen_layout_hybrid">混合式屏幕</string>
<string name="emulation_screen_layout_original">原始</string> <string name="emulation_screen_layout_original">原始</string>
<string name="emulation_portrait_layout_top_full">默认</string> <string name="emulation_portrait_layout_top_full">默认</string>
<string name="emulation_secondary_display_default">系统默认(镜像)</string>
<string name="emulation_screen_layout_custom">自定义布局</string> <string name="emulation_screen_layout_custom">自定义布局</string>
<string name="emulation_small_screen_position">小屏幕位置</string> <string name="emulation_small_screen_position">小屏幕位置</string>
<string name="small_screen_position_description">在“大屏幕”布局中,小屏幕相对于大屏幕应该出现的位置?</string> <string name="small_screen_position_description">在“大屏幕”布局中,小屏幕相对于大屏幕应该出现的位置?</string>

View File

@ -408,7 +408,7 @@ Divertiti usando l\'emulatore!</string>
<string name="emulation_button_sliding">Pulsante scorrevole</string> <string name="emulation_button_sliding">Pulsante scorrevole</string>
<string name="emulation_button_sliding_disabled">Tieni premuto il pulsante originariamente premuto</string> <string name="emulation_button_sliding_disabled">Tieni premuto il pulsante originariamente premuto</string>
<string name="emulation_button_sliding_enabled">Tieni premuto il pulsante attualmente premuto</string> <string name="emulation_button_sliding_enabled">Tieni premuto il pulsante attualmente premuto</string>
<string name="emulation_button_sliding_alternative">Tieni premuto il pulsante originale e attualmente premuto</string> <string name="emulation_button_sliding_alternative">Tieni premuto il pulsante originale e quello attualmente premuto</string>
<string name="emulation_toggle_controls">Attiva Controlli</string> <string name="emulation_toggle_controls">Attiva Controlli</string>
<string name="emulation_control_scale">Regola la Dimensione</string> <string name="emulation_control_scale">Regola la Dimensione</string>
<string name="emulation_control_scale_global">Scala Globale</string> <string name="emulation_control_scale_global">Scala Globale</string>
@ -421,6 +421,8 @@ Divertiti usando l\'emulatore!</string>
<string name="emulation_aspect_ratio">Proporzione</string> <string name="emulation_aspect_ratio">Proporzione</string>
<string name="emulation_switch_screen_layout">Layout Schermi Orizzontale</string> <string name="emulation_switch_screen_layout">Layout Schermi Orizzontale</string>
<string name="emulation_switch_portrait_layout">Layout Schermi Verticale</string> <string name="emulation_switch_portrait_layout">Layout Schermi Verticale</string>
<string name="emulation_switch_secondary_layout">Layout dello schermo di visualizzazione secondario</string>
<string name="emulation_switch_secondary_layout_description">Il layout utilizzato da uno schermo secondario connesso, cablato o wireless (Chromecast, Miracast)</string>
<string name="emulation_screen_layout_largescreen">Schermo Grande</string> <string name="emulation_screen_layout_largescreen">Schermo Grande</string>
<string name="emulation_screen_layout_portrait">Verticale</string> <string name="emulation_screen_layout_portrait">Verticale</string>
<string name="emulation_screen_layout_single">Schermo singolo</string> <string name="emulation_screen_layout_single">Schermo singolo</string>
@ -428,13 +430,14 @@ Divertiti usando l\'emulatore!</string>
<string name="emulation_screen_layout_hybrid">Schermi Ibridi</string> <string name="emulation_screen_layout_hybrid">Schermi Ibridi</string>
<string name="emulation_screen_layout_original">Originale</string> <string name="emulation_screen_layout_original">Originale</string>
<string name="emulation_portrait_layout_top_full">Standard</string> <string name="emulation_portrait_layout_top_full">Standard</string>
<string name="emulation_secondary_display_default">Predefinito del sistema (specchio)</string>
<string name="emulation_screen_layout_custom">Layout personalizzato</string> <string name="emulation_screen_layout_custom">Layout personalizzato</string>
<string name="emulation_small_screen_position">Posizione schermo piccolo</string> <string name="emulation_small_screen_position">Posizione schermo piccolo</string>
<string name="small_screen_position_description">Dove dovrebbe apparire lo schermo inferiore rispetto a quello superiore nella modalità Schermo Grande?</string> <string name="small_screen_position_description">Dove dovrebbe apparire lo schermo inferiore rispetto a quello superiore nella modalità Schermo Grande?</string>
<string name="small_screen_position_top_right">Alto-Destra</string> <string name="small_screen_position_top_right">In alto a destra</string>
<string name="small_screen_position_middle_right">Centro-Destra</string> <string name="small_screen_position_middle_right">Centro-Destra</string>
<string name="small_screen_position_bottom_right">Basso-Destra (Standard)</string> <string name="small_screen_position_bottom_right">Basso-Destra (Standard)</string>
<string name="small_screen_position_top_left">Alto-Sinistra</string> <string name="small_screen_position_top_left">In alto a sinistra</string>
<string name="small_screen_position_middle_left">Centro-Sinistra</string> <string name="small_screen_position_middle_left">Centro-Sinistra</string>
<string name="small_screen_position_bottom_left">Basso-Sinistra</string> <string name="small_screen_position_bottom_left">Basso-Sinistra</string>
<string name="small_screen_position_above">Sopra</string> <string name="small_screen_position_above">Sopra</string>
@ -533,9 +536,13 @@ Divertiti usando l\'emulatore!</string>
<string name="play">Riproduci</string> <string name="play">Riproduci</string>
<string name="uninstall_cia">Disinstalla applicazione</string> <string name="uninstall_cia">Disinstalla applicazione</string>
<string name="uninstalling">Disinstallazione...</string> <string name="uninstalling">Disinstallazione...</string>
<string name="game_context_open_save_dir">Apri cartella dei salvataggi</string>
<string name="game_context_open_app">Apri cartella applicazione</string>
<string name="game_context_open_mods">Apri cartella delle mod</string>
<string name="game_context_open_textures">Apri cartella delle texture</string> <string name="game_context_open_textures">Apri cartella delle texture</string>
<string name="game_context_open_dlc">Apri catella DLC</string> <string name="game_context_open_dlc">Apri catella DLC</string>
<string name="game_context_open_updates">Apri cartella degli aggiornamenti</string> <string name="game_context_open_updates">Apri cartella degli aggiornamenti</string>
<string name="game_context_open_extra">Apri cartella extra</string>
<string name="game_context_uninstall_dlc">Disinstalla DLC</string> <string name="game_context_uninstall_dlc">Disinstalla DLC</string>
<string name="game_context_uninstall_updates">Disinstalla aggiornamenti</string> <string name="game_context_uninstall_updates">Disinstalla aggiornamenti</string>
<string name="shortcut">Scorciatoia</string> <string name="shortcut">Scorciatoia</string>
@ -567,6 +574,12 @@ Divertiti usando l\'emulatore!</string>
<string name="overlay_show_battery_temp_description">Mostra la temperatura attuale, in Celsius e Fahrenheit, della batteria.</string> <string name="overlay_show_battery_temp_description">Mostra la temperatura attuale, in Celsius e Fahrenheit, della batteria.</string>
<string name="overlay_position">Posizione overlay</string> <string name="overlay_position">Posizione overlay</string>
<string name="overlay_position_description">Scegli dove visualizzare l\'overlay delle prestazioni sullo schermo.</string> <string name="overlay_position_description">Scegli dove visualizzare l\'overlay delle prestazioni sullo schermo.</string>
<string name="overlay_position_top_left">In alto a sinistra</string>
<string name="overlay_position_top_right">In alto a destra</string>
<string name="overlay_position_bottom_left">In basso a sinistra</string>
<string name="overlay_position_bottom_right">In basso a destra</string>
<string name="overlay_position_center_top">In alto al centro</string>
<string name="overlay_position_center_bottom">In basso al centro</string>
<string name="overlay_background">Sfondo dell\'overlay</string> <string name="overlay_background">Sfondo dell\'overlay</string>
<string name="overlay_background_description">Aggiunge uno sfondo dietro all\'overlay per una lettura più facile.</string> <string name="overlay_background_description">Aggiunge uno sfondo dietro all\'overlay per una lettura più facile.</string>

View File

@ -35,12 +35,26 @@
<item>@string/emulation_screen_layout_custom</item> <item>@string/emulation_screen_layout_custom</item>
</string-array> </string-array>
<string-array name="secondaryLayouts">
<item>@string/emulation_secondary_display_default</item>
<item>@string/emulation_top_screen</item>
<item>@string/emulation_bottom_screen</item>
<item>@string/emulation_screen_layout_sidebyside</item>
</string-array>
<integer-array name="portraitLayoutValues"> <integer-array name="portraitLayoutValues">
<item>0</item> <item>0</item>
<item>2</item> <item>2</item>
<item>1</item> <item>1</item>
</integer-array> </integer-array>
<integer-array name="secondaryLayoutValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</integer-array>
<string-array name="smallScreenPositions"> <string-array name="smallScreenPositions">
<item>@string/small_screen_position_top_right</item> <item>@string/small_screen_position_top_right</item>
<item>@string/small_screen_position_middle_right</item> <item>@string/small_screen_position_middle_right</item>
@ -134,12 +148,12 @@
</integer-array> </integer-array>
<string-array name="statsPosition"> <string-array name="statsPosition">
<item>@string/overlay_position_top_left</item> <item>@string/performance_overlay_position_top_left</item>
<item>@string/overlay_position_center_top</item> <item>@string/performance_overlay_position_center_top</item>
<item>@string/overlay_position_top_right</item> <item>@string/performance_overlay_position_top_right</item>
<item>@string/overlay_position_bottom_left</item> <item>@string/performance_overlay_position_bottom_left</item>
<item>@string/overlay_position_center_bottom</item> <item>@string/performance_overlay_position_center_bottom</item>
<item>@string/overlay_position_bottom_right</item> <item>@string/performance_overlay_position_bottom_right</item>
</string-array> </string-array>
<integer-array name="statsPositionValues"> <integer-array name="statsPositionValues">
<item>0</item> <item>0</item>

View File

@ -238,25 +238,27 @@
<string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string> <string name="spirv_shader_gen_description">Emits the fragment shader used to emulate PICA using SPIR-V instead of GLSL</string>
<string name="disable_spirv_optimizer">Disable SPIR-V Optimizer</string> <string name="disable_spirv_optimizer">Disable SPIR-V Optimizer</string>
<string name="disable_spirv_optimizer_description">Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</string> <string name="disable_spirv_optimizer_description">Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.</string>
<string name="async_shaders">Enable asynchronous shader compilation</string> <string name="async_shaders">Enable Asynchronous Shader Compilation</string>
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string> <string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
<string name="linear_filtering">Linear Filtering</string> <string name="linear_filtering">Linear Filtering</string>
<string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string> <string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string>
<string name="texture_filter_name">Texture Filter</string> <string name="texture_filter_name">Texture Filter</string>
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string> <string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
<string name="delay_render_thread">Delay game render thread</string> <string name="delay_render_thread">Delay Game Render Thread</string>
<string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string> <string name="delay_render_thread_description">Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates.</string>
<string name="advanced">Advanced</string> <string name="advanced">Advanced</string>
<string name="texture_sampling_name">Texture Sampling</string> <string name="texture_sampling_name">Texture Sampling</string>
<string name="texture_sampling_description">Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure, set this to Game Controlled.</string> <string name="texture_sampling_description">Overrides the sampling filter used by games. This can be useful in certain cases with poorly behaved games when upscaling. If unsure, set this to Game Controlled.</string>
<string name="shaders_accurate_mul">Accurate Multiplication</string> <string name="shaders_accurate_mul">Accurate Multiplication</string>
<string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string> <string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string>
<string name="asynchronous_gpu">Enable asynchronous GPU emulation</string> <string name="asynchronous_gpu">Enable Asynchronous GPU Emulation</string>
<string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string> <string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
<string name="frame_limit_enable">Limit Speed</string> <string name="frame_limit_enable">Limit Speed</string>
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.</string> <string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed. If disabled, emulation speed will be uncapped and the turbo speed hotkey will not work.</string>
<string name="frame_limit_slider">Limit Speed Percent</string> <string name="frame_limit_slider">Limit Speed Percent</string>
<string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string> <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
<string name="android_hide_images">Hide 3DS Images from Android</string>
<string name="android_hide_images_description">Prevent 3DS camera, screenshot, and custom texture images from being indexed by Android and displayed in the gallery. Your device may need to be rebooted after changing this setting to take effect.</string>
<string name="turbo_limit">Turbo Speed Limit</string> <string name="turbo_limit">Turbo Speed Limit</string>
<string name="turbo_limit_description">Emulation speed limit used while the turbo hotkey is active.</string> <string name="turbo_limit_description">Emulation speed limit used while the turbo hotkey is active.</string>
<string name="expand_to_cutout_area">Expand to Cutout Area</string> <string name="expand_to_cutout_area">Expand to Cutout Area</string>
@ -306,7 +308,7 @@
<string name="audio_volume">Volume</string> <string name="audio_volume">Volume</string>
<string name="audio_stretch">Audio Stretching</string> <string name="audio_stretch">Audio Stretching</string>
<string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string> <string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string>
<string name="realtime_audio">Enable realtime audio</string> <string name="realtime_audio">Enable Realtime Audio</string>
<string name="realtime_audio_description">Scales audio playback speed to account for drops in emulation framerate. This means that audio will play at full speed even while the game framerate is low. May cause audio desync issues.</string> <string name="realtime_audio_description">Scales audio playback speed to account for drops in emulation framerate. This means that audio will play at full speed even while the game framerate is low. May cause audio desync issues.</string>
<string name="audio_input_type">Audio Input Device</string> <string name="audio_input_type">Audio Input Device</string>
<string name="sound_output_mode">Sound Output Mode</string> <string name="sound_output_mode">Sound Output Mode</string>
@ -321,9 +323,9 @@
<string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string> <string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string>
<string name="renderer_debug">Debug Renderer</string> <string name="renderer_debug">Debug Renderer</string>
<string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string> <string name="renderer_debug_description">Log additional graphics related debug information. When enabled, game performance will be significantly reduced.</string>
<string name="instant_debug_log">Flush log output on every message</string> <string name="instant_debug_log">Flush Log Output on Every Message</string>
<string name="instant_debug_log_description">Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.</string> <string name="instant_debug_log_description">Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.</string>
<string name="delay_start_lle_modules">Delay start with LLE modules</string> <string name="delay_start_lle_modules">Delay Start With LLE Modules</string>
<string name="delay_start_lle_modules_description">Delays the start of the app when LLE modules are enabled.</string> <string name="delay_start_lle_modules_description">Delays the start of the app when LLE modules are enabled.</string>
<string name="deterministic_async_operations">Deterministic Async Operations</string> <string name="deterministic_async_operations">Deterministic Async Operations</string>
<string name="deterministic_async_operations_description">Makes async operations deterministic for debugging. Enabling this may cause freezes.</string> <string name="deterministic_async_operations_description">Makes async operations deterministic for debugging. Enabling this may cause freezes.</string>
@ -437,6 +439,8 @@
<string name="emulation_aspect_ratio">Aspect Ratio</string> <string name="emulation_aspect_ratio">Aspect Ratio</string>
<string name="emulation_switch_screen_layout">Landscape Screen Layout</string> <string name="emulation_switch_screen_layout">Landscape Screen Layout</string>
<string name="emulation_switch_portrait_layout">Portrait Screen Layout</string> <string name="emulation_switch_portrait_layout">Portrait Screen Layout</string>
<string name="emulation_switch_secondary_layout">Secondary Display Screen Layout</string>
<string name="emulation_switch_secondary_layout_description">The layout used by a connected secondary screen, wired or wireless (Chromecast, Miracast)</string>
<string name="emulation_screen_layout_largescreen">Large Screen</string> <string name="emulation_screen_layout_largescreen">Large Screen</string>
<string name="emulation_screen_layout_portrait">Portrait</string> <string name="emulation_screen_layout_portrait">Portrait</string>
<string name="emulation_screen_layout_single">Single Screen</string> <string name="emulation_screen_layout_single">Single Screen</string>
@ -444,7 +448,15 @@
<string name="emulation_screen_layout_hybrid">Hybrid Screens</string> <string name="emulation_screen_layout_hybrid">Hybrid Screens</string>
<string name="emulation_screen_layout_original">Original</string> <string name="emulation_screen_layout_original">Original</string>
<string name="emulation_portrait_layout_top_full">Default</string> <string name="emulation_portrait_layout_top_full">Default</string>
<string name="emulation_secondary_display_default">System Default (mirror)</string>
<string name="emulation_screen_layout_custom">Custom Layout</string> <string name="emulation_screen_layout_custom">Custom Layout</string>
<string name="bg_color">Background Color</string>
<string name="bg_color_description">The color which appears behind the screens during emulation, represented as an RGB value.</string>
<string name="bg_red">Red</string>
<string name="bg_green">Green</string>
<string name="bg_blue">Blue</string>
<string name="second_screen_opacity">Custom Layout Second Screen Opacity</string>
<string name="second_screen_opacity_description">The opacity of the second 3DS screen when using a custom screen layout. Useful if the second screen is to be positioned on top of the first screen.</string>
<string name="emulation_small_screen_position">Small Screen Position</string> <string name="emulation_small_screen_position">Small Screen Position</string>
<string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string> <string name="small_screen_position_description">Where should the small screen appear relative to the large one in Large Screen Layout?</string>
<string name="small_screen_position_top_right">Top Right</string> <string name="small_screen_position_top_right">Top Right</string>
@ -573,28 +585,28 @@
<string name="performance_overlay_options">Performance Overlay</string> <string name="performance_overlay_options">Performance Overlay</string>
<string name="performance_overlay_enable">Enable Performance Overlay</string> <string name="performance_overlay_enable">Enable Performance Overlay</string>
<string name="performance_overlay_options_description">Configure whether the performance overlay is shown and what information is displayed.</string> <string name="performance_overlay_options_description">Configure whether the performance overlay is shown and what information is displayed.</string>
<string name="overlay_show_fps">Show FPS</string> <string name="performance_overlay_show_fps">Show FPS</string>
<string name="overlay_show_fps_description">Display current frames per second.</string> <string name="performance_overlay_show_fps_description">Display current frames per second.</string>
<string name="overlay_show_frametime">Show Frametime</string> <string name="performance_overlay_show_frametime">Show Frametime</string>
<string name="overlay_show_frametime_description">Display current frametime.</string> <string name="performance_overlay_show_frametime_description">Display current frametime.</string>
<string name="overlay_show_speed">Show Speed</string> <string name="performance_overlay_show_speed">Show Speed</string>
<string name="overlay_show_speed_description">Display current emulation speed percentage.</string> <string name="performance_overlay_show_speed_description">Display current emulation speed percentage.</string>
<string name="overlay_show_app_ram_usage">Show App Memory Usage</string> <string name="performance_overlay_show_app_ram_usage">Show App Memory Usage</string>
<string name="overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string> <string name="performance_overlay_show_app_ram_usage_description">Display the amount of RAM getting used by the emulator.</string>
<string name="overlay_show_available_ram">Show Available Memory</string> <string name="performance_overlay_show_available_ram">Show Available Memory</string>
<string name="overlay_show_available_ram_description">Display the amount of RAM which is available.</string> <string name="performance_overlay_show_available_ram_description">Display the amount of RAM which is available.</string>
<string name="overlay_show_battery_temp">Show Battery Temperature</string> <string name="performance_overlay_show_battery_temp">Show Battery Temperature</string>
<string name="overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string> <string name="performance_overlay_show_battery_temp_description">Display current Battery temperature in Celsius and Fahrenheit.</string>
<string name="overlay_position">Overlay Position</string> <string name="performance_overlay_position">Overlay Position</string>
<string name="overlay_position_description">Choose where the performance overlay is displayed on the screen.</string> <string name="performance_overlay_position_description">Choose where the performance overlay is displayed on the screen.</string>
<string name="overlay_position_top_left">Top Left</string> <string name="performance_overlay_position_top_left">Top Left</string>
<string name="overlay_position_top_right">Top Right</string> <string name="performance_overlay_position_top_right">Top Right</string>
<string name="overlay_position_bottom_left">Bottom Left</string> <string name="performance_overlay_position_bottom_left">Bottom Left</string>
<string name="overlay_position_bottom_right">Bottom Right</string> <string name="performance_overlay_position_bottom_right">Bottom Right</string>
<string name="overlay_position_center_top">Center Top</string> <string name="performance_overlay_position_center_top">Center Top</string>
<string name="overlay_position_center_bottom">Center Bottom</string> <string name="performance_overlay_position_center_bottom">Center Bottom</string>
<string name="overlay_background">Overlay Background</string> <string name="performance_overlay_background">Overlay Background</string>
<string name="overlay_background_description">Adds a background behind the overlay for easier reading.</string> <string name="performance_overlay_background_description">Adds a background behind the overlay for easier reading.</string>
<!-- Cheats --> <!-- Cheats -->
<string name="cheats">Cheats</string> <string name="cheats">Cheats</string>

View File

@ -1,4 +1,4 @@
// Copyright 2016 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -9,7 +9,7 @@
namespace AudioCore { namespace AudioCore {
constexpr char auto_device_name[] = "auto"; constexpr char auto_device_name[] = "Auto";
/** /**
* This class is an interface for an audio sink. An audio sink accepts samples in stereo signed * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed

View File

@ -60,6 +60,10 @@ if (ENABLE_QT AND UNIX AND NOT APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode) target_link_libraries(citra_meta PRIVATE Qt6::DBus gamemode)
endif() endif()
if (ENABLE_QT AND APPLE)
target_link_libraries(citra_meta PRIVATE Qt6::GuiPrivate)
endif()
if (ENABLE_QT AND USE_DISCORD_PRESENCE) if (ENABLE_QT AND USE_DISCORD_PRESENCE)
target_link_libraries(citra_meta PRIVATE discord-rpc) target_link_libraries(citra_meta PRIVATE discord-rpc)
endif() endif()

View File

@ -172,12 +172,12 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
multiplayer/state.h multiplayer/state.h
multiplayer/validation.h multiplayer/validation.h
precompiled_headers.h precompiled_headers.h
qt_image_interface.cpp
qt_image_interface.h
uisettings.cpp uisettings.cpp
uisettings.h uisettings.h
user_data_migration.cpp user_data_migration.cpp
user_data_migration.h user_data_migration.h
qt_image_interface.cpp
qt_image_interface.h
util/clickable_label.cpp util/clickable_label.cpp
util/clickable_label.h util/clickable_label.h
util/graphics_device_info.cpp util/graphics_device_info.cpp
@ -190,6 +190,13 @@ add_library(citra_qt STATIC EXCLUDE_FROM_ALL
util/util.h util/util.h
) )
if (APPLE)
target_sources(citra_qt PUBLIC
qt_swizzle.h
qt_swizzle.mm
)
endif()
file(GLOB COMPAT_LIST file(GLOB COMPAT_LIST
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json) ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
@ -272,7 +279,12 @@ endif()
if (NOT WIN32) if (NOT WIN32)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
if(Qt6_VERSION VERSION_GREATER_EQUAL "6.10.0")
find_package(Qt6 REQUIRED COMPONENTS GuiPrivate)
target_link_libraries(citra_qt PRIVATE Qt6::GuiPrivate)
else()
target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(citra_qt PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
endif()
endif() endif()
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)

View File

@ -69,6 +69,7 @@
#include "citra_qt/movie/movie_record_dialog.h" #include "citra_qt/movie/movie_record_dialog.h"
#include "citra_qt/multiplayer/state.h" #include "citra_qt/multiplayer/state.h"
#include "citra_qt/qt_image_interface.h" #include "citra_qt/qt_image_interface.h"
#include "citra_qt/qt_swizzle.h"
#include "citra_qt/uisettings.h" #include "citra_qt/uisettings.h"
#include "common/play_time_manager.h" #include "common/play_time_manager.h"
#ifdef ENABLE_QT_UPDATE_CHECKER #ifdef ENABLE_QT_UPDATE_CHECKER
@ -170,6 +171,12 @@ void GMainWindow::ShowCommandOutput(std::string title, std::string message) {
#endif #endif
} }
bool IsPrerelease() {
return ((strstr(Common::g_build_fullname, "alpha") != NULL) ||
(strstr(Common::g_build_fullname, "beta") != NULL) ||
(strstr(Common::g_build_fullname, "rc") != NULL));
}
GMainWindow::GMainWindow(Core::System& system_) GMainWindow::GMainWindow(Core::System& system_)
: ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{system.Movie()}, : ui{std::make_unique<Ui::MainWindow>()}, system{system_}, movie{system.Movie()},
user_data_migrator{this}, config{std::make_unique<QtConfig>()}, emu_thread{nullptr} { user_data_migrator{this}, config{std::make_unique<QtConfig>()}, emu_thread{nullptr} {
@ -398,12 +405,8 @@ GMainWindow::GMainWindow(Core::System& system_)
#ifdef ENABLE_QT_UPDATE_CHECKER #ifdef ENABLE_QT_UPDATE_CHECKER
if (UISettings::values.check_for_update_on_start) { if (UISettings::values.check_for_update_on_start) {
update_future = QtConcurrent::run([]() -> QString { update_future = QtConcurrent::run([]() -> QString {
const bool is_prerelease = // TODO: This can be done better -OS
((strstr(Common::g_build_fullname, "alpha") != NULL) ||
(strstr(Common::g_build_fullname, "beta") != NULL) ||
(strstr(Common::g_build_fullname, "rc") != NULL));
const std::optional<std::string> latest_release_tag = const std::optional<std::string> latest_release_tag =
UpdateChecker::GetLatestRelease(is_prerelease); UpdateChecker::GetLatestRelease(IsPrerelease());
if (latest_release_tag && latest_release_tag.value() != Common::g_build_fullname) { if (latest_release_tag && latest_release_tag.value() != Common::g_build_fullname) {
return QString::fromStdString(latest_release_tag.value()); return QString::fromStdString(latest_release_tag.value());
} }
@ -977,6 +980,10 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList); connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList);
connect(game_list, &GameList::PopulatingCompleted, this, connect(game_list, &GameList::PopulatingCompleted, this,
[this] { multiplayer_state->UpdateGameList(game_list->GetModel()); }); [this] { multiplayer_state->UpdateGameList(game_list->GetModel()); });
#ifdef ENABLE_DEVELOPER_OPTIONS
connect(game_list, &GameList::StartingLaunchStressTest, this,
&GMainWindow::StartLaunchStressTest);
#endif
connect(game_list, &GameList::OpenPerGameGeneralRequested, this, connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
&GMainWindow::OnGameListOpenPerGameProperties); &GMainWindow::OnGameListOpenPerGameProperties);
@ -1573,6 +1580,16 @@ void GMainWindow::ShutdownGame() {
secondary_window->ReleaseRenderTarget(); secondary_window->ReleaseRenderTarget();
} }
void GMainWindow::StartLaunchStressTest(const QString& game_path) {
QThreadPool::globalInstance()->start([this, game_path] {
do {
ui->action_Stop->trigger();
emit game_list->GameChosen(game_path);
QThread::sleep(2);
} while (emulation_running);
});
}
void GMainWindow::StoreRecentFile(const QString& filename) { void GMainWindow::StoreRecentFile(const QString& filename) {
UISettings::values.recent_files.prepend(filename); UISettings::values.recent_files.prepend(filename);
UISettings::values.recent_files.removeDuplicates(); UISettings::values.recent_files.removeDuplicates();
@ -3926,8 +3943,13 @@ void GMainWindow::OnEmulatorUpdateAvailable() {
.arg(version_string)); .arg(version_string));
update_prompt.exec(); update_prompt.exec();
if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) { if (update_prompt.button(QMessageBox::Yes) == update_prompt.clickedButton()) {
QDesktopServices::openUrl( std::string update_page_url;
QUrl(QString::fromStdString("https://azahar-emu.org/pages/download/"))); if (IsPrerelease()) {
update_page_url = "https://github.com/azahar-emu/azahar/releases";
} else {
update_page_url = "https://azahar-emu.org/pages/download/";
}
QDesktopServices::openUrl(QUrl(QString::fromStdString(update_page_url)));
} }
} }
#endif #endif
@ -4105,6 +4127,11 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
} }
void LaunchQtFrontend(int argc, char* argv[]) { void LaunchQtFrontend(int argc, char* argv[]) {
#ifdef __APPLE__
// Ensure that the linker doesn't optimize qt_swizzle.mm out of existence.
QtSwizzle::Dummy();
#endif
Common::DetachedTasks detached_tasks; Common::DetachedTasks detached_tasks;
#if MICROPROFILE_ENABLED #if MICROPROFILE_ENABLED

View File

@ -306,6 +306,7 @@ private slots:
#endif #endif
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value, void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total); std::size_t total);
void StartLaunchStressTest(const QString& game_path);
private: private:
Q_INVOKABLE void OnMoviePlaybackCompleted(); Q_INVOKABLE void OnMoviePlaybackCompleted();

View File

@ -371,7 +371,7 @@ void QtConfig::ReadControlValues() {
const auto append_profile = [this, num_touch_from_button_maps] { const auto append_profile = [this, num_touch_from_button_maps] {
Settings::InputProfile profile; Settings::InputProfile profile;
profile.name = profile.name =
ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString(); ReadSetting(QStringLiteral("name"), QStringLiteral("Default")).toString().toStdString();
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]), profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),

View File

@ -35,7 +35,7 @@
<item> <item>
<widget class="QLabel" name="label_emulation"> <widget class="QLabel" name="label_emulation">
<property name="text"> <property name="text">
<string>Emulation:</string> <string>Emulation</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -28,7 +28,7 @@
<string>Select the camera to configure</string> <string>Select the camera to configure</string>
</property> </property>
<property name="text"> <property name="text">
<string>Camera to configure:</string> <string>Camera to Configure</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -59,7 +59,7 @@
<string>Select the camera mode (single or double)</string> <string>Select the camera mode (single or double)</string>
</property> </property>
<property name="text"> <property name="text">
<string>Camera mode:</string> <string>Camera mode</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -90,7 +90,7 @@
<string>Select the position of camera to configure</string> <string>Select the position of camera to configure</string>
</property> </property>
<property name="text"> <property name="text">
<string>Camera position:</string> <string>Camera position</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -130,7 +130,7 @@
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string> <string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Camera Image Source:</string> <string>Camera Image Source</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -166,7 +166,7 @@
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="text"> <property name="text">
<string>File:</string> <string>File</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -193,7 +193,7 @@
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="text"> <property name="text">
<string>Camera:</string> <string>Camera</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -234,7 +234,7 @@
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="text"> <property name="text">
<string>Flip:</string> <string>Flip</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -113,7 +113,7 @@
<item> <item>
<widget class="QCheckBox" name="toggle_linear_filter"> <widget class="QCheckBox" name="toggle_linear_filter">
<property name="text"> <property name="text">
<string>Enable Linear Filtering</string> <string>Enable linear filtering</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -320,7 +320,7 @@
<item> <item>
<widget class="QCheckBox" name="disable_right_eye_render"> <widget class="QCheckBox" name="disable_right_eye_render">
<property name="text"> <property name="text">
<string>Disable Right Eye Rendering</string> <string>Disable right eye rendering</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable Right Eye Rendering&lt;/p&gt;&lt;p&gt;Disables rendering the right eye image when not using stereoscopic mode. Greatly improves performance in some applications, but can cause flickering in others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -342,7 +342,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Replace textures with PNG files.&lt;/p&gt;&lt;p&gt;Textures are loaded from load/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Replace textures with PNG files.&lt;/p&gt;&lt;p&gt;Textures are loaded from load/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Use Custom Textures</string> <string>Use custom textures</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -352,7 +352,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Dump textures to PNG files.&lt;/p&gt;&lt;p&gt;Textures are dumped to dump/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Dump textures to PNG files.&lt;/p&gt;&lt;p&gt;Textures are dumped to dump/textures/[Title ID]/.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Dump Textures</string> <string>Dump textures</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -362,7 +362,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load all custom textures into memory on boot, instead of loading them when the application requires them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load all custom textures into memory on boot, instead of loading them when the application requires them.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Preload Custom Textures</string> <string>Preload custom textures</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -372,7 +372,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load custom textures asynchronously with background threads to reduce loading stutter&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load custom textures asynchronously with background threads to reduce loading stutter&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Async Custom Texture Loading</string> <string>Async custom texture loading</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -97,7 +97,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Set emulation speed:</string> <string>Set emulation speed</string>
</property> </property>
</item> </item>
</widget> </widget>
@ -105,7 +105,7 @@
<item> <item>
<widget class="QLabel" name="label_emulation_speed"> <widget class="QLabel" name="label_emulation_speed">
<property name="text"> <property name="text">
<string>Emulation Speed:</string> <string>Emulation Speed</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -132,14 +132,14 @@
<item> <item>
<widget class="QCheckBox" name="spirv_shader_gen"> <widget class="QCheckBox" name="spirv_shader_gen">
<property name="text"> <property name="text">
<string>SPIR-V Shader Generation</string> <string>SPIR-V shader generation</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="disable_spirv_optimizer"> <widget class="QCheckBox" name="disable_spirv_optimizer">
<property name="text"> <property name="text">
<string>Disable GLSL -> SPIR-V Optimizer</string> <string>Disable GLSL -> SPIR-V optimizer</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disables the SPIR-V optimization pass, reducing stuttering considerably while barely affecting performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -176,7 +176,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the selected graphics API to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the selected graphics API to accelerate shader emulation.&lt;/p&gt;&lt;p&gt;Requires a relatively powerful GPU for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Hardware Shader</string> <string>Enable hardware shader</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -201,7 +201,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Correctly handle all edge cases in multiplication operation in shaders. &lt;/p&gt;&lt;p&gt;Some applications requires this to be enabled for the hardware shader to render properly.&lt;/p&gt;&lt;p&gt;However this would reduce performance in most applications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Correctly handle all edge cases in multiplication operation in shaders. &lt;/p&gt;&lt;p&gt;Some applications requires this to be enabled for the hardware shader to render properly.&lt;/p&gt;&lt;p&gt;However this would reduce performance in most applications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Accurate Multiplication</string> <string>Accurate multiplication</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -217,7 +217,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the JIT engine instead of the interpreter for software shader emulation. &lt;/p&gt;&lt;p&gt;Enable this for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the JIT engine instead of the interpreter for software shader emulation. &lt;/p&gt;&lt;p&gt;Enable this for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Shader JIT</string> <string>Enable shader JIT</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -227,7 +227,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Compile shaders using background threads to avoid shader compilation stutter. Expect temporary graphical glitches&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Async Shader Compilation</string> <string>Enable async shader compilation</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -237,7 +237,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Perform presentation on separate threads. Improves performance when using Vulkan in most applications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Perform presentation on separate threads. Improves performance when using Vulkan in most applications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable Async Presentation</string> <string>Enable async presentation</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -303,7 +303,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reduce stuttering by storing and loading generated shaders to disk.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reduce stuttering by storing and loading generated shaders to disk.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Use Disk Shader Cache</string> <string>Use disk shader cache</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -349,7 +349,7 @@
<item> <item>
<widget class="QLabel" name="label_delay_render"> <widget class="QLabel" name="label_delay_render">
<property name="text"> <property name="text">
<string>Delay application render thread:</string> <string>Delay Application Render Thread</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.&lt;/p&gt;&lt;p&gt;Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Delays the emulated application render thread the specified amount of milliseconds every time it submits render commands to the GPU.&lt;/p&gt;&lt;p&gt;Adjust this feature in the (very few) dynamic framerate applications to fix performance issues.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>

View File

@ -132,14 +132,14 @@
<item> <item>
<widget class="QCheckBox" name="toggle_swap_screen"> <widget class="QCheckBox" name="toggle_swap_screen">
<property name="text"> <property name="text">
<string>Swap Screens</string> <string>Swap screens</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="toggle_upright_screen"> <widget class="QCheckBox" name="toggle_upright_screen">
<property name="text"> <property name="text">
<string>Rotate Screens Upright</string> <string>Rotate screens upright</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -531,7 +531,7 @@
<item> <item>
<widget class="QLabel" name="lb_opacity_second_layer"> <widget class="QLabel" name="lb_opacity_second_layer">
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bottom Screen Opacity % (OpenGL Only)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Bottom Screen Opacity %&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -27,7 +27,7 @@
<item> <item>
<widget class="QCheckBox" name="toggle_virtual_sd"> <widget class="QCheckBox" name="toggle_virtual_sd">
<property name="text"> <property name="text">
<string>Use Virtual SD</string> <string>Use virtual SD card</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -42,7 +42,7 @@
<item> <item>
<widget class="QCheckBox" name="toggle_custom_storage"> <widget class="QCheckBox" name="toggle_custom_storage">
<property name="text"> <property name="text">
<string>Use Custom Storage</string> <string>Use custom storage location</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -92,7 +92,7 @@ online features (if installed)</string>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="region_label"> <widget class="QLabel" name="region_label">
<property name="text"> <property name="text">
<string>Region:</string> <string>Region</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -549,7 +549,7 @@ online features (if installed)</string>
<item row="20" column="0"> <item row="20" column="0">
<widget class="QLabel" name="label_plugin_loader"> <widget class="QLabel" name="label_plugin_loader">
<property name="text"> <property name="text">
<string>3GX Plugin Loader:</string> <string>3GX Plugin Loader</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -37,7 +37,7 @@
<item> <item>
<widget class="QLabel" name="language_label"> <widget class="QLabel" name="language_label">
<property name="text"> <property name="text">
<string>Interface language:</string> <string>Interface Language</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -51,7 +51,7 @@
<item> <item>
<widget class="QLabel" name="theme_label"> <widget class="QLabel" name="theme_label">
<property name="text"> <property name="text">
<string>Theme:</string> <string>Theme</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -78,7 +78,7 @@
<item> <item>
<widget class="QLabel" name="icon_size_label"> <widget class="QLabel" name="icon_size_label">
<property name="text"> <property name="text">
<string>Icon Size:</string> <string>Icon Size</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -108,7 +108,7 @@
<item> <item>
<widget class="QLabel" name="row_1_text_label"> <widget class="QLabel" name="row_1_text_label">
<property name="text"> <property name="text">
<string>Row 1 Text:</string> <string>Row 1 Text</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -148,7 +148,7 @@
<item> <item>
<widget class="QLabel" name="row_2_text_label"> <widget class="QLabel" name="row_2_text_label">
<property name="text"> <property name="text">
<string>Row 2 Text:</string> <string>Row 2 Text</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -191,14 +191,14 @@
<item> <item>
<widget class="QCheckBox" name="toggle_hide_no_icon"> <widget class="QCheckBox" name="toggle_hide_no_icon">
<property name="text"> <property name="text">
<string>Hide Titles without Icon</string> <string>Hide titles without icon</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="toggle_single_line_mode"> <widget class="QCheckBox" name="toggle_single_line_mode">
<property name="text"> <property name="text">
<string>Single Line Mode</string> <string>Single line mode</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -218,7 +218,7 @@
<item> <item>
<widget class="QCheckBox" name="show_advanced_frametime_info"> <widget class="QCheckBox" name="show_advanced_frametime_info">
<property name="text"> <property name="text">
<string>Show Advanced Frame Time Info</string> <string>Show advanced frame time info</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -644,6 +644,11 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
shortcut_menu->addAction(tr("Add to Applications Menu")); shortcut_menu->addAction(tr("Add to Applications Menu"));
#endif #endif
#ifdef ENABLE_DEVELOPER_OPTIONS
context_menu.addSeparator();
QAction* stress_test_launch = context_menu.addAction(tr("Stress Test: App Launch"));
#endif
context_menu.addSeparator(); context_menu.addSeparator();
QAction* properties = context_menu.addAction(tr("Properties")); QAction* properties = context_menu.addAction(tr("Properties"));
@ -755,6 +760,10 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, const QStr
[this, path, program_id] { emit DumpRomFSRequested(path, program_id); }); [this, path, program_id] { emit DumpRomFSRequested(path, program_id); });
connect(remove_play_time_data, &QAction::triggered, connect(remove_play_time_data, &QAction::triggered,
[this, program_id]() { emit RemovePlayTimeRequested(program_id); }); [this, program_id]() { emit RemovePlayTimeRequested(program_id); });
#ifdef ENABLE_DEVELOPER_OPTIONS
connect(stress_test_launch, &QAction::triggered,
[this, path]() { emit StartingLaunchStressTest(path); });
#endif
connect(properties, &QAction::triggered, this, connect(properties, &QAction::triggered, this,
[this, path]() { emit OpenPerGameGeneralRequested(path); }); [this, path]() { emit OpenPerGameGeneralRequested(path); });
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] { connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {

View File

@ -107,6 +107,7 @@ signals:
void AddDirectory(); void AddDirectory();
void ShowList(bool show); void ShowList(bool show);
void PopulatingCompleted(); void PopulatingCompleted();
void StartingLaunchStressTest(const QString& game_path);
private slots: private slots:
void OnItemExpanded(const QModelIndex& item); void OnItemExpanded(const QModelIndex& item);

View File

@ -1,4 +1,4 @@
// Copyright 2017 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -90,7 +90,8 @@ void DirectConnectWindow::Connect() {
room_member->Join(ui->nickname->text().toStdString(), room_member->Join(ui->nickname->text().toStdString(),
Service::CFG::GetConsoleIdHash(system), Service::CFG::GetConsoleIdHash(system),
ui->ip->text().toStdString().c_str(), port, 0, ui->ip->text().toStdString().c_str(), port, 0,
Network::NoPreferredMac, ui->password->text().toStdString().c_str()); Service::CFG::GetConsoleMacAddress(system),
ui->password->text().toStdString().c_str());
} }
}); });
watcher->setFuture(f); watcher->setFuture(f);

View File

@ -193,8 +193,8 @@ void HostRoomWindow::Host() {
} }
#endif #endif
member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system), member->Join(ui->username->text().toStdString(), Service::CFG::GetConsoleIdHash(system),
"127.0.0.1", static_cast<u16>(port), 0, Network::NoPreferredMac, password, "127.0.0.1", static_cast<u16>(port), 0,
token); Service::CFG::GetConsoleMacAddress(system), password, token);
// Store settings // Store settings
UISettings::values.room_nickname = ui->username->text(); UISettings::values.room_nickname = ui->username->text();

View File

@ -175,7 +175,8 @@ void Lobby::OnJoinRoom(const QModelIndex& source) {
#endif #endif
if (auto room_member = Network::GetRoomMember().lock()) { if (auto room_member = Network::GetRoomMember().lock()) {
room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(), room_member->Join(nickname, Service::CFG::GetConsoleIdHash(system), ip.c_str(),
static_cast<u16>(port), 0, Network::NoPreferredMac, password, token); static_cast<u16>(port), 0, Service::CFG::GetConsoleMacAddress(system),
password, token);
} }
}); });
watcher->setFuture(f); watcher->setFuture(f);

View File

@ -0,0 +1,9 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
namespace QtSwizzle {
void Dummy();
} // namespace QtSwizzle

View File

@ -0,0 +1,48 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#import <QtGui/private/qmetallayer_p.h>
#import <objc/runtime.h>
namespace QtSwizzle {
void Dummy() {
// Call this anywhere to make sure that qt_swizzle.mm is linked.
// noop
}
} // namespace QtSwizzle
@implementation QMetalLayer (AzaharPatch)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class targetClass = [self class];
// Get the original and swizzled methods
Method originalMethod =
class_getInstanceMethod(targetClass, @selector(setNeedsDisplayInRect:));
Method swizzledMethod =
class_getInstanceMethod(targetClass, @selector(swizzled_setNeedsDisplayInRect:));
// Swap the implementations
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)swizzled_setNeedsDisplayInRect:(CGRect)rect {
constexpr auto tooBig = 1e10; // Arbitrary large number
// Check for problematic huge rectangles
if ((!self.needsDisplay) && (rect.size.width > tooBig || rect.size.height > tooBig ||
rect.origin.x < -tooBig || rect.origin.y < -tooBig)) {
return;
}
// Call the original implementation
[self swizzled_setNeedsDisplayInRect:rect];
}
@end

View File

@ -67,6 +67,9 @@ HackManager hack_manager = {
0x0004013000002C02, // Normal 0x0004013000002C02, // Normal
0x0004013000002C03, // Safe mode 0x0004013000002C03, // Safe mode
0x0004013020002C03, // New 3DS safe mode 0x0004013020002C03, // New 3DS safe mode
// DLP
0x0004013000002802,
}, },
}}, }},

View File

@ -114,6 +114,7 @@ void LogSettings() {
} }
log_setting("Layout_LayoutOption", values.layout_option.GetValue()); log_setting("Layout_LayoutOption", values.layout_option.GetValue());
log_setting("Layout_PortraitLayoutOption", values.portrait_layout_option.GetValue()); log_setting("Layout_PortraitLayoutOption", values.portrait_layout_option.GetValue());
log_setting("Layout_SecondaryDisplayLayout", values.secondary_display_layout.GetValue());
log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); log_setting("Layout_SwapScreen", values.swap_screen.GetValue());
log_setting("Layout_UprightScreen", values.upright_screen.GetValue()); log_setting("Layout_UprightScreen", values.upright_screen.GetValue());
log_setting("Layout_ScreenGap", values.screen_gap.GetValue()); log_setting("Layout_ScreenGap", values.screen_gap.GetValue());
@ -207,6 +208,7 @@ void RestoreGlobalState(bool is_powered_on) {
values.delay_game_render_thread_us.SetGlobal(true); values.delay_game_render_thread_us.SetGlobal(true);
values.layout_option.SetGlobal(true); values.layout_option.SetGlobal(true);
values.portrait_layout_option.SetGlobal(true); values.portrait_layout_option.SetGlobal(true);
values.secondary_display_layout.SetGlobal(true);
values.swap_screen.SetGlobal(true); values.swap_screen.SetGlobal(true);
values.upright_screen.SetGlobal(true); values.upright_screen.SetGlobal(true);
values.large_screen_proportion.SetGlobal(true); values.large_screen_proportion.SetGlobal(true);

View File

@ -54,6 +54,7 @@ enum class PortraitLayoutOption : u32 {
PortraitOriginal PortraitOriginal
}; };
enum class SecondaryDisplayLayout : u32 { None, TopScreenOnly, BottomScreenOnly, SideBySide };
/** Defines where the small screen will appear relative to the large screen /** Defines where the small screen will appear relative to the large screen
* when in Large Screen mode * when in Large Screen mode
*/ */
@ -519,6 +520,8 @@ struct Values {
SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting<LayoutOption> layout_option{LayoutOption::Default, "layout_option"};
SwitchableSetting<bool> swap_screen{false, "swap_screen"}; SwitchableSetting<bool> swap_screen{false, "swap_screen"};
SwitchableSetting<bool> upright_screen{false, "upright_screen"}; SwitchableSetting<bool> upright_screen{false, "upright_screen"};
SwitchableSetting<SecondaryDisplayLayout> secondary_display_layout{SecondaryDisplayLayout::None,
"secondary_display_layout"};
SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f, SwitchableSetting<float, true> large_screen_proportion{4.f, 1.f, 16.f,
"large_screen_proportion"}; "large_screen_proportion"};
SwitchableSetting<int> screen_gap{0, "screen_gap"}; SwitchableSetting<int> screen_gap{0, "screen_gap"};
@ -582,9 +585,9 @@ struct Values {
SwitchableSetting<bool> enable_realtime_audio{false, "enable_realtime_audio"}; SwitchableSetting<bool> enable_realtime_audio{false, "enable_realtime_audio"};
SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"}; SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"};
Setting<AudioCore::SinkType> output_type{AudioCore::SinkType::Auto, "output_type"}; Setting<AudioCore::SinkType> output_type{AudioCore::SinkType::Auto, "output_type"};
Setting<std::string> output_device{"auto", "output_device"}; Setting<std::string> output_device{"Auto", "output_device"};
Setting<AudioCore::InputType> input_type{AudioCore::InputType::Auto, "input_type"}; Setting<AudioCore::InputType> input_type{AudioCore::InputType::Auto, "input_type"};
Setting<std::string> input_device{"auto", "input_device"}; Setting<std::string> input_device{"Auto", "input_device"};
// Camera // Camera
std::array<std::string, Service::CAM::NumCameras> camera_name; std::array<std::string, Service::CAM::NumCameras> camera_name;

View File

@ -66,6 +66,10 @@ bool EmuWindow::IsWithinTouchscreen(const Layout::FramebufferLayout& layout, uns
} }
#endif #endif
if (!layout.bottom_screen_enabled) {
return false;
}
Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue(); Settings::StereoRenderOption render_3d_mode = Settings::values.render_3d.GetValue();
if (render_3d_mode == Settings::StereoRenderOption::SideBySide || if (render_3d_mode == Settings::StereoRenderOption::SideBySide ||
@ -271,6 +275,11 @@ void EmuWindow::UpdateCurrentFramebufferLayout(u32 width, u32 height, bool is_po
break; break;
} }
} }
#ifdef ANDROID
if (is_secondary) {
layout = Layout::AndroidSecondaryLayout(width, height);
}
#endif
UpdateMinimumWindowSize(min_size); UpdateMinimumWindowSize(min_size);
if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) {

View File

@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project // Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
@ -254,6 +254,9 @@ public:
bool is_portrait_mode = {}); bool is_portrait_mode = {});
std::unique_ptr<TextureMailbox> mailbox = nullptr; std::unique_ptr<TextureMailbox> mailbox = nullptr;
bool isSecondary() const {
return is_secondary;
}
protected: protected:
EmuWindow(); EmuWindow();

View File

@ -79,7 +79,7 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
// TODO: This is kind of gross, make it platform agnostic. -OS // TODO: This is kind of gross, make it platform agnostic. -OS
#ifdef ANDROID #ifdef ANDROID
const float window_aspect_ratio = static_cast<float>(height) / width; const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const auto aspect_ratio_setting = Settings::values.aspect_ratio.GetValue(); const auto aspect_ratio_setting = Settings::values.aspect_ratio.GetValue();
float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO; float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
@ -172,7 +172,7 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
emulation_height = std::max(large_height, small_height); emulation_height = std::max(large_height, small_height);
} }
const float window_aspect_ratio = static_cast<float>(height) / width; const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const float emulation_aspect_ratio = emulation_height / emulation_width; const float emulation_aspect_ratio = emulation_height / emulation_width;
Common::Rectangle<u32> screen_window_area{0, 0, width, height}; Common::Rectangle<u32> screen_window_area{0, 0, width, height};
@ -281,7 +281,7 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
// Split the window into two parts. Give 2.25x width to the main screen, // Split the window into two parts. Give 2.25x width to the main screen,
// and make a bar on the right side with 1x width top screen and 1.25x width bottom screen // and make a bar on the right side with 1x width top screen and 1.25x width bottom screen
// To do that, find the total emulation box and maximize that based on window size // To do that, find the total emulation box and maximize that based on window size
const float window_aspect_ratio = static_cast<float>(height) / width; const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const float scale_factor = 2.25f; const float scale_factor = 2.25f;
float main_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO; float main_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
@ -338,6 +338,24 @@ FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary
return SingleFrameLayout(width, height, is_secondary, upright); return SingleFrameLayout(width, height, is_secondary, upright);
} }
FramebufferLayout AndroidSecondaryLayout(u32 width, u32 height) {
const Settings::SecondaryDisplayLayout layout =
Settings::values.secondary_display_layout.GetValue();
switch (layout) {
case Settings::SecondaryDisplayLayout::BottomScreenOnly:
return SingleFrameLayout(width, height, true, Settings::values.upright_screen.GetValue());
case Settings::SecondaryDisplayLayout::SideBySide:
return LargeFrameLayout(width, height, false, Settings::values.upright_screen.GetValue(),
1.0f, Settings::SmallScreenPosition::MiddleRight);
case Settings::SecondaryDisplayLayout::None:
// this should never happen, but if it does, somehow, send the top screen
case Settings::SecondaryDisplayLayout::TopScreenOnly:
default:
return SingleFrameLayout(width, height, false, Settings::values.upright_screen.GetValue());
}
}
FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool is_portrait_mode) { FramebufferLayout CustomFrameLayout(u32 width, u32 height, bool is_swapped, bool is_portrait_mode) {
ASSERT(width > 0); ASSERT(width > 0);
ASSERT(height > 0); ASSERT(height > 0);

View File

@ -133,6 +133,14 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
*/ */
FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright); FramebufferLayout SeparateWindowsLayout(u32 width, u32 height, bool is_secondary, bool upright);
/**
* Method for constructing the secondary layout for Android, based on
* the appropriate setting.
* @param width Window framebuffer width in pixels
* @param height Window framebuffer height in pixels
*/
FramebufferLayout AndroidSecondaryLayout(u32 width, u32 height);
/** /**
* Factory method for constructing a custom FramebufferLayout * Factory method for constructing a custom FramebufferLayout
* @param width Window framebuffer width in pixels * @param width Window framebuffer width in pixels

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