diff --git a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate index 7e812551b..df53ca7b5 100644 Binary files a/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate and b/src/MeloNX/MeloNX.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme b/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme index 327ff8d71..8788fb1fc 100644 --- a/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme +++ b/src/MeloNX/MeloNX.xcodeproj/xcshareddata/xcschemes/MeloNX.xcscheme @@ -63,7 +63,6 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift index 4e453773b..fc8ca1ae3 100644 --- a/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift +++ b/src/MeloNX/MeloNX/Core/Swift/Ryujinx.swift @@ -98,7 +98,9 @@ class Ryujinx { // Fixes the Stubs.DispatchLoop Crash args.append(contentsOf: ["--memory-manager-mode", "SoftwarePageTable"]) if config.fullscreen { - args.append(contentsOf: ["--fullscreen", String(config.fullscreen)]) + // args.append(contentsOf: ["--fullscreen", String(config.fullscreen)]) + args.append(contentsOf: ["--exclusive-fullscreen", String(config.fullscreen)]) + // exclusive-fullscreen } // Debug Logs args.append(contentsOf: ["--enable-debug-logs", String(config.debuglogs)]) diff --git a/src/MeloNX/MeloNX/Views/ContentView.swift b/src/MeloNX/MeloNX/Views/ContentView.swift index d5e386651..0c09b0f00 100644 --- a/src/MeloNX/MeloNX/Views/ContentView.swift +++ b/src/MeloNX/MeloNX/Views/ContentView.swift @@ -10,16 +10,19 @@ import SDL2 import GameController struct ContentView: View { + @State public var theWindow: UIWindow? = nil @State private var virtualController: GCVirtualController? @State var game: URL? = nil init() { // Initialize SDL - DispatchQueue.main.async { + DispatchQueue.main.async { [self] in SDL_SetMainReady() SDL_iPhoneSetEventPump(SDL_TRUE) - SDL_Init(SDL_INIT_VIDEO) + + // Apply the window patch early + patchMakeKeyAndVisible() } } @@ -31,8 +34,7 @@ struct ContentView: View { GCInputButtonA, GCInputButtonB, GCInputButtonX, - GCInputButtonY, - //GCInputDirectionPad + GCInputButtonY ] let controller = GCVirtualController(configuration: configuration) @@ -41,24 +43,66 @@ struct ContentView: View { } var body: some View { - - - if let game { - SDLViewRepresentable { - setupVirtualController() - - - let config = Ryujinx.Configuration(gamepath: game.path, debuglogs: true, tracelogs: true, listinputids: false, inputids: ["1-47150005-05ac-0000-0100-00004f066d01"], ryufullscreen: true) - // Starts the emulation - do { - try Ryujinx().start(with: config) - } catch { - print("Error \(error.localizedDescription)") - } + + SDLViewRepresentable { displayid in + start(displayid: displayid) } } else { GameListView(startemu: $game) } } + + func start(displayid: UInt32) { + setupVirtualController() + + if theWindow == nil { + // Ensure theWindow is set + theWindow = UIApplication.shared.windows.first + } + + let config = Ryujinx.Configuration( + gamepath: game!.path, + additionalArgs: [ + "--display-id", String(displayid) + ], + debuglogs: true, + tracelogs: true, + listinputids: false, + inputids: ["1-47150005-05ac-0000-0100-00004f066d01"], + ryufullscreen: true + ) + // Start the emulation + do { + try Ryujinx().start(with: config) + } catch { + print("Error \(error.localizedDescription)") + } + } + + 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) + } + } } + +extension UIWindow { + @objc func wdb_makeKeyAndVisible() { + print("Making window key and visible...") + + if #available(iOS 13.0, *) { + self.windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene + } + + self.wdb_makeKeyAndVisible() + + // Update ContentView's reference to this window instance + if let rootView = self.rootViewController as? UIHostingController { + rootView.rootView.theWindow = self + } + } +} + diff --git a/src/MeloNX/MeloNX/Views/SDLView/SDLView.swift b/src/MeloNX/MeloNX/Views/SDLView/SDLView.swift index a6544d35f..0db2193eb 100644 --- a/src/MeloNX/MeloNX/Views/SDLView/SDLView.swift +++ b/src/MeloNX/MeloNX/Views/SDLView/SDLView.swift @@ -11,61 +11,67 @@ import SDL2 class SDLView: UIView { var sdlwin: OpaquePointer? - private var sdlWindowID: UInt32 = 1 // Adjust this ID based on Ryujinx window ID override init(frame: CGRect) { super.init(frame: frame) - startSDLWindowRetrieval() + DispatchQueue.main.async { [self] in + makeSDLWindow() + } } required init?(coder: NSCoder) { super.init(coder: coder) - startSDLWindowRetrieval() + DispatchQueue.main.async { [self] in + makeSDLWindow() + } } - private func startSDLWindowRetrieval() { - Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in - self.makeSDLWindow() - - // Stop the timer once the window is successfully retrieved - if self.sdlwin != nil { - timer.invalidate() - } - } + func getWindowFlags() -> UInt32 { + return SDL_WINDOW_VULKAN.rawValue } private func makeSDLWindow() { - // Attempt to retrieve the SDL window created by Ryujinx - sdlwin = SDL_GetWindowFromID(sdlWindowID) + let width: Int32 = 1280 // Replace with the desired width + let height: Int32 = 720 // Replace with the desired height + let defaultFlags: UInt32 = SDL_WINDOW_SHOWN.rawValue + let fullscreenFlag: UInt32 = SDL_WINDOW_FULLSCREEN.rawValue // Or SDL_WINDOW_FULLSCREEN_DESKTOP if needed + + // Create the SDL window + sdlwin = SDL_CreateWindow( + "Ryujinx", + 0, + 0, + width, + height, + defaultFlags | fullscreenFlag | getWindowFlags() + ) + // Check if we successfully retrieved the SDL window guard sdlwin != nil else { - print("Error getting SDL window: \(String(cString: SDL_GetError()))") + print("Error creating SDL window: \(String(cString: SDL_GetError()))") return } - print("SDL window retrieved successfully.") + print("SDL window created successfully.") // Position SDL window over this UIView - DispatchQueue.main.async { - self.syncSDLWindowPosition() - } + self.syncSDLWindowPosition() } private func syncSDLWindowPosition() { guard let sdlwin = sdlwin else { return } - DispatchQueue.main.async { - - // Get the frame of the UIView in screen coordinates - let viewFrameInWindow = self.convert(self.bounds, to: nil) - - // Set the SDL window position and size to match the UIView frame - SDL_SetWindowPosition(sdlwin, Int32(viewFrameInWindow.origin.x), Int32(viewFrameInWindow.origin.y)) - SDL_SetWindowSize(sdlwin, Int32(viewFrameInWindow.width), Int32(viewFrameInWindow.height)) - - // Bring SDL window to the front - SDL_RaiseWindow(sdlwin) - } + + + // Get the frame of the UIView in screen coordinates + let viewFrameInWindow = self.convert(self.bounds, to: nil) + + // Set the SDL window position and size to match the UIView frame + SDL_SetWindowPosition(sdlwin, Int32(viewFrameInWindow.origin.x), Int32(viewFrameInWindow.origin.y)) + SDL_SetWindowSize(sdlwin, Int32(viewFrameInWindow.width), Int32(viewFrameInWindow.height)) + + // Bring SDL window to the front + SDL_RaiseWindow(sdlwin) print("SDL window positioned over SDLView.") } diff --git a/src/MeloNX/MeloNX/Views/SDLView/SDLViewRepresentable.swift b/src/MeloNX/MeloNX/Views/SDLView/SDLViewRepresentable.swift index a8b985693..ebfea2c68 100644 --- a/src/MeloNX/MeloNX/Views/SDLView/SDLViewRepresentable.swift +++ b/src/MeloNX/MeloNX/Views/SDLView/SDLViewRepresentable.swift @@ -7,13 +7,14 @@ import UIKit import SwiftUI +import SDL2 struct SDLViewRepresentable: UIViewRepresentable { - let configure: () -> Void + let configure: (UInt32) -> Void func makeUIView(context: Context) -> SDLView { // Configure (start ryu) before initialsing SDLView so SDLView can get the SDL_Window from Ryu - configure() let view = SDLView(frame: .zero) + configure(SDL_GetWindowID(view.sdlwin)) return view } diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 9603a86be..8b24f1b9d 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -185,7 +185,8 @@ namespace Ryujinx.Headless.SDL2 FullscreenFlag = SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP; } - WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | GetWindowFlags()); + WindowHandle = SDL_GetWindowFromID(1); + // WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | GetWindowFlags()); if (WindowHandle == IntPtr.Zero) {