diff --git a/src/ios/CMakeLists.txt b/src/ios/CMakeLists.txt index 6f7ad7e94f..2e98dc843f 100644 --- a/src/ios/CMakeLists.txt +++ b/src/ios/CMakeLists.txt @@ -20,16 +20,11 @@ add_executable(eden-ios EmulationGame.swift JoystickView.swift EmulationHandler.swift - NavView.swift PomeloApp.swift - SettingsView.swift FileManager.swift EmulationView.swift LibraryView.swift KeyboardHostingController.swift - MetalView.swift - ControllerView.swift - InfoView.swift FolderMonitor.swift GameButtonView.swift EmulationScreenView.swift diff --git a/src/ios/ControllerView.swift b/src/ios/ControllerView.swift deleted file mode 100644 index c4d5a8a6a7..0000000000 --- a/src/ios/ControllerView.swift +++ /dev/null @@ -1,432 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2024 Pomelo, Stossy11 -// SPDX-License-Identifier: GPL-3.0-or-later - -import SwiftUI -import GameController - -import SwiftUIJoystick - -struct ControllerView: View { - let appui = AppUI.shared - @State var isPressed = false - @State var controllerconnected = false - @State private var x: CGFloat = 0.0 - @State private var y: CGFloat = 0.0 - @Environment(\.presentationMode) var presentationMode - - var body: some View { - GeometryReader { geometry in - ZStack { - if !controllerconnected { - OnScreenController(geometry: geometry) // i did this to clean it up as it was quite long lmfao - } - } - } - .onAppear { - print("checking for controller:") - controllerconnected = false - DispatchQueue.main.async { - setupControllers() // i dont know what half of this shit does - } - } - } - - // Add a dictionary to track controller IDs - @State var controllerIDs: [GCController: Int] = [:] - - private func setupControllers() { - NotificationCenter.default.addObserver(forName: .GCControllerDidConnect, object: nil, queue: .main) { notification in - if let controller = notification.object as? GCController { - print("wow controller onstart") // yippeeee - self.setupController(controller) - self.controllerconnected = true - } else { - print("not GCController :((((((") // wahhhhhhh - } - } - - - NotificationCenter.default.addObserver(forName: .GCControllerDidDisconnect, object: nil, queue: .main) { notification in - if let controller = notification.object as? GCController { - print("wow controller gone") - if self.controllerIDs.isEmpty { - controllerconnected = false - } - self.controllerIDs.removeValue(forKey: controller) // Remove the controller ID - } - } - - GCController.controllers().forEach { controller in - print("wow controller") - self.controllerconnected = true - self.setupController(controller) - } - } - - private func setupController(_ controller: GCController) { - // Assign a unique ID to the controller, max 5 controllers - if controllerIDs.count < 6, controllerIDs[controller] == nil { - controllerIDs[controller] = controllerIDs.count - } - - guard let controllerId = controllerIDs[controller] else { return } - - if let extendedGamepad = controller.extendedGamepad { - - // Handle extended gamepad - extendedGamepad.dpad.up.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadUp, controllerId: controllerId) : self.touchUpInside(.directionalPadUp, controllerId: controllerId) - } - - extendedGamepad.dpad.down.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadDown, controllerId: controllerId) : self.touchUpInside(.directionalPadDown, controllerId: controllerId) - } - extendedGamepad.dpad.left.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadLeft, controllerId: controllerId) : self.touchUpInside(.directionalPadLeft, controllerId: controllerId) - } - extendedGamepad.dpad.right.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadRight, controllerId: controllerId) : self.touchUpInside(.directionalPadRight, controllerId: controllerId) - } - extendedGamepad.buttonOptions?.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.minus, controllerId: controllerId) : self.touchUpInside(.minus, controllerId: controllerId) - } - extendedGamepad.buttonMenu.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.plus, controllerId: controllerId) : self.touchUpInside(.plus, controllerId: controllerId) - } - extendedGamepad.buttonA.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.A, controllerId: controllerId) : self.touchUpInside(.A, controllerId: controllerId) - } - extendedGamepad.buttonB.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.B, controllerId: controllerId) : self.touchUpInside(.B, controllerId: controllerId) - } - extendedGamepad.buttonX.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.X, controllerId: controllerId) : self.touchUpInside(.X, controllerId: controllerId) - } - extendedGamepad.buttonY.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.Y, controllerId: controllerId) : self.touchUpInside(.Y, controllerId: controllerId) - } - extendedGamepad.leftShoulder.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.triggerL, controllerId: controllerId) : self.touchUpInside(.L, controllerId: controllerId) - } - extendedGamepad.leftTrigger.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.triggerZL, controllerId: controllerId) : self.touchUpInside(.triggerZL, controllerId: controllerId) - } - extendedGamepad.rightShoulder.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.triggerR, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) - } - extendedGamepad.leftThumbstickButton?.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.L, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) - } - extendedGamepad.rightThumbstickButton?.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.R, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) - } - extendedGamepad.rightTrigger.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.triggerZR, controllerId: controllerId) : self.touchUpInside(.triggerZR, controllerId: controllerId) - } - extendedGamepad.buttonHome?.pressedChangedHandler = { button, value, pressed in - if pressed { - appui.exit() - presentationMode.wrappedValue.dismiss() - } - } - extendedGamepad.leftThumbstick.valueChangedHandler = { dpad, x, y in - self.appui.thumbstickMoved(analog: .left, x: x, y: y, controllerid: controllerId) - } - - extendedGamepad.rightThumbstick.valueChangedHandler = { dpad, x, y in - self.appui.thumbstickMoved(analog: .right, x: x, y: y, controllerid: controllerId) - } - - if let motion = controller.motion { - var lastTimestamp = Date().timeIntervalSince1970 // Initialize timestamp when motion starts - - motion.valueChangedHandler = { motion in - // Get current time - let currentTimestamp = Date().timeIntervalSince1970 - let deltaTimestamp = Int32((currentTimestamp - lastTimestamp) * 1000) // Difference in milliseconds - - // Update last timestamp - lastTimestamp = currentTimestamp - - // Get gyroscope data - let gyroX = motion.rotationRate.x - let gyroY = motion.rotationRate.y - let gyroZ = motion.rotationRate.z - - // Get accelerometer data - let accelX = motion.gravity.x + motion.userAcceleration.x - let accelY = motion.gravity.y + motion.userAcceleration.y - let accelZ = motion.gravity.z + motion.userAcceleration.z - - print("\(gyroX), \(gyroY), \(gyroZ), \(accelX), \(accelY), \(accelZ)") - - // Call your gyroMoved function with the motion data - appui.gyroMoved(x: Float(gyroX), y: Float(gyroY), z: Float(gyroZ), accelX: Float(accelX), accelY: Float(accelY), accelZ: Float(accelZ), controllerId: Int32(controllerId), deltaTimestamp: Int32(lastTimestamp)) - } - } - } else if let microGamepad = controller.microGamepad { - // Handle micro gamepad - microGamepad.dpad.up.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadUp, controllerId: controllerId) : self.touchUpInside(.directionalPadUp, controllerId: controllerId) - } - microGamepad.dpad.down.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadDown, controllerId: controllerId) : self.touchUpInside(.directionalPadDown, controllerId: controllerId) - } - microGamepad.dpad.left.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadLeft, controllerId: controllerId) : self.touchUpInside(.directionalPadLeft, controllerId: controllerId) - } - microGamepad.dpad.right.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.directionalPadRight, controllerId: controllerId) : self.touchUpInside(.directionalPadRight, controllerId: controllerId) - } - microGamepad.buttonA.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.A, controllerId: controllerId) : self.touchUpInside(.A, controllerId: controllerId) - } - microGamepad.buttonX.pressedChangedHandler = { button, value, pressed in - pressed ? self.touchDown(.X, controllerId: controllerId) : self.touchUpInside(.X, controllerId: controllerId) - } - } - } - - private func touchDown(_ button: VirtualControllerButtonType, controllerId: Int) { - appui.virtualControllerButtonDown(button: button, controllerid: controllerId) } - - private func touchUpInside(_ button: VirtualControllerButtonType, controllerId: Int) { - appui.virtualControllerButtonUp(button: button, controllerid: controllerId) - } -} - -struct OnScreenController: View { - @State var geometry: GeometryProxy - var body: some View { - if geometry.size.height > geometry.size.width && UIDevice.current.userInterfaceIdiom != .pad { - // portrait - VStack { - Spacer() - VStack { - HStack { - VStack { - ShoulderButtonsViewLeft() - ZStack { - Joystick() - DPadView() - } - } - .padding() - VStack { - ShoulderButtonsViewRight() - ZStack { - Joystick(iscool: true) // hope this works - ABXYView() - } - } - .padding() - } - HStack { - ButtonView(button: .plus).padding(.horizontal, 40) - ButtonView(button: .minus).padding(.horizontal, 40) - } - } - .padding(.bottom, geometry.size.height / 3.2) // very broken - } - } else { - // could be landscape - VStack { - HStack { - Spacer() - ButtonView(button: .home) - .padding(.horizontal) - } - Spacer() - VStack { - HStack { - - // gotta fuckin add + and - now - VStack { - ShoulderButtonsViewLeft() - ZStack { - Joystick() - DPadView() - } - } - HStack { - Spacer() - VStack { - Spacer() - ButtonView(button: .plus) // Adding the + button - } - VStack { - Spacer() - ButtonView(button: .minus) // Adding the - button - } - Spacer() - } - VStack { - ShoulderButtonsViewRight() - ZStack { - Joystick(iscool: true) // hope this work s - ABXYView() - } - } - } - - } - .padding(.bottom, geometry.size.height / 11) // also extremally broken ( - } - } - } -} - -struct ShoulderButtonsViewLeft: View { - var body: some View { - HStack { - ButtonView(button: .triggerZL) - .padding(.horizontal) - ButtonView(button: .triggerL) - .padding(.horizontal) - } - .frame(width: 160, height: 20) - } -} - -struct ShoulderButtonsViewRight: View { - var body: some View { - HStack { - ButtonView(button: .triggerR) - .padding(.horizontal) - ButtonView(button: .triggerZR) - .padding(.horizontal) - } - .frame(width: 160, height: 20) - - } -} - -struct DPadView: View { - var body: some View { - VStack { - ButtonView(button: .directionalPadUp) - HStack { - ButtonView(button: .directionalPadLeft) - Spacer(minLength: 20) - ButtonView(button: .directionalPadRight) - } - ButtonView(button: .directionalPadDown) - .padding(.horizontal) - } - .frame(width: 145, height: 145) - } -} - -struct ABXYView: View { - var body: some View { - VStack { - ButtonView(button: .X) - HStack { - ButtonView(button: .Y) - Spacer(minLength: 20) - ButtonView(button: .A) - } - ButtonView(button: .B) - .padding(.horizontal) - } - .frame(width: 145, height: 145) - } -} - -class Haptics { - static let shared = Haptics() - private init() { } - func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) { - print("haptics") - UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred() - } - func notify(_ feedbackType: UINotificationFeedbackGenerator.FeedbackType) { - UINotificationFeedbackGenerator().notificationOccurred(feedbackType) - } -} - -struct ButtonView: View { - var button: VirtualControllerButtonType - @StateObject private var viewModel: EmulationViewModel = EmulationViewModel(game: nil) - let appui = AppUI.shared - @State var mtkView: MTKView? - @State var width: CGFloat = 45 - @State var height: CGFloat = 45 - @State var isPressed = false - var id: Int { - if onscreenjoy { - return 8 - } - return 0 - } - @AppStorage("onscreenhandheld") var onscreenjoy: Bool = false - @Environment(\.colorScheme) var colorScheme - @Environment(\.presentationMode) var presentationMode - - var body: some View { - Image(systemName: buttonText) - .resizable() - .frame(width: width, height: height) - .foregroundColor(colorScheme == .dark ? Color.gray : Color.gray) - .opacity(isPressed ? 0.5 : 1) - .gesture( - DragGesture(minimumDistance: 0) - .onChanged { _ in - if !self.isPressed { - self.isPressed = true - DispatchQueue.main.async { - if button == .home { - presentationMode.wrappedValue.dismiss() - appui.exit() - } else { - appui.virtualControllerButtonDown(button: button, controllerid: id) - Haptics.shared.play(.heavy) - } - } - } - } - .onEnded { _ in - self.isPressed = false - DispatchQueue.main.async { - if button != .home { - appui.virtualControllerButtonUp(button: button, controllerid: id) - } - } - } - ) - .onAppear() { - if button == .triggerL || button == .triggerZL || button == .triggerZR || button == .triggerR { - width = 65 - } - - - if button == .minus || button == .plus || button == .home { - width = 35 - height = 35 - } - } - } - - private var buttonText: String { - switch button { - case .A: return "a.circle.fill" - case .B: return "b.circle.fill" - case .X: return "x.circle.fill" - case .Y: return "y.circle.fill" - case .directionalPadUp: return "arrowtriangle.up.circle.fill" - case .directionalPadDown: return "arrowtriangle.down.circle.fill" - case .directionalPadLeft: return "arrowtriangle.left.circle.fill" - case .directionalPadRight: return "arrowtriangle.right.circle.fill" - case .triggerZL: return"zl.rectangle.roundedtop.fill" - case .triggerZR: return "zr.rectangle.roundedtop.fill" - case .triggerL: return "l.rectangle.roundedbottom.fill" - case .triggerR: return "r.rectangle.roundedbottom.fill" - case .plus: return "plus.circle.fill" - case .minus: return "minus.circle.fill" - case .home: return "house.circle.fill" - default: return "" - } - } -} diff --git a/src/ios/EmulationView.swift b/src/ios/EmulationView.swift index 354f1dfe33..2bb60e77f4 100644 --- a/src/ios/EmulationView.swift +++ b/src/ios/EmulationView.swift @@ -9,6 +9,430 @@ import Foundation import GameController import UIKit import SwiftUIIntrospect +import SwiftUIJoystick +import Metal + +struct MetalView: UIViewRepresentable { + let device: MTLDevice? + let configure: (UIView) -> Void + func makeUIView(context: Context) -> EmulationScreenView { + let view = EmulationScreenView() + configure(view.primaryScreen) + return view + } + func updateUIView(_ uiView: EmulationScreenView, context: Context) { + // + } +} + +struct ControllerView: View { + let appui = AppUI.shared + @State var isPressed = false + @State var controllerconnected = false + @State private var x: CGFloat = 0.0 + @State private var y: CGFloat = 0.0 + @Environment(\.presentationMode) var presentationMode + + var body: some View { + GeometryReader { geometry in + ZStack { + if !controllerconnected { + OnScreenController(geometry: geometry) // i did this to clean it up as it was quite long lmfao + } + } + } + .onAppear { + print("checking for controller:") + controllerconnected = false + DispatchQueue.main.async { + setupControllers() // i dont know what half of this shit does + } + } + } + + // Add a dictionary to track controller IDs + @State var controllerIDs: [GCController: Int] = [:] + + private func setupControllers() { + NotificationCenter.default.addObserver(forName: .GCControllerDidConnect, object: nil, queue: .main) { notification in + if let controller = notification.object as? GCController { + print("wow controller onstart") // yippeeee + self.setupController(controller) + self.controllerconnected = true + } else { + print("not GCController :((((((") // wahhhhhhh + } + } + + + NotificationCenter.default.addObserver(forName: .GCControllerDidDisconnect, object: nil, queue: .main) { notification in + if let controller = notification.object as? GCController { + print("wow controller gone") + if self.controllerIDs.isEmpty { + controllerconnected = false + } + self.controllerIDs.removeValue(forKey: controller) // Remove the controller ID + } + } + + GCController.controllers().forEach { controller in + print("wow controller") + self.controllerconnected = true + self.setupController(controller) + } + } + + private func setupController(_ controller: GCController) { + // Assign a unique ID to the controller, max 5 controllers + if controllerIDs.count < 6, controllerIDs[controller] == nil { + controllerIDs[controller] = controllerIDs.count + } + guard let controllerId = controllerIDs[controller] else { return } + if let extendedGamepad = controller.extendedGamepad { + // Handle extended gamepad + extendedGamepad.dpad.up.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadUp, controllerId: controllerId) : self.touchUpInside(.directionalPadUp, controllerId: controllerId) + } + extendedGamepad.dpad.down.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadDown, controllerId: controllerId) : self.touchUpInside(.directionalPadDown, controllerId: controllerId) + } + extendedGamepad.dpad.left.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadLeft, controllerId: controllerId) : self.touchUpInside(.directionalPadLeft, controllerId: controllerId) + } + extendedGamepad.dpad.right.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadRight, controllerId: controllerId) : self.touchUpInside(.directionalPadRight, controllerId: controllerId) + } + extendedGamepad.buttonOptions?.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.minus, controllerId: controllerId) : self.touchUpInside(.minus, controllerId: controllerId) + } + extendedGamepad.buttonMenu.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.plus, controllerId: controllerId) : self.touchUpInside(.plus, controllerId: controllerId) + } + extendedGamepad.buttonA.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.A, controllerId: controllerId) : self.touchUpInside(.A, controllerId: controllerId) + } + extendedGamepad.buttonB.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.B, controllerId: controllerId) : self.touchUpInside(.B, controllerId: controllerId) + } + extendedGamepad.buttonX.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.X, controllerId: controllerId) : self.touchUpInside(.X, controllerId: controllerId) + } + extendedGamepad.buttonY.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.Y, controllerId: controllerId) : self.touchUpInside(.Y, controllerId: controllerId) + } + extendedGamepad.leftShoulder.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.triggerL, controllerId: controllerId) : self.touchUpInside(.L, controllerId: controllerId) + } + extendedGamepad.leftTrigger.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.triggerZL, controllerId: controllerId) : self.touchUpInside(.triggerZL, controllerId: controllerId) + } + extendedGamepad.rightShoulder.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.triggerR, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) + } + extendedGamepad.leftThumbstickButton?.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.L, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) + } + extendedGamepad.rightThumbstickButton?.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.R, controllerId: controllerId) : self.touchUpInside(.triggerR, controllerId: controllerId) + } + extendedGamepad.rightTrigger.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.triggerZR, controllerId: controllerId) : self.touchUpInside(.triggerZR, controllerId: controllerId) + } + extendedGamepad.buttonHome?.pressedChangedHandler = { button, value, pressed in + if pressed { + appui.exit() + presentationMode.wrappedValue.dismiss() + } + } + extendedGamepad.leftThumbstick.valueChangedHandler = { dpad, x, y in + self.appui.thumbstickMoved(analog: .left, x: x, y: y, controllerid: controllerId) + } + extendedGamepad.rightThumbstick.valueChangedHandler = { dpad, x, y in + self.appui.thumbstickMoved(analog: .right, x: x, y: y, controllerid: controllerId) + } + if let motion = controller.motion { + var lastTimestamp = Date().timeIntervalSince1970 // Initialize timestamp when motion starts + motion.valueChangedHandler = { motion in + // Get current time + let currentTimestamp = Date().timeIntervalSince1970 + let deltaTimestamp = Int32((currentTimestamp - lastTimestamp) * 1000) // Difference in milliseconds + + // Update last timestamp + lastTimestamp = currentTimestamp + + // Get gyroscope data + let gyroX = motion.rotationRate.x + let gyroY = motion.rotationRate.y + let gyroZ = motion.rotationRate.z + + // Get accelerometer data + let accelX = motion.gravity.x + motion.userAcceleration.x + let accelY = motion.gravity.y + motion.userAcceleration.y + let accelZ = motion.gravity.z + motion.userAcceleration.z + + print("\(gyroX), \(gyroY), \(gyroZ), \(accelX), \(accelY), \(accelZ)") + + // Call your gyroMoved function with the motion data + appui.gyroMoved(x: Float(gyroX), y: Float(gyroY), z: Float(gyroZ), accelX: Float(accelX), accelY: Float(accelY), accelZ: Float(accelZ), controllerId: Int32(controllerId), deltaTimestamp: Int32(lastTimestamp)) + } + } + } else if let microGamepad = controller.microGamepad { + // Handle micro gamepad + microGamepad.dpad.up.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadUp, controllerId: controllerId) : self.touchUpInside(.directionalPadUp, controllerId: controllerId) + } + microGamepad.dpad.down.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadDown, controllerId: controllerId) : self.touchUpInside(.directionalPadDown, controllerId: controllerId) + } + microGamepad.dpad.left.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadLeft, controllerId: controllerId) : self.touchUpInside(.directionalPadLeft, controllerId: controllerId) + } + microGamepad.dpad.right.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.directionalPadRight, controllerId: controllerId) : self.touchUpInside(.directionalPadRight, controllerId: controllerId) + } + microGamepad.buttonA.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.A, controllerId: controllerId) : self.touchUpInside(.A, controllerId: controllerId) + } + microGamepad.buttonX.pressedChangedHandler = { button, value, pressed in + pressed ? self.touchDown(.X, controllerId: controllerId) : self.touchUpInside(.X, controllerId: controllerId) + } + } + } + + private func touchDown(_ button: VirtualControllerButtonType, controllerId: Int) { + appui.virtualControllerButtonDown(button: button, controllerid: controllerId) } + + private func touchUpInside(_ button: VirtualControllerButtonType, controllerId: Int) { + appui.virtualControllerButtonUp(button: button, controllerid: controllerId) + } +} + +struct OnScreenController: View { + @State var geometry: GeometryProxy + var body: some View { + if geometry.size.height > geometry.size.width && UIDevice.current.userInterfaceIdiom != .pad { + // portrait + VStack { + Spacer() + VStack { + HStack { + VStack { + ShoulderButtonsViewLeft() + ZStack { + Joystick() + DPadView() + } + } + .padding() + VStack { + ShoulderButtonsViewRight() + ZStack { + Joystick(iscool: true) // hope this works + ABXYView() + } + } + .padding() + } + HStack { + ButtonView(button: .plus).padding(.horizontal, 40) + ButtonView(button: .minus).padding(.horizontal, 40) + } + } + .padding(.bottom, geometry.size.height / 3.2) // very broken + } + } else { + // could be landscape + VStack { + HStack { + Spacer() + ButtonView(button: .home) + .padding(.horizontal) + } + Spacer() + VStack { + HStack { + + // gotta fuckin add + and - now + VStack { + ShoulderButtonsViewLeft() + ZStack { + Joystick() + DPadView() + } + } + HStack { + Spacer() + VStack { + Spacer() + ButtonView(button: .plus) // Adding the + button + } + VStack { + Spacer() + ButtonView(button: .minus) // Adding the - button + } + Spacer() + } + VStack { + ShoulderButtonsViewRight() + ZStack { + Joystick(iscool: true) // hope this work s + ABXYView() + } + } + } + } + .padding(.bottom, geometry.size.height / 11) // also extremally broken ( + } + } + } +} + +struct ShoulderButtonsViewLeft: View { + var body: some View { + HStack { + ButtonView(button: .triggerZL).padding(.horizontal) + ButtonView(button: .triggerL).padding(.horizontal) + } + .frame(width: 160, height: 20) + } +} + +struct ShoulderButtonsViewRight: View { + var body: some View { + HStack { + ButtonView(button: .triggerR).padding(.horizontal) + ButtonView(button: .triggerZR).padding(.horizontal) + } + .frame(width: 160, height: 20) + + } +} + +struct DPadView: View { + var body: some View { + VStack { + ButtonView(button: .directionalPadUp) + HStack { + ButtonView(button: .directionalPadLeft) + Spacer(minLength: 20) + ButtonView(button: .directionalPadRight) + } + ButtonView(button: .directionalPadDown).padding(.horizontal) + } + .frame(width: 145, height: 145) + } +} + +struct ABXYView: View { + var body: some View { + VStack { + ButtonView(button: .X) + HStack { + ButtonView(button: .Y) + Spacer(minLength: 20) + ButtonView(button: .A) + } + ButtonView(button: .B).padding(.horizontal) + } + .frame(width: 145, height: 145) + } +} + +class Haptics { + static let shared = Haptics() + private init() { } + func play(_ feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle) { + print("haptics") + UIImpactFeedbackGenerator(style: feedbackStyle).impactOccurred() + } + func notify(_ feedbackType: UINotificationFeedbackGenerator.FeedbackType) { + UINotificationFeedbackGenerator().notificationOccurred(feedbackType) + } +} + +struct ButtonView: View { + var button: VirtualControllerButtonType + @StateObject private var viewModel: EmulationViewModel = EmulationViewModel(game: nil) + let appui = AppUI.shared + @State var mtkView: MTKView? + @State var width: CGFloat = 45 + @State var height: CGFloat = 45 + @State var isPressed = false + var id: Int { + if onscreenjoy { + return 8 + } + return 0 + } + @AppStorage("onscreenhandheld") var onscreenjoy: Bool = false + @Environment(\.colorScheme) var colorScheme + @Environment(\.presentationMode) var presentationMode + + var body: some View { + Image(systemName: buttonText) + .resizable() + .frame(width: width, height: height) + .foregroundColor(colorScheme == .dark ? Color.gray : Color.gray) + .opacity(isPressed ? 0.5 : 1) + .gesture( + DragGesture(minimumDistance: 0) + .onChanged { _ in + if !self.isPressed { + self.isPressed = true + DispatchQueue.main.async { + if button == .home { + presentationMode.wrappedValue.dismiss() + appui.exit() + } else { + appui.virtualControllerButtonDown(button: button, controllerid: id) + Haptics.shared.play(.heavy) + } + } + } + } + .onEnded { _ in + self.isPressed = false + DispatchQueue.main.async { + if button != .home { + appui.virtualControllerButtonUp(button: button, controllerid: id) + } + } + } + ) + .onAppear() { + if button == .triggerL || button == .triggerZL || button == .triggerZR || button == .triggerR { + width = 65 + } + + + if button == .minus || button == .plus || button == .home { + width = 35 + height = 35 + } + } + } + + private var buttonText: String { + switch button { + case .A: return "a.circle.fill" + case .B: return "b.circle.fill" + case .X: return "x.circle.fill" + case .Y: return "y.circle.fill" + case .directionalPadUp: return "arrowtriangle.up.circle.fill" + case .directionalPadDown: return "arrowtriangle.down.circle.fill" + case .directionalPadLeft: return "arrowtriangle.left.circle.fill" + case .directionalPadRight: return "arrowtriangle.right.circle.fill" + case .triggerZL: return"zl.rectangle.roundedtop.fill" + case .triggerZR: return "zr.rectangle.roundedtop.fill" + case .triggerL: return "l.rectangle.roundedbottom.fill" + case .triggerR: return "r.rectangle.roundedbottom.fill" + case .plus: return "plus.circle.fill" + case .minus: return "minus.circle.fill" + case .home: return "house.circle.fill" + default: return "" + } + } +} struct EmulationView: View { @StateObject private var viewModel: EmulationViewModel diff --git a/src/ios/FileManager.swift b/src/ios/FileManager.swift index 5d0b317c56..2074376c77 100644 --- a/src/ios/FileManager.swift +++ b/src/ios/FileManager.swift @@ -6,7 +6,6 @@ import SwiftUI import Foundation import UIKit - import Zip struct Core : Comparable, Hashable { @@ -40,55 +39,6 @@ struct Core : Comparable, Hashable { } } -class YuzuFileManager { - static var shared = YuzuFileManager() - - func directories() -> [String : [String : String]] { - [ - "themes" : [:], - "amiibo" : [:], - "cache" : [:], - "config" : [:], - "crash_dumps" : [:], - "dump" : [:], - "keys" : [:], - "load" : [:], - "log" : [:], - "nand" : [:], - "play_time" : [:], - "roms" : [:], - "screenshots" : [:], - "sdmc" : [:], - "shader" : [:], - "tas" : [:], - "icons" : [:] - ] - } - - func createdirectories() throws { - let documentdir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - try directories().forEach() { directory, filename in - let directoryURL = documentdir.appendingPathComponent(directory) - - if !FileManager.default.fileExists(atPath: directoryURL.path) { - print("creating dir at \(directoryURL.path)") // yippee - try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: false, attributes: nil) - } - } - } - - func DetectKeys() -> (Bool, Bool) { - var prodkeys = false - var titlekeys = false - let filemanager = FileManager.default - let documentdir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - let KeysFolderURL = documentdir.appendingPathComponent("keys") - prodkeys = filemanager.fileExists(atPath: KeysFolderURL.appendingPathComponent("prod.keys").path) - titlekeys = filemanager.fileExists(atPath: KeysFolderURL.appendingPathComponent("title.keys").path) - return (prodkeys, titlekeys) - } -} - enum LibManError : Error { case ripenum, urlgobyebye } @@ -97,7 +47,6 @@ class LibraryManager { static let shared = LibraryManager() let documentdir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("roms", conformingTo: .folder) - func removerom(_ game: EmulationGame) throws { do { try FileManager.default.removeItem(at: game.fileURL) diff --git a/src/ios/GameButtonView.swift b/src/ios/GameButtonView.swift index c4b2b2537d..5b2a09f17c 100644 --- a/src/ios/GameButtonView.swift +++ b/src/ios/GameButtonView.swift @@ -9,6 +9,18 @@ import UIKit import UniformTypeIdentifiers import Combine +struct SettingsView: View { + @State var core: Core + @State var showprompt = false + + @AppStorage("icon") var iconused = 1 + var body: some View { + NavigationStack { + + } + } +} + struct GameIconView: View { var game: EmulationGame @Binding var selectedGame: EmulationGame? diff --git a/src/ios/GameListView.swift b/src/ios/GameListView.swift index a2bef69b9c..cdbd6adcfe 100644 --- a/src/ios/GameListView.swift +++ b/src/ios/GameListView.swift @@ -55,7 +55,6 @@ struct GameListView: View { guard let EmulationGame = game as? PoYuzume else { return false } return searchText.isEmpty || EmulationGame.title.localizedCaseInsensitiveContains(searchText) } - ScrollView { VStack { VStack(alignment: .leading) { diff --git a/src/ios/LibraryView.swift b/src/ios/LibraryView.swift index a2a3f418c6..cec8744b32 100644 --- a/src/ios/LibraryView.swift +++ b/src/ios/LibraryView.swift @@ -6,7 +6,6 @@ import SwiftUI import CryptoKit - struct LibraryView: View { @Binding var core: Core @State var isGridView: Bool = true diff --git a/src/ios/MetalView.swift b/src/ios/MetalView.swift deleted file mode 100644 index f0471a6ce6..0000000000 --- a/src/ios/MetalView.swift +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2024 Pomelo, Stossy11 -// SPDX-License-Identifier: GPL-3.0-or-later - -import SwiftUI -import Metal - - -struct MetalView: UIViewRepresentable { - let device: MTLDevice? - let configure: (UIView) -> Void - - func makeUIView(context: Context) -> EmulationScreenView { - let view = EmulationScreenView() - configure(view.primaryScreen) - return view - } - - func updateUIView(_ uiView: EmulationScreenView, context: Context) { - // - } -} diff --git a/src/ios/NavView.swift b/src/ios/NavView.swift deleted file mode 100644 index 3ce63c44a0..0000000000 --- a/src/ios/NavView.swift +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2024 Pomelo, Stossy11 -// SPDX-License-Identifier: GPL-3.0-or-later - -import SwiftUI - -struct BootOSView: View { - @Binding var core: Core - @Binding var currentnavigarion: Int - @State var appui = AppUI.shared - @AppStorage("cangetfullpath") var canGetFullPath: Bool = false - var body: some View { - if (appui.canGetFullPath() -- canGetFullPath) { - EmulationView(game: nil) - } else { - VStack { - Text("Unable Launch Switch OS") - .font(.largeTitle) - .padding() - Text("You do not have the Switch Home Menu Files Needed to launch the Ηome Menu") - } - } - } -} - -struct NavView: View { - @Binding var core: Core - @State private var selectedTab = 0 - var body: some View { - TabView(selection: $selectedTab) { - LibraryView(core: $core) - .tabItem { Label("Library", systemImage: "rectangle.on.rectangle") } - .tag(0) - BootOSView(core: $core, currentnavigarion: $selectedTab) - .toolbar(.hidden, for: .tabBar) - .tabItem { Label("Boot OS", systemImage: "house") } - .tag(1) - SettingsView(core: core) - .tabItem { Label("Settings", systemImage: "gear") } - .tag(2) - } - } -} diff --git a/src/ios/PomeloApp.swift b/src/ios/PomeloApp.swift index 2dbc2a9c5c..7f1516c1c1 100644 --- a/src/ios/PomeloApp.swift +++ b/src/ios/PomeloApp.swift @@ -4,6 +4,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later import SwiftUI +import Foundation +import UIKit +import Zip infix operator --: LogicalDisjunctionPrecedence @@ -11,10 +14,58 @@ func --(lhs: Bool, rhs: Bool) -> Bool { return lhs || rhs } +class YuzuFileManager { + static var shared = YuzuFileManager() + func directories() -> [String : [String : String]] { + [ + "themes" : [:], + "amiibo" : [:], + "cache" : [:], + "config" : [:], + "crash_dumps" : [:], + "dump" : [:], + "keys" : [:], + "load" : [:], + "log" : [:], + "nand" : [:], + "play_time" : [:], + "roms" : [:], + "screenshots" : [:], + "sdmc" : [:], + "shader" : [:], + "tas" : [:], + "icons" : [:] + ] + } + + func createdirectories() throws { + let documentdir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + try directories().forEach() { directory, filename in + let directoryURL = documentdir.appendingPathComponent(directory) + + if !FileManager.default.fileExists(atPath: directoryURL.path) { + print("creating dir at \(directoryURL.path)") // yippee + try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: false, attributes: nil) + } + } + } +} + struct ContentView: View { @State var core = Core(games: [], root: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]) var body: some View { HomeView(core: core).onAppear() { + do { + try YuzuFileManager.shared.createdirectories() // this took a while to create the proper directories + do { + core = try LibraryManager.shared.library() // this shit is like you tried to throw a egg into a blender with no lid on + } catch { + print("Failed to fetch library: \(error)") // aaaaaaaaa + } + } catch { + print("Failed to create directories: \(error)") // i wonder why hmmmmmmm + return + } } } } diff --git a/src/ios/SettingsView.swift b/src/ios/SettingsView.swift deleted file mode 100644 index dca90e7574..0000000000 --- a/src/ios/SettingsView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: Copyright 2024 Pomelo, Stossy11 -// SPDX-License-Identifier: GPL-3.0-or-later - -import SwiftUI - -struct SettingsView: View { - @State var core: Core - @State var showprompt = false - - @AppStorage("icon") var iconused = 1 - var body: some View { - NavigationStack { - - } - } -}