forked from MeloNX/MeloNX
Add Airplay
This commit is contained in:
parent
f57d24706b
commit
2d5f1d8015
Binary file not shown.
@ -57,6 +57,7 @@ class Ryujinx {
|
||||
@Published var controllerMap: [Controller] = []
|
||||
@Published var metalLayer: CAMetalLayer? = nil
|
||||
@Published var firmwareversion = "0"
|
||||
@Published var emulationUIView = UIView()
|
||||
|
||||
var shouldMetal: Bool {
|
||||
metalLayer == nil
|
||||
|
@ -75,7 +75,7 @@ struct ContentView: View {
|
||||
// MARK: - Body
|
||||
var body: some View {
|
||||
if game != nil, quits == false {
|
||||
if Ryujinx.shared.metalLayer == nil {
|
||||
if isLoading {
|
||||
emulationView
|
||||
.onAppear() {
|
||||
// This is fro the old exiting game feature that didn't work properly. will look into it and figure out a better alternative
|
||||
@ -187,8 +187,10 @@ struct ContentView: View {
|
||||
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
|
||||
if get_current_fps() != 0 {
|
||||
isLoading = false
|
||||
if Ryujinx.shared.metalLayer != nil {
|
||||
withAnimation {
|
||||
isLoading = false
|
||||
}
|
||||
isAnimating = false
|
||||
timer.invalidate()
|
||||
}
|
||||
@ -217,6 +219,11 @@ struct ContentView: View {
|
||||
refreshControllersList()
|
||||
}
|
||||
|
||||
Air.play(AnyView(
|
||||
Text("Select Game")
|
||||
.font(.system(size: 100))
|
||||
|
||||
))
|
||||
|
||||
let isJIT = UserDefaults.standard.bool(forKey: "JIT-ENABLED")
|
||||
|
||||
|
143
src/MeloNX/MeloNX/App/Views/Emulation/AirPlay/Air.swift
Normal file
143
src/MeloNX/MeloNX/App/Views/Emulation/AirPlay/Air.swift
Normal file
@ -0,0 +1,143 @@
|
||||
// Credit https://github.com/heestand-xyz/AirKit
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
public class Air {
|
||||
|
||||
static let shared = Air()
|
||||
|
||||
public var connected: Bool = false {
|
||||
didSet {
|
||||
connectionCallbacks.forEach({ $0(connected) })
|
||||
}
|
||||
}
|
||||
var connectionCallbacks: [(Bool) -> ()] = []
|
||||
|
||||
var airScreen: UIScreen?
|
||||
var airWindow: UIWindow?
|
||||
|
||||
var hostingController: UIHostingController<AnyView>?
|
||||
|
||||
var appIsActive: Bool { UIApplication.shared.applicationState == .active }
|
||||
|
||||
init() {
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didConnect),
|
||||
name: UIScreen.didConnectNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didDisconnect),
|
||||
name: UIScreen.didDisconnectNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive),
|
||||
name: UIApplication.didBecomeActiveNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(willResignActive),
|
||||
name: UIApplication.willResignActiveNotification, object: nil)
|
||||
}
|
||||
|
||||
private func check() {
|
||||
if let connectedScreen = UIScreen.screens.first(where: { $0 != .main }) {
|
||||
add(screen: connectedScreen) { success in
|
||||
guard success else { return }
|
||||
self.connected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func play(_ view: AnyView) {
|
||||
Air.shared.hostingController = UIHostingController<AnyView>(rootView: view)
|
||||
Air.shared.check()
|
||||
}
|
||||
|
||||
public static func stop() {
|
||||
Air.shared.remove()
|
||||
Air.shared.hostingController = nil
|
||||
}
|
||||
|
||||
public static func connection(_ callback: @escaping (Bool) -> ()) {
|
||||
Air.shared.connectionCallbacks.append(callback)
|
||||
}
|
||||
|
||||
@objc func didConnect(sender: NSNotification) {
|
||||
print("AirKit - Connect")
|
||||
self.connected = true
|
||||
guard let screen: UIScreen = sender.object as? UIScreen else { return }
|
||||
add(screen: screen) { success in
|
||||
guard success else { return }
|
||||
self.connected = true
|
||||
}
|
||||
}
|
||||
|
||||
func add(screen: UIScreen, completion: @escaping (Bool) -> ()) {
|
||||
|
||||
print("AirKit - Add Screen")
|
||||
|
||||
airScreen = screen
|
||||
|
||||
airWindow = UIWindow(frame: airScreen!.bounds)
|
||||
|
||||
guard let viewController: UIViewController = hostingController else {
|
||||
print("AirKit - Add - Failed: Hosting Controller Not Found")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
findWindowScene(for: airScreen!) { windowScene in
|
||||
guard let airWindowScene: UIWindowScene = windowScene else {
|
||||
print("AirKit - Add - Failed: Window Scene Not Found")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
self.airWindow?.rootViewController = viewController
|
||||
self.airWindow?.windowScene = airWindowScene
|
||||
self.airWindow?.isHidden = false
|
||||
print("AirKit - Add Screen - Done")
|
||||
completion(true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func findWindowScene(for screen: UIScreen, shouldRecurse: Bool = true, completion: @escaping (UIWindowScene?) -> ()) {
|
||||
print("AirKit - Find Window Scene")
|
||||
var matchingWindowScene: UIWindowScene? = nil
|
||||
let scenes = UIApplication.shared.connectedScenes
|
||||
for scene in scenes {
|
||||
if let windowScene = scene as? UIWindowScene {
|
||||
if windowScene.screen == screen {
|
||||
matchingWindowScene = windowScene
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let windowScene: UIWindowScene = matchingWindowScene else {
|
||||
DispatchQueue.main.async {
|
||||
self.findWindowScene(for: screen, shouldRecurse: false) { windowScene in
|
||||
completion(windowScene)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
completion(windowScene)
|
||||
}
|
||||
|
||||
@objc func didDisconnect() {
|
||||
print("AirKit - Disconnect")
|
||||
remove()
|
||||
connected = false
|
||||
}
|
||||
|
||||
func remove() {
|
||||
print("AirKit - Remove")
|
||||
airWindow = nil
|
||||
airScreen = nil
|
||||
}
|
||||
|
||||
@objc func didBecomeActive() {
|
||||
print("AirKit - App Active")
|
||||
}
|
||||
|
||||
@objc func willResignActive() {
|
||||
print("AirKit - App Inactive")
|
||||
|
||||
}
|
||||
|
||||
}
|
13
src/MeloNX/MeloNX/App/Views/Emulation/AirPlay/AirPlay.swift
Normal file
13
src/MeloNX/MeloNX/App/Views/Emulation/AirPlay/AirPlay.swift
Normal file
@ -0,0 +1,13 @@
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
public extension View {
|
||||
|
||||
func airPlay() -> some View {
|
||||
print("AirKit - airPlay")
|
||||
Air.play(AnyView(self))
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,11 +10,19 @@ import SwiftUI
|
||||
// Emulation View
|
||||
struct EmulationView: View {
|
||||
@AppStorage("isVirtualController") var isVCA: Bool = true
|
||||
@State var isAirplaying = Air.shared.connected
|
||||
var body: some View {
|
||||
ZStack {
|
||||
MetalView() // The Emulation View
|
||||
.ignoresSafeArea()
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
if isAirplaying {
|
||||
Text("")
|
||||
.onAppear {
|
||||
Air.play(AnyView(MetalView().ignoresSafeArea()))
|
||||
}
|
||||
} else {
|
||||
MetalView() // The Emulation View
|
||||
.ignoresSafeArea()
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
}
|
||||
|
||||
// Above Emulation View
|
||||
|
||||
@ -22,5 +30,13 @@ struct EmulationView: View {
|
||||
ControllerView() // Virtual Controller
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Air.shared.connectionCallbacks.append { cool in
|
||||
DispatchQueue.main.async {
|
||||
isAirplaying = cool
|
||||
print(cool)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,15 @@ import MetalKit
|
||||
struct MetalView: UIViewRepresentable {
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = UIView()
|
||||
|
||||
let metalLayer = Ryujinx.shared.metalLayer!
|
||||
metalLayer.frame = view.bounds
|
||||
view.contentScaleFactor = metalLayer.contentsScale // Right size and Fix Touch :3
|
||||
view.layer.addSublayer(metalLayer)
|
||||
metalLayer.frame = Ryujinx.shared.emulationUIView.bounds
|
||||
Ryujinx.shared.emulationUIView.contentScaleFactor = metalLayer.contentsScale // Right size and Fix Touch :3
|
||||
if !Ryujinx.shared.emulationUIView.subviews.contains(where: { $0 == metalLayer }) {
|
||||
Ryujinx.shared.emulationUIView.layer.addSublayer(metalLayer)
|
||||
}
|
||||
|
||||
return view
|
||||
return Ryujinx.shared.emulationUIView
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user