forked from MeloNX/MeloNX
Add On-Screen controller and Fix Internet and Lan Multiplayer
This commit is contained in:
parent
b2424a9652
commit
9a86b2000a
@ -59,7 +59,7 @@
|
|||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */
|
||||||
4E80AA0A2CD6FAA800029585 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */ = {
|
4E9A82F32CF87822006D7086 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */ = {
|
||||||
isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
|
isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
|
||||||
attributesByRelativePath = {
|
attributesByRelativePath = {
|
||||||
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (CodeSignOnCopy, );
|
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (CodeSignOnCopy, );
|
||||||
@ -68,13 +68,13 @@
|
|||||||
"Dependencies/Dynamic Libraries/libavutil.dylib" = (CodeSignOnCopy, );
|
"Dependencies/Dynamic Libraries/libavutil.dylib" = (CodeSignOnCopy, );
|
||||||
Dependencies/XCFrameworks/MoltenVK.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/MoltenVK.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/SDL2.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/SDL2.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libSPIRV.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
|
||||||
Dependencies/XCFrameworks/libavcodec.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libavcodec.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libavfilter.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libavfilter.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libavformat.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libavformat.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libavutil.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libavutil.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libswresample.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libswresample.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
Dependencies/XCFrameworks/libswscale.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
Dependencies/XCFrameworks/libswscale.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
|
Dependencies/XCFrameworks/libteakra.xcframework = (CodeSignOnCopy, RemoveHeadersOnCopy, );
|
||||||
};
|
};
|
||||||
buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */;
|
buildPhase = 4E80AA092CD6FAA800029585 /* Embed Libraries */;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
@ -86,9 +86,9 @@
|
|||||||
Dependencies/XCFrameworks/libavfilter.xcframework,
|
Dependencies/XCFrameworks/libavfilter.xcframework,
|
||||||
Dependencies/XCFrameworks/libavformat.xcframework,
|
Dependencies/XCFrameworks/libavformat.xcframework,
|
||||||
Dependencies/XCFrameworks/libavutil.xcframework,
|
Dependencies/XCFrameworks/libavutil.xcframework,
|
||||||
Dependencies/XCFrameworks/libSPIRV.xcframework,
|
|
||||||
Dependencies/XCFrameworks/libswresample.xcframework,
|
Dependencies/XCFrameworks/libswresample.xcframework,
|
||||||
Dependencies/XCFrameworks/libswscale.xcframework,
|
Dependencies/XCFrameworks/libswscale.xcframework,
|
||||||
|
Dependencies/XCFrameworks/libteakra.xcframework,
|
||||||
Dependencies/XCFrameworks/MoltenVK.xcframework,
|
Dependencies/XCFrameworks/MoltenVK.xcframework,
|
||||||
Dependencies/XCFrameworks/SDL2.xcframework,
|
Dependencies/XCFrameworks/SDL2.xcframework,
|
||||||
);
|
);
|
||||||
@ -100,7 +100,7 @@
|
|||||||
isa = PBXFileSystemSynchronizedRootGroup;
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
exceptions = (
|
exceptions = (
|
||||||
4E80AA1D2CD7015100029585 /* Exceptions for "MeloNX" folder in "MeloNX" target */,
|
4E80AA1D2CD7015100029585 /* Exceptions for "MeloNX" folder in "MeloNX" target */,
|
||||||
4E80AA0A2CD6FAA800029585 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */,
|
4E9A82F32CF87822006D7086 /* Exceptions for "MeloNX" folder in "Embed Libraries" phase from "MeloNX" target */,
|
||||||
);
|
);
|
||||||
path = MeloNX;
|
path = MeloNX;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -542,6 +542,18 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
@ -622,6 +634,18 @@
|
|||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
|
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
|
||||||
|
Binary file not shown.
@ -55,7 +55,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
|
@ -23,6 +23,7 @@ class MTLHud {
|
|||||||
static let shared = MTLHud()
|
static let shared = MTLHud()
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
|
openMetalDylib()
|
||||||
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
if UserDefaults.standard.bool(forKey: "MTL_HUD_ENABLED") {
|
||||||
enable()
|
enable()
|
||||||
} else {
|
} else {
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// VirtualController.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 28/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import GameController
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public var controllerCallback: (() -> Void)?
|
||||||
|
|
||||||
|
var VirtualController: GCVirtualController!
|
||||||
|
func showVirtualController() {
|
||||||
|
let config = GCVirtualController.Configuration()
|
||||||
|
if UserDefaults.standard.bool(forKey: "RyuDemoControls") {
|
||||||
|
config.elements = [
|
||||||
|
GCInputLeftThumbstick,
|
||||||
|
GCInputButtonA,
|
||||||
|
GCInputButtonB,
|
||||||
|
GCInputButtonX,
|
||||||
|
GCInputButtonY,
|
||||||
|
// GCInputRightThumbstick,
|
||||||
|
GCInputRightTrigger,
|
||||||
|
GCInputLeftTrigger,
|
||||||
|
GCInputLeftShoulder,
|
||||||
|
GCInputRightShoulder
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
config.elements = [
|
||||||
|
GCInputLeftThumbstick,
|
||||||
|
GCInputButtonA,
|
||||||
|
GCInputButtonB,
|
||||||
|
GCInputButtonX,
|
||||||
|
GCInputButtonY,
|
||||||
|
GCInputRightThumbstick,
|
||||||
|
GCInputRightTrigger,
|
||||||
|
GCInputLeftTrigger,
|
||||||
|
GCInputLeftShoulder,
|
||||||
|
GCInputRightShoulder
|
||||||
|
]
|
||||||
|
}
|
||||||
|
VirtualController = GCVirtualController(configuration: config)
|
||||||
|
VirtualController.connect { err in
|
||||||
|
print("controller connect: \(String(describing: err))")
|
||||||
|
patchMakeKeyAndVisible()
|
||||||
|
if let controllerCallback {
|
||||||
|
controllerCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitforcontroller() {
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
|
||||||
|
|
||||||
|
if let window = UIApplication.shared.windows.first {
|
||||||
|
// Function to recursively search for GCControllerView
|
||||||
|
func findGCControllerView(in view: UIView) -> UIView? {
|
||||||
|
// Check if current view is GCControllerView
|
||||||
|
if String(describing: type(of: view)) == "GCControllerView" {
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search through subviews
|
||||||
|
for subview in view.subviews {
|
||||||
|
if let found = findGCControllerView(in: subview) {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let gcControllerView = findGCControllerView(in: window) {
|
||||||
|
// Found the GCControllerView
|
||||||
|
print("Found GCControllerView:", gcControllerView)
|
||||||
|
|
||||||
|
if let theWindow = theWindow, (findGCControllerView(in: theWindow) == nil) {
|
||||||
|
theWindow.addSubview(gcControllerView)
|
||||||
|
|
||||||
|
theWindow.bringSubviewToFront(gcControllerView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15.0, *)
|
||||||
|
func reconnectVirtualController() {
|
||||||
|
VirtualController.disconnect()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
VirtualController.connect { err in
|
||||||
|
print("reconnected: err \(String(describing: err))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
40
src/MeloNX/MeloNX/Core/Swift/Display/DisplayVisible.swift
Normal file
40
src/MeloNX/MeloNX/Core/Swift/Display/DisplayVisible.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Untitled.swift
|
||||||
|
// MeloNX
|
||||||
|
//
|
||||||
|
// Created by Stossy11 on 28/11/2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import GameController
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var theWindow: UIWindow? = nil
|
||||||
|
extension UIWindow {
|
||||||
|
@objc func wdb_makeKeyAndVisible() {
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
self.windowScene = (UIApplication.shared.connectedScenes.first! as! UIWindowScene)
|
||||||
|
}
|
||||||
|
self.wdb_makeKeyAndVisible()
|
||||||
|
theWindow = self
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
reconnectVirtualController()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if let window = theWindow {
|
||||||
|
waitforcontroller()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func patchMakeKeyAndVisible() {
|
||||||
|
let uiwindowClass = UIWindow.self
|
||||||
|
if let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible)),
|
||||||
|
let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible)) {
|
||||||
|
method_exchangeImplementations(m1, m2)
|
||||||
|
}
|
||||||
|
}
|
@ -44,6 +44,8 @@ class Ryujinx {
|
|||||||
var resscale: Float
|
var resscale: Float
|
||||||
var debuglogs: Bool
|
var debuglogs: Bool
|
||||||
var tracelogs: Bool
|
var tracelogs: Bool
|
||||||
|
var nintendoinput: Bool
|
||||||
|
var enableInternet: Bool
|
||||||
var listinputids: Bool
|
var listinputids: Bool
|
||||||
var fullscreen: Bool
|
var fullscreen: Bool
|
||||||
var memoryManagerMode: String
|
var memoryManagerMode: String
|
||||||
@ -64,6 +66,8 @@ class Ryujinx {
|
|||||||
disableVSync: Bool = false,
|
disableVSync: Bool = false,
|
||||||
disableShaderCache: Bool = false,
|
disableShaderCache: Bool = false,
|
||||||
disableDockedMode: Bool = false,
|
disableDockedMode: Bool = false,
|
||||||
|
nintendoinput: Bool = true,
|
||||||
|
enableInternet: Bool = false,
|
||||||
enableTextureRecompression: Bool = true,
|
enableTextureRecompression: Bool = true,
|
||||||
additionalArgs: [String] = [],
|
additionalArgs: [String] = [],
|
||||||
resscale: Float = 1.00
|
resscale: Float = 1.00
|
||||||
@ -81,6 +85,8 @@ class Ryujinx {
|
|||||||
self.additionalArgs = additionalArgs
|
self.additionalArgs = additionalArgs
|
||||||
self.memoryManagerMode = memoryManagerMode
|
self.memoryManagerMode = memoryManagerMode
|
||||||
self.resscale = resscale
|
self.resscale = resscale
|
||||||
|
self.nintendoinput = nintendoinput
|
||||||
|
self.enableInternet = enableInternet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,6 +157,13 @@ class Ryujinx {
|
|||||||
args.append(contentsOf: ["--resolution-scale", String(config.resscale)])
|
args.append(contentsOf: ["--resolution-scale", String(config.resscale)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.nintendoinput {
|
||||||
|
args.append("--correct-ons-controller")
|
||||||
|
}
|
||||||
|
if config.enableInternet {
|
||||||
|
args.append("--enable-internet-connection")
|
||||||
|
}
|
||||||
|
|
||||||
// Adding default args directly into additionalArgs
|
// Adding default args directly into additionalArgs
|
||||||
if config.disableVSync {
|
if config.disableVSync {
|
||||||
args.append("--disable-vsync")
|
args.append("--disable-vsync")
|
||||||
|
@ -16,196 +16,230 @@ struct MoltenVKSettings: Codable, Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@State public var theWindow: UIWindow? = nil
|
// MARK: - Properties
|
||||||
|
@State private var theWindow: UIWindow?
|
||||||
@State private var virtualController: GCVirtualController?
|
@State private var virtualController: GCVirtualController?
|
||||||
@State var game: URL? = nil
|
@State private var game: URL?
|
||||||
@State var controllersList: [Controller] = []
|
@State private var controllersList: [Controller] = []
|
||||||
@State var currentControllers: [Controller] = []
|
@State private var currentControllers: [Controller] = []
|
||||||
@State var config: Ryujinx.Configuration = Ryujinx.Configuration(gamepath: "")
|
@State private var config: Ryujinx.Configuration
|
||||||
|
@State private var settings: [MoltenVKSettings]
|
||||||
@State var settings: [MoltenVKSettings] = [
|
@State private var isVirtualControllerActive: Bool = false
|
||||||
// MoltenVKSettings(string: "MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS", value: ""),
|
|
||||||
// MoltenVKSettings(string: "MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS", value: "1"),
|
|
||||||
MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "1024"),
|
|
||||||
MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", value: "1"),
|
|
||||||
MoltenVKSettings(string: "MVK_CONFIG_RESUME_LOST_DEVICE", value: "1")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
// MARK: - Initialization
|
||||||
init() {
|
init() {
|
||||||
// Initialize SDL
|
let defaultConfig = Ryujinx.Configuration(gamepath: "")
|
||||||
DispatchQueue.main.async { [self] in
|
_config = State(initialValue: defaultConfig)
|
||||||
setMoltenVKSettings()
|
|
||||||
SDL_SetMainReady()
|
let defaultSettings: [MoltenVKSettings] = [
|
||||||
SDL_iPhoneSetEventPump(SDL_TRUE)
|
MoltenVKSettings(string: "MVK_CONFIG_MAX_ACTIVE_METAL_COMMAND_BUFFERS_PER_QUEUE", value: "1024"),
|
||||||
SDL_Init(SDL_INIT_VIDEO)
|
MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", value: "1"),
|
||||||
patchMakeKeyAndVisible()
|
MoltenVKSettings(string: "MVK_CONFIG_RESUME_LOST_DEVICE", value: "1")
|
||||||
|
]
|
||||||
|
_settings = State(initialValue: defaultSettings)
|
||||||
|
|
||||||
|
initializeSDL()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Body
|
||||||
|
var body: some View {
|
||||||
|
iOSNav {
|
||||||
|
if let game {
|
||||||
|
emulationView
|
||||||
|
} else {
|
||||||
|
mainMenuView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: isVirtualControllerActive) { newValue in
|
||||||
|
if newValue {
|
||||||
|
createVirtualController()
|
||||||
|
} else {
|
||||||
|
destroyVirtualController()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupVirtualController() {
|
// MARK: - View Components
|
||||||
|
private var emulationView: some View {
|
||||||
|
ZStack {}
|
||||||
|
.onAppear {
|
||||||
|
setupEmulation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mainMenuView: some View {
|
||||||
|
HStack {
|
||||||
|
GameListView(startemu: $game)
|
||||||
|
.onAppear {
|
||||||
|
createVirtualController()
|
||||||
|
refreshControllersList()
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsListView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var settingsListView: some View {
|
||||||
|
List {
|
||||||
|
Section("Settings") {
|
||||||
|
NavigationLink("Config") {
|
||||||
|
SettingsView(config: $config, MoltenVKSettings: $settings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section("Controller") {
|
||||||
|
Button("Refresh", action: refreshControllersList)
|
||||||
|
|
||||||
|
ForEach(controllersList, id: \.self) { controller in
|
||||||
|
if controller.name != "Apple Touch Controller" {
|
||||||
|
controllerRow(for: controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func controllerRow(for controller: Controller) -> some View {
|
||||||
|
HStack {
|
||||||
|
Button(controller.name) {
|
||||||
|
toggleController(controller)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Controller Management
|
||||||
|
private func createVirtualController() {
|
||||||
let configuration = GCVirtualController.Configuration()
|
let configuration = GCVirtualController.Configuration()
|
||||||
configuration.elements = [
|
configuration.elements = [
|
||||||
|
/*
|
||||||
GCInputLeftThumbstick,
|
GCInputLeftThumbstick,
|
||||||
GCInputRightThumbstick,
|
GCInputRightThumbstick,
|
||||||
GCInputButtonA,
|
GCInputButtonA,
|
||||||
GCInputButtonB,
|
GCInputButtonB,
|
||||||
GCInputButtonX,
|
GCInputButtonX,
|
||||||
GCInputButtonY
|
GCInputButtonY,
|
||||||
|
*/
|
||||||
]
|
]
|
||||||
|
|
||||||
let controller = GCVirtualController(configuration: configuration)
|
virtualController = GCVirtualController(configuration: configuration)
|
||||||
self.virtualController = controller
|
virtualController?.connect()
|
||||||
self.virtualController?.connect()
|
|
||||||
|
controllersList.removeAll(where: { $0.name == "Apple Touch Controller" })
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
private func destroyVirtualController() {
|
||||||
iOSNav {
|
virtualController?.disconnect()
|
||||||
|
virtualController = nil
|
||||||
|
|
||||||
|
// Remove virtual controller from current controllers
|
||||||
|
controllersList.removeAll(where: { $0.name == "Apple Touch Controller" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helper Methods
|
||||||
|
private func initializeSDL() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
setMoltenVKSettings()
|
||||||
|
SDL_SetMainReady()
|
||||||
|
SDL_iPhoneSetEventPump(SDL_TRUE)
|
||||||
|
SDL_Init(SDL_INIT_VIDEO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupEmulation() {
|
||||||
|
virtualController?.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
controllerCallback = {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||||
|
currentControllers.removeAll(where: { $0.name == "Apple Touch Controller" })
|
||||||
|
if controllersList.count == 2,
|
||||||
|
controllersList.contains(where: { $0.name == "Apple Touch Controller" }) {
|
||||||
|
currentControllers.append(controllersList[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
print(currentControllers)
|
||||||
|
start(displayid: 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
showVirtualController()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func refreshControllersList() {
|
||||||
|
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||||
|
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||||
|
controllersList.removeAll(where: { $0.id == "0" })
|
||||||
|
|
||||||
if let game {
|
controllersList.removeAll(where: { $0.name == "Apple Touch Controller" })
|
||||||
ZStack {
|
|
||||||
|
if let controller = controllersList.first, !controllersList.isEmpty {
|
||||||
}
|
currentControllers.append(controller)
|
||||||
.onAppear {
|
|
||||||
start(displayid: 0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HStack {
|
|
||||||
GameListView(startemu: $game)
|
|
||||||
.onAppear() {
|
|
||||||
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { _ in
|
|
||||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
|
||||||
controllersList.removeAll(where: { $0.id == "0" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List {
|
|
||||||
Section("Settings") {
|
|
||||||
NavigationLink {
|
|
||||||
SettingsView(config: $config, MoltenVKSettings: $settings)
|
|
||||||
} label: {
|
|
||||||
Text("Config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Section("Controller") {
|
|
||||||
Button {
|
|
||||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
|
||||||
controllersList.removeAll(where: { $0.id == "0" })
|
|
||||||
} label: {
|
|
||||||
Text("Refresh")
|
|
||||||
}
|
|
||||||
ForEach(controllersList, id: \.self) { controller in
|
|
||||||
HStack {
|
|
||||||
Button {
|
|
||||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
|
||||||
currentControllers.removeAll(where: { $0.id == controller.id })
|
|
||||||
} else {
|
|
||||||
currentControllers.append(controller)
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Text(controller.name)
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
|
||||||
Image(systemName: "checkmark.circle.fill")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(displayid: UInt32) {
|
private func toggleController(_ controller: Controller) {
|
||||||
|
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||||
if let game {
|
currentControllers.removeAll(where: { $0.id == controller.id })
|
||||||
self.config.gamepath = game.path
|
|
||||||
|
|
||||||
self.config.inputids = currentControllers.map(\.id)
|
|
||||||
|
|
||||||
allocateSixGB()
|
|
||||||
|
|
||||||
// Start the emulation
|
|
||||||
|
|
||||||
print("Is MetalHud Enabled? " + (MTLHud.shared.isEnabled ? "yeah" : "nope"))
|
|
||||||
do {
|
|
||||||
setupVirtualController()
|
|
||||||
|
|
||||||
try Ryujinx.shared.start(with: config)
|
|
||||||
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
print("Error \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
currentControllers.append(controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func allocateSixGB() -> UnsafeMutableRawPointer? {
|
private func start(displayid: UInt32) {
|
||||||
|
guard let game else { return }
|
||||||
|
|
||||||
|
config.gamepath = game.path
|
||||||
|
config.inputids = currentControllers.map(\.id)
|
||||||
|
|
||||||
|
allocateMemory()
|
||||||
|
|
||||||
|
do {
|
||||||
|
try Ryujinx.shared.start(with: config)
|
||||||
|
} catch {
|
||||||
|
print("Error: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func allocateMemory() {
|
||||||
let physicalMemory = ProcessInfo.processInfo.physicalMemory
|
let physicalMemory = ProcessInfo.processInfo.physicalMemory
|
||||||
let totalMemoryInGB = Double(physicalMemory) / (1024 * 1024 * 1024)
|
let totalMemoryInGB = Double(physicalMemory) / (1024 * 1024 * 1024)
|
||||||
let mem = totalMemoryInGB
|
|
||||||
print(mem)
|
let pointer = UnsafeMutableRawPointer.allocate(
|
||||||
// Allocate memory
|
byteCount: Int(totalMemoryInGB),
|
||||||
let pointer = UnsafeMutableRawPointer.allocate(byteCount: Int(mem), alignment: MemoryLayout<UInt8>.alignment)
|
alignment: MemoryLayout<UInt8>.alignment
|
||||||
|
)
|
||||||
// Optionally initialize the memory
|
pointer.initializeMemory(as: UInt8.self, repeating: 0, count: Int(totalMemoryInGB))
|
||||||
pointer.initializeMemory(as: UInt8.self, repeating: 0, count: Int(mem))
|
|
||||||
|
|
||||||
print("Successfully allocated 6GB of memory.")
|
|
||||||
return pointer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func patchMakeKeyAndVisible() {
|
|
||||||
let uiwindowClass = UIWindow.self
|
|
||||||
if let m1 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.makeKeyAndVisible)),
|
|
||||||
let m2 = class_getInstanceMethod(uiwindowClass, #selector(UIWindow.wdb_makeKeyAndVisible)) {
|
|
||||||
method_exchangeImplementations(m1, m2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func setMoltenVKSettings() {
|
private func setMoltenVKSettings() {
|
||||||
|
|
||||||
if let configs = loadSettings() {
|
if let configs = loadSettings() {
|
||||||
self.config = configs
|
self.config = configs
|
||||||
print(configs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.forEach { setting in
|
settings.forEach { setting in
|
||||||
setenv(setting.string, setting.value, 1)
|
setenv(setting.string, setting.value, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Helper Functions
|
||||||
func loadSettings() -> Ryujinx.Configuration? {
|
func loadSettings() -> Ryujinx.Configuration? {
|
||||||
guard let jsonString = UserDefaults.standard.string(forKey: "config") else {
|
guard let jsonString = UserDefaults.standard.string(forKey: "config"),
|
||||||
|
let data = jsonString.data(using: .utf8) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let decoder = JSONDecoder()
|
return try JSONDecoder().decode(Ryujinx.Configuration.self, from: data)
|
||||||
if let data = jsonString.data(using: .utf8) {
|
|
||||||
return try decoder.decode(Ryujinx.Configuration.self, from: data)
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Failed to load settings: \(error)")
|
print("Failed to load settings: \(error)")
|
||||||
}
|
return nil
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UIWindow {
|
|
||||||
@objc func wdb_makeKeyAndVisible() {
|
|
||||||
print("Making window key and visible...")
|
|
||||||
|
|
||||||
self.windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene
|
|
||||||
|
|
||||||
self.wdb_makeKeyAndVisible()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,19 +14,22 @@ struct SettingsView: View {
|
|||||||
var memoryManagerModes = [
|
var memoryManagerModes = [
|
||||||
("HostMapped", "Host (fast)"),
|
("HostMapped", "Host (fast)"),
|
||||||
("HostMappedUnsafe", "Host Unchecked (fast, unstable / unsafe)"),
|
("HostMappedUnsafe", "Host Unchecked (fast, unstable / unsafe)"),
|
||||||
("SoftwarePageTable", "Software")
|
("SoftwarePageTable", "Software (slow)"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@AppStorage("RyuDemoControls") var ryuDemo: Bool = false
|
||||||
|
|
||||||
@AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false
|
@AppStorage("MTL_HUD_ENABLED") var metalHUDEnabled: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack {
|
VStack {
|
||||||
Section(header: Text("Graphics and Performance")) {
|
Section(header: Title("Graphics and Performance")) {
|
||||||
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
Toggle("Ryujinx Fullscreen", isOn: $config.fullscreen)
|
||||||
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
Toggle("Disable V-Sync", isOn: $config.disableVSync)
|
||||||
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
Toggle("Disable Shader Cache", isOn: $config.disableShaderCache)
|
||||||
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
Toggle("Enable Texture Recompression", isOn: $config.enableTextureRecompression)
|
||||||
|
Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
|
||||||
Resolution(value: $config.resscale)
|
Resolution(value: $config.resscale)
|
||||||
Toggle("Enable Metal HUD", isOn: $metalHUDEnabled)
|
Toggle("Enable Metal HUD", isOn: $metalHUDEnabled)
|
||||||
.onChange(of: metalHUDEnabled) { newValue in
|
.onChange(of: metalHUDEnabled) { newValue in
|
||||||
@ -38,17 +41,18 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: Text("Input Settings")) {
|
Section(header: Title("Input Settings")) {
|
||||||
Toggle("List Input IDs", isOn: $config.listinputids)
|
Toggle("List Input IDs", isOn: $config.listinputids)
|
||||||
|
Toggle("Nintendo Controller Layout", isOn: $config.nintendoinput)
|
||||||
|
Toggle("Ryujinx Demo On-Screen Controller", isOn: $ryuDemo)
|
||||||
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
||||||
Toggle("Disable Docked Mode", isOn: $config.disableDockedMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: Text("Logging Settings")) {
|
Section(header: Title("Logging Settings")) {
|
||||||
Toggle("Enable Debug Logs", isOn: $config.debuglogs)
|
Toggle("Enable Debug Logs", isOn: $config.debuglogs)
|
||||||
Toggle("Enable Trace Logs", isOn: $config.tracelogs)
|
Toggle("Enable Trace Logs", isOn: $config.tracelogs)
|
||||||
}
|
}
|
||||||
Section(header: Text("CPU Mode")) {
|
Section(header: Title("CPU Mode")) {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
Picker("Memory Manager Mode", selection: $config.memoryManagerMode) {
|
Picker("Memory Manager Mode", selection: $config.memoryManagerMode) {
|
||||||
@ -62,9 +66,11 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Section(header: Text("Additional Settings")) {
|
Section(header: Title("Additional Settings")) {
|
||||||
//TextField("Game Path", text: $config.gamepath)
|
//TextField("Game Path", text: $config.gamepath)
|
||||||
|
|
||||||
|
Text("PageSize \(String(Int(getpagesize())))")
|
||||||
|
|
||||||
TextField("Additional Arguments", text: Binding(
|
TextField("Additional Arguments", text: Binding(
|
||||||
get: {
|
get: {
|
||||||
config.additionalArgs.joined(separator: ", ")
|
config.additionalArgs.joined(separator: ", ")
|
||||||
@ -75,8 +81,8 @@ struct SettingsView: View {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if let configs = loadSettings() {
|
if let configs = loadSettings() {
|
||||||
self.config = configs
|
self.config = configs
|
||||||
@ -158,3 +164,20 @@ extension NumberFormatter {
|
|||||||
return formatter
|
return formatter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Title: View {
|
||||||
|
let string: String
|
||||||
|
|
||||||
|
init(_ string: String) {
|
||||||
|
self.string = string
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Text(string)
|
||||||
|
.font(.title2)
|
||||||
|
Divider()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
@ -10,12 +11,13 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
IPInterfaceProperties properties = adapter.GetIPProperties();
|
IPInterfaceProperties properties = adapter.GetIPProperties();
|
||||||
|
|
||||||
if (isPreferred || (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0))
|
// Skip problematic checks on non-Windows and iOS platforms
|
||||||
|
if (isPreferred || OperatingSystem.IsWindows() || properties.UnicastAddresses.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
|
foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
|
||||||
{
|
{
|
||||||
// Only accept an IPv4 address
|
// Only accept an IPv4 address
|
||||||
if (info.Address.GetAddressBytes().Length == 4)
|
if (info.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||||
{
|
{
|
||||||
return (properties, info);
|
return (properties, info);
|
||||||
}
|
}
|
||||||
@ -44,8 +46,9 @@ namespace Ryujinx.Common.Utilities
|
|||||||
{
|
{
|
||||||
bool isPreferred = adapter.Id == guid;
|
bool isPreferred = adapter.Id == guid;
|
||||||
|
|
||||||
// Ignore loopback and non IPv4 capable interface.
|
// Ignore loopback and ensure the adapter supports IPv4
|
||||||
if (isPreferred || (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)))
|
if (isPreferred ||
|
||||||
|
(targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4)))
|
||||||
{
|
{
|
||||||
(IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred);
|
(IPInterfaceProperties properties, UnicastIPAddressInformation info) = GetLocalInterface(adapter, isPreferred);
|
||||||
|
|
||||||
@ -77,7 +80,13 @@ namespace Ryujinx.Common.Utilities
|
|||||||
|
|
||||||
public static IPAddress ConvertUint(uint ipAddress)
|
public static IPAddress ConvertUint(uint ipAddress)
|
||||||
{
|
{
|
||||||
return new IPAddress(new byte[] { (byte)((ipAddress >> 24) & 0xFF), (byte)((ipAddress >> 16) & 0xFF), (byte)((ipAddress >> 8) & 0xFF), (byte)(ipAddress & 0xFF) });
|
return new IPAddress(new byte[]
|
||||||
|
{
|
||||||
|
(byte)((ipAddress >> 24) & 0xFF),
|
||||||
|
(byte)((ipAddress >> 16) & 0xFF),
|
||||||
|
(byte)((ipAddress >> 8) & 0xFF),
|
||||||
|
(byte)(ipAddress & 0xFF)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,35 +107,53 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
CreateFonts(uiTheme.FontFamily);
|
CreateFonts(uiTheme.FontFamily);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateFonts(string uiThemeFontFamily)
|
private void CreateFonts(string uiThemeFontFamily)
|
||||||
|
{
|
||||||
|
// Try a list of fonts in case any of them is not available in the system.
|
||||||
|
string[] availableFonts = { uiThemeFontFamily };
|
||||||
|
|
||||||
|
// If it's iOS, we'll want to use a more appropriate set of fonts.
|
||||||
|
if (OperatingSystem.IsIOS())
|
||||||
|
{
|
||||||
|
availableFonts = new string[] {
|
||||||
|
"Chalkboard",
|
||||||
|
"Chalkboard", // San Francisco is the default font on iOS
|
||||||
|
"Chalkboard", // Legacy iOS font
|
||||||
|
"Chalkboard" // Common system font
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback for other platforms (e.g., Android, Windows, etc.)
|
||||||
|
availableFonts = new string[] {
|
||||||
|
uiThemeFontFamily,
|
||||||
|
"Liberation Sans",
|
||||||
|
"FreeSans",
|
||||||
|
"DejaVu Sans",
|
||||||
|
"Lucida Grande"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create the fonts with the selected font families
|
||||||
|
foreach (string fontFamily in availableFonts)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// Try a list of fonts in case any of them is not available in the system.
|
_messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
|
||||||
|
_inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
|
||||||
|
_labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
|
||||||
|
|
||||||
string[] availableFonts = {
|
return;
|
||||||
uiThemeFontFamily,
|
|
||||||
"Liberation Sans",
|
|
||||||
"FreeSans",
|
|
||||||
"DejaVu Sans",
|
|
||||||
"Lucida Grande",
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (string fontFamily in availableFonts)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
|
|
||||||
_inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
|
|
||||||
_labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
|
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If the font creation fails, try the next font family
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
|
private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
|
namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
|
||||||
{
|
{
|
||||||
@ -15,16 +16,23 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
|
|||||||
public DnsSetting(IPInterfaceProperties interfaceProperties)
|
public DnsSetting(IPInterfaceProperties interfaceProperties)
|
||||||
{
|
{
|
||||||
IsDynamicDnsEnabled = OperatingSystem.IsWindows() && interfaceProperties.IsDynamicDnsEnabled;
|
IsDynamicDnsEnabled = OperatingSystem.IsWindows() && interfaceProperties.IsDynamicDnsEnabled;
|
||||||
|
|
||||||
|
IPAddress ip = IPAddress.Parse("1.1.1.1");
|
||||||
|
|
||||||
if (interfaceProperties.DnsAddresses.Count == 0)
|
if (OperatingSystem.IsIOS()) {
|
||||||
{
|
PrimaryDns = new IpV4Address(ip);
|
||||||
PrimaryDns = new IpV4Address();
|
SecondaryDns = new IpV4Address(ip);
|
||||||
SecondaryDns = new IpV4Address();
|
} else {
|
||||||
}
|
if (interfaceProperties.DnsAddresses.Count == 0)
|
||||||
else
|
{
|
||||||
{
|
PrimaryDns = new IpV4Address();
|
||||||
PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]);
|
SecondaryDns = new IpV4Address();
|
||||||
SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[interfaceProperties.DnsAddresses.Count > 1 ? 1 : 0]);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrimaryDns = new IpV4Address(interfaceProperties.DnsAddresses[0]);
|
||||||
|
SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[interfaceProperties.DnsAddresses.Count > 1 ? 1 : 0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
|
[Option("correct-ons-controller", Required = false, Default = false, HelpText = "Makes the on-screen controller (iOS) buttons correspond to what they show.")]
|
||||||
|
public bool OnScreenCorrespond { get; set; }
|
||||||
|
|
||||||
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
|
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
|
||||||
public string InputProfile1Name { get; set; }
|
public string InputProfile1Name { get; set; }
|
||||||
|
|
||||||
|
@ -127,11 +127,17 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser.Default.ParseArguments<Options>(args)
|
var result = Parser.Default.ParseArguments<Options>(args)
|
||||||
.WithParsed(Load)
|
.WithParsed(options =>
|
||||||
.WithNotParsed(errors => errors.Output());
|
{
|
||||||
|
Load(options); // Load is called with the parsed options
|
||||||
|
})
|
||||||
|
.WithNotParsed(errors => errors.Output());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[UnmanagedCallersOnly(EntryPoint = "get_game_controllers")]
|
[UnmanagedCallersOnly(EntryPoint = "get_game_controllers")]
|
||||||
public static unsafe IntPtr GetGamepadList()
|
public static unsafe IntPtr GetGamepadList()
|
||||||
{
|
{
|
||||||
@ -164,7 +170,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index, Options option)
|
||||||
{
|
{
|
||||||
if (inputId == null)
|
if (inputId == null)
|
||||||
{
|
{
|
||||||
@ -264,8 +270,9 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool isNintendoStyle = gamepadName.Contains("Nintendo");
|
bool isAppleController = gamepadName.Contains("Apple") ? option.OnScreenCorrespond : false;
|
||||||
|
bool isNintendoStyle = gamepadName.Contains("Nintendo") || isAppleController;
|
||||||
|
|
||||||
config = new StandardControllerInputConfig
|
config = new StandardControllerInputConfig
|
||||||
{
|
{
|
||||||
@ -461,9 +468,9 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_enableKeyboard = option.EnableKeyboard;
|
_enableKeyboard = option.EnableKeyboard;
|
||||||
_enableMouse = option.EnableMouse;
|
_enableMouse = option.EnableMouse;
|
||||||
|
|
||||||
static void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
|
static void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index, Options option)
|
||||||
{
|
{
|
||||||
InputConfig inputConfig = HandlePlayerConfiguration(inputProfileName, inputId, index);
|
InputConfig inputConfig = HandlePlayerConfiguration(inputProfileName, inputId, index, option);
|
||||||
|
|
||||||
if (inputConfig != null)
|
if (inputConfig != null)
|
||||||
{
|
{
|
||||||
@ -471,15 +478,15 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1);
|
LoadPlayerConfiguration(option.InputProfile1Name, option.InputId1, PlayerIndex.Player1, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2);
|
LoadPlayerConfiguration(option.InputProfile2Name, option.InputId2, PlayerIndex.Player2, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3);
|
LoadPlayerConfiguration(option.InputProfile3Name, option.InputId3, PlayerIndex.Player3, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4);
|
LoadPlayerConfiguration(option.InputProfile4Name, option.InputId4, PlayerIndex.Player4, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5);
|
LoadPlayerConfiguration(option.InputProfile5Name, option.InputId5, PlayerIndex.Player5, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6);
|
LoadPlayerConfiguration(option.InputProfile6Name, option.InputId6, PlayerIndex.Player6, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7);
|
LoadPlayerConfiguration(option.InputProfile7Name, option.InputId7, PlayerIndex.Player7, option);
|
||||||
LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8);
|
LoadPlayerConfiguration(option.InputProfile8Name, option.InputId8, PlayerIndex.Player8, option);
|
||||||
LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld);
|
LoadPlayerConfiguration(option.InputProfileHandheldName, option.InputIdHandheld, PlayerIndex.Handheld, option);
|
||||||
|
|
||||||
if (_inputConfiguration.Count == 0)
|
if (_inputConfiguration.Count == 0)
|
||||||
{
|
{
|
||||||
@ -627,7 +634,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
options.AudioVolume,
|
options.AudioVolume,
|
||||||
options.UseHypervisor ?? true,
|
options.UseHypervisor ?? true,
|
||||||
options.MultiplayerLanInterfaceId,
|
options.MultiplayerLanInterfaceId,
|
||||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled);
|
Common.Configuration.Multiplayer.MultiplayerMode.LdnMitm);
|
||||||
|
|
||||||
return new Switch(configuration);
|
return new Switch(configuration);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user