[iOS] New iOS 13 project to support Swift; Custom keyboard and touch mouse support (#13435)

* Support for Swift, added emulator keyboard

* fixed toggle key handling using magic number hack for now

* fixed keyboard transparency slider for now with suboptimal fix; add gesture recognizer to hide/show keyboard

* Support CocoaView extensions in Swift; move keyboard delegate impl and setup to swift extension

* moved keyboard view model creation out of EmulatorKeyboard

* implement key pressed delegate in swift extension

* added input method for directly sending RETROK_* codes to support a touchscreen keyboard; assign keyboard model delegates; updated keyboard layout (added F1-F12 keys); change shift, control and alt keys to be modifiers

* enable focus mode when custom keyboard is shown; enable/disable overlay when custom keyboard is toggled

* Specify -DHAVE_OPENGLES2 instead of -DHAVE_OPENGLES3 since glsym_es3.h does not compile in iOS 14.5
Fix tvOS build using compiler flags

* Create new project for iOS 13 deploy target; add check for deploy target to conditionally compile for new iOS 13 specific feature (custom keyboard)

* force disable core info caching for iOS, use opengl es2 for debug

* Add flag for iOS custom keyboard - 3-finger swipe up to show, 3-finger swipe down to hide

* use OpenGLES2 instead; using ES3 results in compile time errors on iOS 14.5

* code cleanup

* Updated references to -DDONT_WANT_ARM_ASM_OPTIMIZATIONS flag

* Add JIT support for non-jailbroken devices

* iOS: Add support for touch mouse handler

* Added a HAVE_IOS_TOUCHMOUSE preprocessor macro so that it builds under the iOS11_Metal xcode project

* Changed click-and-drag behavior to double tap hold and drag

* Visual improvements to the emulator keyboard: updated colors, improved key-press effect
This commit is contained in:
yoshisuga 2022-02-21 21:21:34 -10:00 committed by GitHub
parent 3d54d952a5
commit bc02f8319e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3889 additions and 19 deletions

View File

@ -41,6 +41,20 @@ float cocoa_screen_get_backing_scale_factor(void);
#endif
static bool apple_key_state[MAX_KEYS];
// Send keyboard inputs directly using RETROK_* codes
// Used by the iOS custom keyboard implementation
void apple_direct_input_keyboard_event(bool down,
unsigned code, uint32_t character, uint32_t mod, unsigned device)
{
int appleKey = rarch_keysym_lut[code];
apple_key_state[appleKey] = down;
input_keyboard_event(down,
code,
character, (enum retro_mod)mod, device);
}
#if TARGET_OS_IPHONE
/* TODO/FIXME - static globals */
static bool small_keyboard_active = false;
@ -433,8 +447,12 @@ static int16_t cocoa_input_state(
return apple->window_pos_x * cocoa_screen_get_backing_scale_factor();
#endif
}
#ifdef IOS
val = apple->mouse_rel_x;
#else
val = apple->window_pos_x - apple->mouse_x_last;
apple->mouse_x_last = apple->window_pos_x;
#endif
return val;
case RETRO_DEVICE_ID_MOUSE_Y:
if (device == RARCH_DEVICE_MOUSE_SCREEN)
@ -445,8 +463,12 @@ static int16_t cocoa_input_state(
return apple->window_pos_y * cocoa_screen_get_backing_scale_factor();
#endif
}
#ifdef IOS
val = apple->mouse_rel_y;
#else
val = apple->window_pos_y - apple->mouse_y_last;
apple->mouse_y_last = apple->window_pos_y;
#endif
return val;
case RETRO_DEVICE_ID_MOUSE_LEFT:
return apple->mouse_buttons & 1;

View File

@ -169,6 +169,9 @@ RETRO_BEGIN_DECLS
void apple_input_keyboard_event(bool down,
unsigned code, uint32_t character, uint32_t mod, unsigned device);
void apple_direct_input_keyboard_event(bool down,
unsigned code, uint32_t character, uint32_t mod, unsigned device);
RETRO_END_DECLS
#endif

View File

@ -2487,6 +2487,7 @@ static int action_ok_playlist_entry_collection(const char *path,
}
else
{
#ifndef IOS
core_info = playlist_entry_get_core_info(entry);
if (core_info && !string_is_empty(core_info->path))
@ -2498,6 +2499,10 @@ static int action_ok_playlist_entry_collection(const char *path,
strlcpy(core_path, entry->core_path, sizeof(core_path));
playlist_resolve_path(PLAYLIST_LOAD, true, core_path, sizeof(core_path));
}
#else
strlcpy(core_path, entry->core_path, sizeof(core_path));
playlist_resolve_path(PLAYLIST_LOAD, true, core_path, sizeof(core_path));
#endif
}
}

View File

@ -0,0 +1,233 @@
//
// CocoaView+KeyboardSupport.swift
// RetroArchiOS11
//
// Created by Yoshi Sugawara on 2/25/21.
// Copyright © 2021 RetroArch. All rights reserved.
//
import Foundation
extension CocoaView {
var leftKeyboardModel: EmulatorKeyboardViewModel {
return EmulatorKeyboardViewModel(keys: [
[
EmulatorKeyboardKey(label: "1", code: Int(RETROK_1.rawValue)),
EmulatorKeyboardKey(label: "2", code: Int(RETROK_2.rawValue)),
EmulatorKeyboardKey(label: "3", code: Int(RETROK_3.rawValue)),
EmulatorKeyboardKey(label: "4", code: Int(RETROK_4.rawValue)),
EmulatorKeyboardKey(label: "5", code: Int(RETROK_5.rawValue)),
],
[
EmulatorKeyboardKey(label: "q", code: Int(RETROK_q.rawValue)),
EmulatorKeyboardKey(label: "w", code: Int(RETROK_w.rawValue)),
EmulatorKeyboardKey(label: "e", code: Int(RETROK_e.rawValue)),
EmulatorKeyboardKey(label: "r", code: Int(RETROK_r.rawValue)),
EmulatorKeyboardKey(label: "t", code: Int(RETROK_t.rawValue)),
],
[
EmulatorKeyboardKey(label: "a", code: Int(RETROK_a.rawValue)),
EmulatorKeyboardKey(label: "s", code: Int(RETROK_s.rawValue)),
EmulatorKeyboardKey(label: "d", code: Int(RETROK_d.rawValue)),
EmulatorKeyboardKey(label: "f", code: Int(RETROK_f.rawValue)),
EmulatorKeyboardKey(label: "g", code: Int(RETROK_g.rawValue)),
],
[
EmulatorKeyboardKey(label: "z", code: Int(RETROK_z.rawValue)),
EmulatorKeyboardKey(label: "x", code: Int(RETROK_x.rawValue)),
EmulatorKeyboardKey(label: "c", code: Int(RETROK_c.rawValue)),
EmulatorKeyboardKey(label: "v", code: Int(RETROK_v.rawValue)),
EmulatorKeyboardKey(label: "b", code: Int(RETROK_b.rawValue)),
],
[
EmulatorKeyboardKey(label: "SHIFT", code: Int(RETROK_LSHIFT.rawValue), keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
EmulatorKeyboardKey(label: "Fn", code: 9000, keySize: .standard, imageName: "fn"),
EmulatorKeyboardKey(label: "CTRL", code: Int(RETROK_LCTRL.rawValue), isModifier: true, imageName: "control"),
EmulatorKeyboardKey(label: "Space", code: Int(RETROK_SPACE.rawValue), keySize: .wide)
]
],
alternateKeys: [
[
EmulatorKeyboardKey(label: "ESC", code: Int(RETROK_ESCAPE.rawValue), imageName: "escape"),
SliderKey(keySize: .standard)
],
[
EmulatorKeyboardKey(label: "F1", code: Int(RETROK_F1.rawValue)),
EmulatorKeyboardKey(label: "F2", code: Int(RETROK_F2.rawValue)),
EmulatorKeyboardKey(label: "F3", code: Int(RETROK_F3.rawValue)),
EmulatorKeyboardKey(label: "F4", code: Int(RETROK_F4.rawValue)),
EmulatorKeyboardKey(label: "F5", code: Int(RETROK_F5.rawValue)),
],
[
EmulatorKeyboardKey(label: "-", code: Int(RETROK_MINUS.rawValue)),
EmulatorKeyboardKey(label: "=", code: Int(RETROK_EQUALS.rawValue)),
EmulatorKeyboardKey(label: "/", code: Int(RETROK_SLASH.rawValue)),
EmulatorKeyboardKey(label: "[", code: Int(RETROK_LEFTBRACKET.rawValue)),
EmulatorKeyboardKey(label: "]", code: Int(RETROK_RIGHTBRACKET.rawValue)),
],
[
EmulatorKeyboardKey(label: ";", code: Int(RETROK_SEMICOLON.rawValue)),
EmulatorKeyboardKey(label: "~", code: Int(RETROK_TILDE.rawValue)),
EmulatorKeyboardKey(label: ":", code: Int(RETROK_COLON.rawValue)),
EmulatorKeyboardKey(label: "?", code: Int(RETROK_QUESTION.rawValue)),
EmulatorKeyboardKey(label: "!", code: Int(RETROK_EXCLAIM.rawValue)),
],
[
EmulatorKeyboardKey(label: "SHIFT", code: Int(RETROK_LSHIFT.rawValue), keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
EmulatorKeyboardKey(label: "Fn", code: 9000, keySize: .standard, imageName: "fn"),
EmulatorKeyboardKey(label: "CTRL", code: Int(RETROK_LCTRL.rawValue), isModifier: true, imageName: "control"),
EmulatorKeyboardKey(label: "Space", code: Int(RETROK_SPACE.rawValue), keySize: .wide)
]
])
}
var rightKeyboardModel: EmulatorKeyboardViewModel {
EmulatorKeyboardViewModel(keys: [
[
EmulatorKeyboardKey(label: "6", code: Int(RETROK_6.rawValue)),
EmulatorKeyboardKey(label: "7", code: Int(RETROK_7.rawValue)),
EmulatorKeyboardKey(label: "8", code: Int(RETROK_8.rawValue)),
EmulatorKeyboardKey(label: "9", code: Int(RETROK_9.rawValue)),
EmulatorKeyboardKey(label: "0", code: Int(RETROK_0.rawValue))
],
[
EmulatorKeyboardKey(label: "y", code: Int(RETROK_y.rawValue)),
EmulatorKeyboardKey(label: "u", code: Int(RETROK_u.rawValue)),
EmulatorKeyboardKey(label: "i", code: Int(RETROK_i.rawValue)),
EmulatorKeyboardKey(label: "o", code: Int(RETROK_o.rawValue)),
EmulatorKeyboardKey(label: "p", code: Int(RETROK_p.rawValue)),
],
[
EmulatorKeyboardKey(label: "h", code: Int(RETROK_h.rawValue)),
EmulatorKeyboardKey(label: "j", code: Int(RETROK_j.rawValue)),
EmulatorKeyboardKey(label: "k", code: Int(RETROK_k.rawValue)),
EmulatorKeyboardKey(label: "l", code: Int(RETROK_l.rawValue)),
EmulatorKeyboardKey(label: "'", code: Int(RETROK_QUOTE.rawValue))
],
[
EmulatorKeyboardKey(label: "n", code: Int(RETROK_n.rawValue)),
EmulatorKeyboardKey(label: "m", code: Int(RETROK_m.rawValue)),
EmulatorKeyboardKey(label: ",", code: Int(RETROK_COMMA.rawValue)),
EmulatorKeyboardKey(label: ".", code: Int(RETROK_PERIOD.rawValue)),
EmulatorKeyboardKey(label: "BKSPC", code: Int(RETROK_BACKSPACE.rawValue), imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
],
[
EmulatorKeyboardKey(label: "Alt", code: Int(RETROK_LALT.rawValue), isModifier: true, imageName: "alt"),
EmulatorKeyboardKey(label: "tab", code: Int(RETROK_TAB.rawValue), imageName: "arrow.right.to.line"),
EmulatorKeyboardKey(label: "RETURN", code: Int(RETROK_RETURN.rawValue), keySize: .wide)
],
],
alternateKeys: [
[
EmulatorKeyboardKey(label: "F6", code: Int(RETROK_F6.rawValue)),
EmulatorKeyboardKey(label: "F7", code: Int(RETROK_F7.rawValue)),
EmulatorKeyboardKey(label: "F8", code: Int(RETROK_F8.rawValue)),
EmulatorKeyboardKey(label: "F9", code: Int(RETROK_F9.rawValue)),
EmulatorKeyboardKey(label: "F10", code: Int(RETROK_F10.rawValue)),
],
[
EmulatorKeyboardKey(label: "PAGEUP", code: Int(RETROK_PAGEUP.rawValue), imageName: "arrow.up.doc"),
EmulatorKeyboardKey(label: "HOME", code: Int(RETROK_HOME.rawValue), imageName: "house"),
EmulatorKeyboardKey(label: "INS", code: Int(RETROK_INSERT.rawValue), imageName: "text.insert"),
EmulatorKeyboardKey(label: "END", code: Int(RETROK_END.rawValue)),
EmulatorKeyboardKey(label: "PAGEDWN", code: Int(RETROK_PAGEDOWN.rawValue), imageName: "arrow.down.doc"),
],
[
EmulatorKeyboardKey(label: "F11", code: Int(RETROK_F11.rawValue)),
EmulatorKeyboardKey(label: "⬆️", code: Int(RETROK_UP.rawValue), imageName: "arrow.up"),
SpacerKey(),
SpacerKey(),
EmulatorKeyboardKey(label: "F12", code: Int(RETROK_F12.rawValue)),
],
[
EmulatorKeyboardKey(label: "⬅️", code: Int(RETROK_LEFT.rawValue), imageName: "arrow.left"),
EmulatorKeyboardKey(label: "⬇️", code: Int(RETROK_DOWN.rawValue), imageName: "arrow.down"),
EmulatorKeyboardKey(label: "➡️", code: Int(RETROK_RIGHT.rawValue), imageName: "arrow.right"),
SpacerKey(),
EmulatorKeyboardKey(label: "DEL", code: Int(RETROK_DELETE.rawValue), imageName: "clear", imageNameHighlighted: "clear.fill"),
],
[
EmulatorKeyboardKey(label: "RETURN", code: Int(RETROK_RETURN.rawValue), keySize: .wide)
]
])
}
@objc func setupEmulatorKeyboard() {
keyboardController = EmulatorKeyboardController(leftKeyboardModel: leftKeyboardModel, rightKeyboardModel: rightKeyboardModel)
keyboardController.leftKeyboardModel.delegate = self;
keyboardController.rightKeyboardModel.delegate = self;
addChild(keyboardController)
keyboardController.didMove(toParent: self)
keyboardController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(keyboardController.view)
keyboardController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
keyboardController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
keyboardController.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
keyboardController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
keyboardController.leftKeyboardModel.delegate = self
keyboardController.rightKeyboardModel.delegate = self
keyboardController.leftKeyboardModel.modifierDelegate = self
keyboardController.rightKeyboardModel.modifierDelegate = self
keyboardController.view.isHidden = true
keyboardModifierState = 0
}
}
extension CocoaView: EmulatorKeyboardKeyPressedDelegate {
func keyUp(_ key: KeyCoded) {
print("keyUp: code=\(key.keyCode) keyboardModifierState = \(keyboardModifierState)")
apple_direct_input_keyboard_event(false, UInt32(key.keyCode), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
}
func keyDown(_ key: KeyCoded) {
print("keyDown: code=\(key.keyCode) keyboardModifierState = \(keyboardModifierState)")
apple_direct_input_keyboard_event(true, UInt32(key.keyCode), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
}
}
extension CocoaView: EmulatorKeyboardModifierPressedDelegate {
func modifierPressedWithKey(_ key: KeyCoded, enable: Bool) {
switch UInt32(key.keyCode) {
case RETROK_LSHIFT.rawValue:
if enable {
keyboardModifierState |= RETROKMOD_SHIFT.rawValue
apple_direct_input_keyboard_event(true, UInt32(RETROK_LSHIFT.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
} else {
keyboardModifierState &= ~RETROKMOD_SHIFT.rawValue
apple_direct_input_keyboard_event(false, UInt32(RETROK_LSHIFT.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
}
case RETROK_LCTRL.rawValue:
if enable {
keyboardModifierState |= RETROKMOD_CTRL.rawValue
apple_direct_input_keyboard_event(true, UInt32(RETROK_LCTRL.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
} else {
keyboardModifierState &= ~RETROKMOD_CTRL.rawValue
apple_direct_input_keyboard_event(false, UInt32(RETROK_LCTRL.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
}
case RETROK_LALT.rawValue:
if enable {
keyboardModifierState |= RETROKMOD_ALT.rawValue
apple_direct_input_keyboard_event(true, UInt32(RETROK_LALT.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
} else {
keyboardModifierState &= ~RETROKMOD_ALT.rawValue
apple_direct_input_keyboard_event(false, UInt32(RETROK_LALT.rawValue), 0, keyboardModifierState, UInt32(RETRO_DEVICE_KEYBOARD))
}
default:
break
}
}
func isModifierEnabled(key: KeyCoded) -> Bool {
switch UInt32(key.keyCode) {
case RETROK_LSHIFT.rawValue:
return (keyboardModifierState & RETROKMOD_SHIFT.rawValue) != 0
case RETROK_LCTRL.rawValue:
return (keyboardModifierState & RETROKMOD_CTRL.rawValue) != 0
case RETROK_LALT.rawValue:
return (keyboardModifierState & RETROKMOD_ALT.rawValue) != 0
default:
break
}
return false
}
}

View File

@ -0,0 +1,601 @@
//
// EmulatorKeyboard.swift
//
// Created by Yoshi Sugawara on 7/30/20.
//
// TODO: shift key should change the label of the keys to uppercase (need callback mechanism?)
// pan gesture to outer edges of keyboard view for better dragging
import Foundation
import UIKit
class KeyboardButton: UIButton {
let key: KeyCoded
var toggleState = false
// MARK: - Functions
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let newArea = CGRect(
x: self.bounds.origin.x - 5.0,
y: self.bounds.origin.y - 5.0,
width: self.bounds.size.width + 20.0,
height: self.bounds.size.height + 20.0
)
return newArea.contains(point)
}
private func updateColors() {
backgroundColor = isHighlighted ? EmulatorKeyboardView.keyPressedBackgroundColor : isSelected ? EmulatorKeyboardView.keySelectedBackgroundColor : EmulatorKeyboardView.keyNormalBackgroundColor
layer.borderColor = (isHighlighted ? EmulatorKeyboardView.keyPressedBorderColor : isSelected ? EmulatorKeyboardView.keySelectedBorderColor : EmulatorKeyboardView.keyNormalBorderColor).cgColor
titleLabel?.textColor = isHighlighted ? EmulatorKeyboardView.keyPressedTextColor : isSelected ? EmulatorKeyboardView.keySelectedTextColor : EmulatorKeyboardView.keyNormalTextColor
titleLabel?.tintColor = titleLabel?.textColor
}
override open var isHighlighted: Bool {
didSet {
updateColors()
}
}
override open var isSelected: Bool {
didSet {
updateColors()
}
}
required init(key: KeyCoded) {
self.key = key
super.init(frame: .zero)
updateColors()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@objc protocol EmulatorKeyboardKeyPressedDelegate: AnyObject {
func keyDown(_ key: KeyCoded)
func keyUp(_ key: KeyCoded)
}
@objc protocol EmulatorKeyboardModifierPressedDelegate: AnyObject {
func modifierPressedWithKey(_ key: KeyCoded, enable: Bool)
func isModifierEnabled(key: KeyCoded) -> Bool
}
protocol EmulatorKeyboardViewDelegate: AnyObject {
func toggleAlternateKeys()
func refreshModifierStates()
func updateTransparency(toAlpha alpha: Float)
}
class EmulatorKeyboardView: UIView {
static var keyboardBackgroundColor = UIColor.systemGray6.withAlphaComponent(0.5)
static var keyboardCornerRadius = 6.0
static var keyboardDragColor = UIColor.systemGray
static var keyCornerRadius = 6.0
static var keyBorderWidth = 1.0
static var rowSpacing = 12.0
static var keySpacing = 8.0
static var keyNormalFont = UIFont.systemFont(ofSize: 12)
static var keyPressedFont = UIFont.boldSystemFont(ofSize: 24)
static var keyNormalBackgroundColor = UIColor.systemGray4.withAlphaComponent(0.5)
static var keyNormalBorderColor = keyNormalBackgroundColor
static var keyNormalTextColor = UIColor.label
static var keyPressedBackgroundColor = UIColor.systemGray2
static var keyPressedBorderColor = keyPressedBackgroundColor
static var keyPressedTextColor = UIColor.label
static var keySelectedBackgroundColor = UIColor.systemGray2.withAlphaComponent(0.8)
static var keySelectedBorderColor = keySelectedBackgroundColor
static var keySelectedTextColor = UIColor.label
var viewModel = EmulatorKeyboardViewModel(keys: [[KeyCoded]]()) {
didSet {
setupWithModel(viewModel)
}
}
var modifierButtons = Set<KeyboardButton>()
weak var delegate: EmulatorKeyboardViewDelegate?
private lazy var keyRowsStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .equalCentering
stackView.spacing = Self.rowSpacing
return stackView
}()
private lazy var alternateKeyRowsStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .equalCentering
stackView.spacing = Self.rowSpacing
stackView.isHidden = true
return stackView
}()
let dragMeView: UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = EmulatorKeyboardView.keyboardDragColor
view.translatesAutoresizingMaskIntoConstraints = false
view.widthAnchor.constraint(equalToConstant: 80).isActive = true
view.heightAnchor.constraint(equalToConstant: 2).isActive = true
let outerView = UIView(frame: .zero)
outerView.backgroundColor = .clear
outerView.translatesAutoresizingMaskIntoConstraints = false
outerView.addSubview(view)
view.centerXAnchor.constraint(equalTo: outerView.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: outerView.centerYAnchor).isActive = true
outerView.heightAnchor.constraint(equalToConstant: 20).isActive = true
outerView.widthAnchor.constraint(equalToConstant: 100).isActive = true
return outerView
}()
private var pressedKeyViews = [UIControl: UIView]()
convenience init() {
self.init(frame: CGRect.zero)
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
backgroundColor = Self.keyboardBackgroundColor
layer.cornerRadius = Self.keyboardCornerRadius
layoutMargins = UIEdgeInsets(top: 16, left: 4, bottom: 16, right: 4)
insetsLayoutMarginsFromSafeArea = false
addSubview(keyRowsStackView)
keyRowsStackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
keyRowsStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 4.0).isActive = true
keyRowsStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: -4.0).isActive = true
addSubview(alternateKeyRowsStackView)
alternateKeyRowsStackView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
alternateKeyRowsStackView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor, constant: 4.0).isActive = true
alternateKeyRowsStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: -4.0).isActive = true
addSubview(dragMeView)
dragMeView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
dragMeView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
@objc private func keyPressed(_ sender: KeyboardButton) {
if sender.key.keyCode == 9000 { // hack for now
return
}
if !sender.key.isModifier {
// make a "stand-in" for our key, and scale up key
let view = UIView()
view.backgroundColor = EmulatorKeyboardView.keyPressedBackgroundColor
view.layer.cornerRadius = EmulatorKeyboardView.keyCornerRadius
view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
view.frame = sender.convert(sender.bounds, to: self)
addSubview(view)
var tx = 0.0
let ty = sender.bounds.height * -1.20
if let window = self.window {
let rect = sender.convert(sender.bounds, to:window)
if rect.maxX > window.bounds.width * 0.9 {
tx = sender.bounds.width * -0.5
}
if rect.minX < window.bounds.width * 0.1 {
tx = sender.bounds.width * 0.5
}
}
sender.superview!.bringSubviewToFront(sender)
sender.transform = CGAffineTransform(translationX:tx, y:ty).scaledBy(x:2, y:2)
pressedKeyViews[sender] = view
}
viewModel.keyPressed(sender.key)
}
@objc private func keyCancelled(_ sender: KeyboardButton) {
sender.transform = .identity
if let view = pressedKeyViews[sender] {
view.removeFromSuperview()
pressedKeyViews.removeValue(forKey: sender)
}
}
@objc private func keyReleased(_ sender: KeyboardButton) {
sender.transform = .identity
if sender.key.keyCode == 9000 {
delegate?.toggleAlternateKeys()
return
}
if let view = pressedKeyViews[sender] {
view.removeFromSuperview()
pressedKeyViews.removeValue(forKey: sender)
}
sender.isSelected = viewModel.modifierKeyToggleStateForKey(sender.key)
viewModel.keyReleased(sender.key)
self.delegate?.refreshModifierStates()
}
func setupWithModel(_ model: EmulatorKeyboardViewModel) {
for row in model.keys {
let keysInRow = createKeyRow(keys: row)
keyRowsStackView.addArrangedSubview(keysInRow)
}
if let altKeys = model.alternateKeys {
for row in altKeys {
let keysInRow = createKeyRow(keys: row)
alternateKeyRowsStackView.addArrangedSubview(keysInRow)
}
}
if !model.isDraggable {
dragMeView.isHidden = true
}
}
func toggleKeysStackView() {
if viewModel.alternateKeys != nil {
keyRowsStackView.isHidden.toggle()
alternateKeyRowsStackView.isHidden.toggle()
refreshModifierStates()
}
}
func refreshModifierStates() {
modifierButtons.forEach{ button in
button.isSelected = viewModel.modifierKeyToggleStateForKey(button.key)
}
}
private func createKey(_ keyCoded: KeyCoded) -> UIButton {
let key = KeyboardButton(key: keyCoded)
if let imageName = keyCoded.keyImageName {
key.tintColor = EmulatorKeyboardView.keyNormalTextColor
key.setImage(UIImage(systemName: imageName), for: .normal)
if let highlightedImageName = keyCoded.keyImageNameHighlighted {
key.setImage(UIImage(systemName: highlightedImageName), for: .highlighted)
key.setImage(UIImage(systemName: highlightedImageName), for: .selected)
}
} else {
key.setTitle(keyCoded.keyLabel, for: .normal)
key.titleLabel?.font = EmulatorKeyboardView.keyNormalFont
key.setTitleColor(EmulatorKeyboardView.keyNormalTextColor, for: .normal)
key.setTitleColor(EmulatorKeyboardView.keySelectedTextColor, for: .selected)
key.setTitleColor(EmulatorKeyboardView.keyPressedTextColor, for: .highlighted)
}
key.translatesAutoresizingMaskIntoConstraints = false
key.widthAnchor.constraint(equalToConstant: (25 * CGFloat(keyCoded.keySize.rawValue))).isActive = true
key.heightAnchor.constraint(equalToConstant: 35).isActive = true
key.backgroundColor = EmulatorKeyboardView.keyNormalBackgroundColor
key.layer.borderWidth = EmulatorKeyboardView.keyBorderWidth
key.layer.borderColor = EmulatorKeyboardView.keyNormalBorderColor.cgColor
key.layer.cornerRadius = EmulatorKeyboardView.keyCornerRadius
key.addTarget(self, action: #selector(keyPressed(_:)), for: .touchDown)
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpInside)
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpOutside)
key.addTarget(self, action: #selector(keyCancelled(_:)), for: .touchCancel)
if keyCoded.isModifier {
modifierButtons.update(with: key)
}
return key
}
private func createKeyRow(keys: [KeyCoded]) -> UIStackView {
let subviews: [UIView] = keys.enumerated().map { index, keyCoded -> UIView in
if keyCoded is SpacerKey {
let spacer = UIView()
spacer.widthAnchor.constraint(equalToConstant: 25.0 * CGFloat(keyCoded.keySize.rawValue)).isActive = true
spacer.heightAnchor.constraint(equalToConstant: 25.0).isActive = true
return spacer
} else if let sliderKey = keyCoded as? SliderKey {
sliderKey.keyboardView = self
return sliderKey.createView()
}
return createKey(keyCoded)
}
let stack = UIStackView(arrangedSubviews: subviews)
stack.axis = .horizontal
stack.distribution = .fill
stack.spacing = 8
return stack
}
}
@objc enum KeySize: Int {
case standard = 1, wide, wider
}
// represents a key that has an underlying code that gets sent to the emulator
@objc protocol KeyCoded: AnyObject {
var keyLabel: String { get }
var keyImageName: String? { get }
var keyImageNameHighlighted: String? { get }
var keyCode: Int { get }
var keySize: KeySize { get }
var isModifier: Bool { get }
}
protocol KeyRowsDataSource {
func keyForPositionAt(_ position: KeyPosition) -> KeyCoded?
}
@objc class EmulatorKeyboardKey: NSObject, KeyCoded {
let keyLabel: String
var keyImageName: String?
var keyImageNameHighlighted: String?
let keyCode: Int
let keySize: KeySize
let isModifier: Bool
override var description: String {
return String(format: "\(keyLabel) (%02X)", keyCode)
}
init(label: String, code: Int, keySize: KeySize = .standard, isModifier: Bool = false, imageName: String? = nil, imageNameHighlighted: String? = nil) {
self.keyLabel = label
self.keyCode = code
self.keySize = keySize
self.isModifier = isModifier
self.keyImageName = imageName
self.keyImageNameHighlighted = imageNameHighlighted
}
}
class SpacerKey: KeyCoded {
let keyLabel = ""
let keyCode = 0
let keySize: KeySize
let isModifier = false
let keyImageName: String? = nil
let keyImageNameHighlighted: String? = nil
init(keySize: KeySize = .standard) {
self.keySize = keySize
}
}
class SliderKey: KeyCoded {
let keyLabel = ""
let keyCode = 0
let keySize: KeySize
let isModifier = false
let keyImageName: String? = nil
let keyImageNameHighlighted: String? = nil
weak var keyboardView: EmulatorKeyboardView?
init(keySize: KeySize = .standard) {
self.keySize = keySize
}
func createView() -> UIView {
let slider = UISlider(frame: .zero)
slider.minimumValue = 0.1
slider.maximumValue = 1.0
slider.addTarget(self, action: #selector(adjustKeyboardAlpha(_:)), for: .valueChanged)
slider.value = 1.0
let size = CGSize(width:EmulatorKeyboardView.keyNormalFont.pointSize, height:EmulatorKeyboardView.keyNormalFont.pointSize)
slider.setThumbImage(UIImage.dot(size:size, color:EmulatorKeyboardView.keyNormalTextColor), for: .normal)
return slider
}
@objc func adjustKeyboardAlpha(_ sender: UISlider) {
keyboardView?.delegate?.updateTransparency(toAlpha: sender.value)
}
}
struct KeyPosition {
let row: Int
let column: Int
}
@objc class EmulatorKeyboardViewModel: NSObject, KeyRowsDataSource {
var keys = [[KeyCoded]]()
var alternateKeys: [[KeyCoded]]?
var modifiers: [Int16: KeyCoded]?
var isDraggable = true
@objc weak var delegate: EmulatorKeyboardKeyPressedDelegate?
@objc weak var modifierDelegate: EmulatorKeyboardModifierPressedDelegate?
init(keys: [[KeyCoded]], alternateKeys: [[KeyCoded]]? = nil) {
self.keys = keys
self.alternateKeys = alternateKeys
}
func createView() -> EmulatorKeyboardView {
let view = EmulatorKeyboardView()
view.viewModel = self
return view
}
func keyForPositionAt(_ position: KeyPosition) -> KeyCoded? {
guard position.row < keys.count else {
return nil
}
let row = keys[position.row]
guard position.column < row.count else {
return nil
}
return row[position.column]
}
func modifierKeyToggleStateForKey(_ key: KeyCoded) -> Bool {
return key.isModifier && (modifierDelegate?.isModifierEnabled(key: key) ?? false)
}
func keyPressed(_ key: KeyCoded) {
if key.isModifier {
let isPressed = modifierDelegate?.isModifierEnabled(key: key) ?? false
modifierDelegate?.modifierPressedWithKey(key, enable: !isPressed)
return
}
delegate?.keyDown(key)
}
func keyReleased(_ key: KeyCoded) {
if key.isModifier {
return
}
delegate?.keyUp(key)
}
// KeyCoded can support a shifted key label
// view can update with shifted key labels?
// cluster can support alternate keys and view can swap them out?
}
@objc class EmulatorKeyboardController: UIViewController {
class EmulatorKeyboardPassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if hitView == self {
return nil
}
return hitView
}
}
@objc let leftKeyboardModel: EmulatorKeyboardViewModel
@objc let rightKeyboardModel: EmulatorKeyboardViewModel
@objc lazy var leftKeyboardView: EmulatorKeyboardView = {
let view = leftKeyboardModel.createView()
view.delegate = self
return view
}()
@objc lazy var rightKeyboardView: EmulatorKeyboardView = {
let view = rightKeyboardModel.createView()
view.delegate = self
return view
}()
var keyboardConstraints = [NSLayoutConstraint]()
init(leftKeyboardModel: EmulatorKeyboardViewModel, rightKeyboardModel: EmulatorKeyboardViewModel) {
self.leftKeyboardModel = leftKeyboardModel
self.rightKeyboardModel = rightKeyboardModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = EmulatorKeyboardPassthroughView()
}
override func viewDidLoad() {
super.viewDidLoad()
setupView()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
leftKeyboardView.dragMeView.isUserInteractionEnabled = true
leftKeyboardView.dragMeView.addGestureRecognizer(panGesture)
let panGestureRightKeyboard = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
rightKeyboardView.dragMeView.isUserInteractionEnabled = true
rightKeyboardView.dragMeView.addGestureRecognizer(panGestureRightKeyboard)
}
func setupView() {
NSLayoutConstraint.deactivate(keyboardConstraints)
keyboardConstraints.removeAll()
leftKeyboardView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(leftKeyboardView)
leftKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
leftKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
keyboardConstraints.append(contentsOf: [
leftKeyboardView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
leftKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
rightKeyboardView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(rightKeyboardView)
keyboardConstraints.append(contentsOf: [
rightKeyboardView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
rightKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
rightKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
rightKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
NSLayoutConstraint.activate(keyboardConstraints)
}
func setupViewFrames() {
// initial placement on the bottom corners
// since we don't know the frame of this view yet until layout time,
// assume it's taking the full screen
let screenFrame = UIScreen.main.bounds
let keyboardHeight: CGFloat = 250.0
let keyboardWidth: CGFloat = 180.0
let bottomLeftFrame = CGRect(
x: 0,
y: screenFrame.size.height - 40 - keyboardHeight - 20,
width: keyboardWidth, height: keyboardHeight)
let bottomRightFrame = CGRect(
x: screenFrame.size.width - 20 - keyboardWidth,
y:screenFrame.size.height - 40 - keyboardHeight - 20,
width: keyboardWidth, height: keyboardHeight
)
view.addSubview(leftKeyboardView)
view.addSubview(rightKeyboardView)
leftKeyboardView.frame = bottomLeftFrame
rightKeyboardView.frame = bottomRightFrame
}
func setupKeyModels() {
leftKeyboardView.setupWithModel(leftKeyboardModel)
rightKeyboardView.setupWithModel(rightKeyboardModel)
}
@objc func draggedView(_ sender:UIPanGestureRecognizer){
guard let keyboardView = sender.view?.superview else {
return
}
let translation = sender.translation(in: self.view)
keyboardView.center = CGPoint(x: keyboardView.center.x + translation.x, y: keyboardView.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: self.view)
}
}
extension EmulatorKeyboardController: EmulatorKeyboardViewDelegate {
func toggleAlternateKeys() {
for keyboard in [leftKeyboardView, rightKeyboardView] {
keyboard.toggleKeysStackView()
}
}
func refreshModifierStates() {
for keyboard in [leftKeyboardView, rightKeyboardView] {
keyboard.refreshModifierStates()
}
}
func updateTransparency(toAlpha alpha: Float) {
for keyboard in [leftKeyboardView, rightKeyboardView] {
keyboard.alpha = CGFloat(alpha)
}
}
}
private extension UIImage {
static func dot(size:CGSize, color:UIColor) -> UIImage {
return UIGraphicsImageRenderer(size: size).image { context in
context.cgContext.setFillColor(color.cgColor)
context.cgContext.fillEllipse(in: CGRect(origin:.zero, size:size))
}
}
}

View File

@ -0,0 +1,175 @@
//
// EmulatorTouchMouse.swift
// RetroArchiOS
//
// Created by Yoshi Sugawara on 12/27/21.
// Copyright © 2021 RetroArch. All rights reserved.
//
/**
Touch mouse behavior:
- Mouse movement: Pan finger around screen
- Left click: Tap with one finger
- Right click: Tap with two fingers (or hold with one finger and tap with another)
- Click-and-drag: Double tap and hold for 1 second, then pan finger around screen to drag mouse
Code adapted from iDOS/dospad: https://github.com/litchie/dospad
*/
import Combine
import UIKit
@objc protocol EmulatorTouchMouseHandlerDelegate: AnyObject {
func handleMouseClick(isLeftClick: Bool, isPressed: Bool)
func handleMouseMove(x: CGFloat, y: CGFloat)
}
@objcMembers public class EmulatorTouchMouseHandler: NSObject {
enum MouseHoldState {
case notHeld, wait, held
}
struct MouseClick {
var isRightClick = false
var isPressed = false
}
struct TouchInfo {
let touch: UITouch
let origin: CGPoint
let holdState: MouseHoldState
}
let view: UIView
weak var delegate: EmulatorTouchMouseHandlerDelegate?
private let positionChangeThreshold: CGFloat = 20.0
private let mouseHoldInterval: TimeInterval = 1.0
private var pendingMouseEvents = [MouseClick]()
private var mouseEventPublisher: AnyPublisher<MouseClick, Never> {
mouseEventSubject.eraseToAnyPublisher()
}
private let mouseEventSubject = PassthroughSubject<MouseClick, Never>()
private var subscription: AnyCancellable?
private var primaryTouch: TouchInfo?
private var secondaryTouch: TouchInfo?
private let mediumHaptic = UIImpactFeedbackGenerator(style: .medium)
public init(view: UIView) {
self.view = view
super.init()
setup()
}
private func setup() {
subscription = mouseEventPublisher
.sink(receiveValue: {[weak self] value in
self?.pendingMouseEvents.append(value)
self?.processMouseEvents()
})
}
private func processMouseEvents() {
guard let event = pendingMouseEvents.first else {
return
}
delegate?.handleMouseClick(isLeftClick: !event.isRightClick, isPressed: event.isPressed)
if event.isPressed {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.mouseEventSubject.send(MouseClick(isRightClick: event.isRightClick, isPressed: false))
}
}
pendingMouseEvents.removeFirst()
processMouseEvents()
}
@objc private func beginHold() {
guard let primaryTouch = primaryTouch, primaryTouch.holdState == .wait else {
return
}
self.primaryTouch = TouchInfo(touch: primaryTouch.touch, origin: primaryTouch.origin, holdState: .held)
mediumHaptic.impactOccurred()
delegate?.handleMouseClick(isLeftClick: true, isPressed: true)
}
private func endHold() {
guard let primaryTouch = primaryTouch else { return }
if primaryTouch.holdState == .notHeld {
return
}
if primaryTouch.holdState == .wait {
Thread.cancelPreviousPerformRequests(withTarget: self, selector: #selector(beginHold), object: self)
} else {
delegate?.handleMouseClick(isLeftClick: true, isPressed: false)
}
self.primaryTouch = TouchInfo(touch: primaryTouch.touch, origin: primaryTouch.origin, holdState: .notHeld)
}
public func touchesBegan(touches: Set<UITouch>) {
guard let touch = touches.first else {
return
}
if primaryTouch == nil {
primaryTouch = TouchInfo(touch: touch, origin: touch.location(in: view), holdState: .wait)
if touch.tapCount == 2 {
self.perform(#selector(beginHold), with: nil, afterDelay: mouseHoldInterval)
}
} else if secondaryTouch == nil {
secondaryTouch = TouchInfo(touch: touch, origin: touch.location(in: view), holdState: .notHeld)
}
}
public func touchesEnded(touches: Set<UITouch>) {
for touch in touches {
if touch == primaryTouch?.touch {
if touch.tapCount > 0 {
for _ in 1...touch.tapCount {
mouseEventSubject.send(MouseClick(isRightClick: false, isPressed: true))
}
}
endHold()
primaryTouch = nil
secondaryTouch = nil
} else if touch == secondaryTouch?.touch {
if touch.tapCount > 0 {
mouseEventSubject.send(MouseClick(isRightClick: true, isPressed: true))
endHold()
}
secondaryTouch = nil
}
}
delegate?.handleMouseMove(x: 0, y: 0)
}
public func touchesMoved(touches: Set<UITouch>) {
for touch in touches {
if touch == primaryTouch?.touch {
let a = touch.previousLocation(in: view)
let b = touch.location(in: view)
if primaryTouch?.holdState == .wait && (distanceBetween(pointA: a, pointB: b) > positionChangeThreshold) {
endHold()
}
delegate?.handleMouseMove(x: b.x-a.x, y: b.y-a.y)
}
}
}
public func touchesCancelled(touches: Set<UITouch>) {
for touch in touches {
if touch == primaryTouch?.touch {
endHold()
}
}
primaryTouch = nil
secondaryTouch = nil
}
func distanceBetween(pointA: CGPoint, pointB: CGPoint) -> CGFloat {
let dx = pointA.x - pointB.x
let dy = pointA.y - pointB.y
return sqrt(dx*dx*dy*dy)
}
}

17
pkg/apple/JITSupport.h Normal file
View File

@ -0,0 +1,17 @@
//
// JITSupport.h
// RetroArchiOS
//
// Created by Yoshi Sugawara on 9/25/21.
// Copyright © 2021 RetroArch. All rights reserved.
//
#ifndef JITSupport_h
#define JITSupport_h
#include <stdbool.h>
static bool jb_has_debugger_attached(void);
bool jb_enable_ptrace_hack(void);
#endif /* JITSupport_h */

76
pkg/apple/JITSupport.m Normal file
View File

@ -0,0 +1,76 @@
//
// JITSupport.m
// RetroArchiOS
//
// Created by Yoshi Sugawara on 9/25/21.
// Copyright © 2021 RetroArch. All rights reserved.
//
// Copied from UTMApp, original author: osy
//
#import <Foundation/Foundation.h>
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/getsect.h>
#include <pthread.h>
extern int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
extern boolean_t exc_server(mach_msg_header_t *, mach_msg_header_t *);
extern int ptrace(int request, pid_t pid, caddr_t addr, int data);
#define CS_OPS_STATUS 0 /* return status */
#define CS_KILL 0x00000200 /* kill process if it becomes invalid */
#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
#define PT_TRACE_ME 0 /* child declares it's being traced */
#define PT_SIGEXC 12 /* signals as exceptions for current_proc */
static void *exception_handler(void *argument) {
mach_port_t port = *(mach_port_t *)argument;
mach_msg_server(exc_server, 2048, port, 0);
return NULL;
}
static bool jb_has_debugger_attached(void) {
int flags;
return !csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) && flags & CS_DEBUGGED;
}
bool jb_enable_ptrace_hack(void) {
bool debugged = jb_has_debugger_attached();
// Thanks to this comment: https://news.ycombinator.com/item?id=18431524
// We use this hack to allow mmap with PROT_EXEC (which usually requires the
// dynamic-codesigning entitlement) by tricking the process into thinking
// that Xcode is debugging it. We abuse the fact that JIT is needed to
// debug the process.
if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
return false;
}
// ptracing ourselves confuses the kernel and will cause bad things to
// happen to the system (hangs) if an exception or signal occurs. Setup
// some "safety nets" so we can cause the process to exit in a somewhat sane
// state. We only need to do this if the debugger isn't attached. (It'll do
// this itself, and if we do it we'll interfere with its normal operation
// anyways.)
if (!debugged) {
// First, ensure that signals are delivered as Mach software exceptions
ptrace(PT_SIGEXC, 0, NULL, 0);
// then ensure that this exception goes through our exception handler.
// I think it's OK to just watch for EXC_SOFTWARE because the other
// exceptions (e.g. EXC_BAD_ACCESS, EXC_BAD_INSTRUCTION, and friends)
// will end up being delivered as signals anyways, and we can get them
// once they're resent as a software exception.
mach_port_t port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_SOFTWARE, port, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
pthread_t thread;
pthread_create(&thread, NULL, exception_handler, (void *)&port);
}
return true;
}

View File

@ -0,0 +1,16 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#ifndef HAVE_COCOATOUCH
#define HAVE_COCOATOUCH
#endif
#ifndef HAVE_IOS_CUSTOMKEYBOARD
#define HAVE_IOS_CUSTOMKEYBOARD
#endif
#include "libretro-common/include/libretro.h"
#import "../ui/drivers/cocoa/cocoa_common.h"
#include "../../input/drivers_keyboard/keyboard_event_apple.h"
#include "../../input/input_keymaps.h"

View File

@ -80,6 +80,7 @@
92CC05C321FE3C6D00FF79F0 /* WebServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 92CC05C121FE3C6D00FF79F0 /* WebServer.m */; };
92CC05C521FEDC9F00FF79F0 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92CC05C421FEDC9F00FF79F0 /* CFNetwork.framework */; };
92CC05C721FEDD0B00FF79F0 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92CC05C621FEDD0B00FF79F0 /* MobileCoreServices.framework */; };
92DAF342277EC52900FE2A9E /* JITSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 92DAF341277EC52900FE2A9E /* JITSupport.m */; };
92E5DCD4231A5786006491BF /* modules in Resources */ = {isa = PBXBuildFile; fileRef = 92E5DCD3231A5786006491BF /* modules */; };
/* End PBXBuildFile section */
@ -332,6 +333,8 @@
92CC05C121FE3C6D00FF79F0 /* WebServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebServer.m; sourceTree = "<group>"; };
92CC05C421FEDC9F00FF79F0 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
92CC05C621FEDD0B00FF79F0 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
92DAF340277EC52900FE2A9E /* JITSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITSupport.h; sourceTree = "<group>"; };
92DAF341277EC52900FE2A9E /* JITSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JITSupport.m; sourceTree = "<group>"; };
92E5DCD3231A5786006491BF /* modules */ = {isa = PBXFileReference; lastKnownFileType = folder; path = modules; sourceTree = "<group>"; };
96366C5416C9AC3300D64A22 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
96366C5816C9ACF500D64A22 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
@ -410,6 +413,8 @@
children = (
92B9EC9324E0536200E6CFB2 /* core */,
92B9EAE524E0518600E6CFB2 /* libretro-common */,
92DAF340277EC52900FE2A9E /* JITSupport.h */,
92DAF341277EC52900FE2A9E /* JITSupport.m */,
);
name = Sources;
sourceTree = "<group>";
@ -1102,7 +1107,7 @@
ORGANIZATIONNAME = RetroArch;
TargetAttributes = {
9204BE091D319EF300BD49DB = {
DevelopmentTeam = UK699V5ZS8;
DevelopmentTeam = R72X3BF4KE;
DevelopmentTeamName = RetroArch;
};
926C77D621FD1E6500103EDE = {
@ -1217,6 +1222,7 @@
92CC05BA21FE3C1700FF79F0 /* GCDWebServerFileRequest.m in Sources */,
92CC05AE21FE3C1700FF79F0 /* GCDWebServerFileResponse.m in Sources */,
9210C2F624B3A32D00E6FE7C /* griffin_cpp.cpp in Sources */,
92DAF342277EC52900FE2A9E /* JITSupport.m in Sources */,
92CC05B221FE3C1700FF79F0 /* GCDWebServerStreamedResponse.m in Sources */,
92CC05A221FE3C1700FF79F0 /* GCDWebServerResponse.m in Sources */,
);
@ -1276,7 +1282,7 @@
CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist";
CURRENT_PROJECT_VERSION = 1.10.0;
DEPS_DIR = "$(SRCBASE)/deps";
DEVELOPMENT_TEAM = UK699V5ZS8;
DEVELOPMENT_TEAM = R72X3BF4KE;
ENABLE_BITCODE = NO;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";
@ -1392,7 +1398,7 @@
CODE_SIGN_RESOURCE_RULES_PATH = "$(SDKROOT)/ResourceRules.plist";
CURRENT_PROJECT_VERSION = 1.10.0;
DEPS_DIR = "$(SRCBASE)/deps";
DEVELOPMENT_TEAM = UK699V5ZS8;
DEVELOPMENT_TEAM = R72X3BF4KE;
ENABLE_BITCODE = NO;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
GCC_PREFIX_HEADER = "";

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:/Users/yoshi/Code/personal/RetroArch-yoshi/pkg/apple/RetroArch_iOS13.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>6D7FAF49-495D-480E-B0C9-8C39AC77CE3C</string>
<key>IDESourceControlProjectName</key>
<string>RetroArch_iOS</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</key>
<string>https://github.com/libretro/retroarch-joypad-autoconfig.git</string>
<key>76200F0D6584D865E96F58DE862E738E88B23A3C</key>
<string>https://github.com/libretro/libretro-super.git</string>
<key>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</key>
<string>https://github.com/libretro/retroarch-assets.git</string>
<key>C3AEE01BDA902108663DB5DB9CD7916436919463</key>
<string>https://github.com/libretro/libretro-database.git</string>
<key>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</key>
<string>https://github.com/libretro/RetroArch.git</string>
<key>EF363D58F01B3FB341FA6C851870E60E4F080E97</key>
<string>https://github.com/libretro/common-overlays.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>apple/iOS/RetroArch_iOS.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</key>
<string>../../../..media/autoconfig</string>
<key>76200F0D6584D865E96F58DE862E738E88B23A3C</key>
<string>../../../../..</string>
<key>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</key>
<string>../../../..media/assets</string>
<key>C3AEE01BDA902108663DB5DB9CD7916436919463</key>
<string>../../../..media/libretrodb</string>
<key>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</key>
<string>../../../..</string>
<key>EF363D58F01B3FB341FA6C851870E60E4F080E97</key>
<string>../../../..media/overlays</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/libretro/RetroArch.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>76200F0D6584D865E96F58DE862E738E88B23A3C</string>
<key>IDESourceControlWCCName</key>
<string></string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>A267D9543F572B4C32EC6E1B876E3B9BFE4DE8F6</string>
<key>IDESourceControlWCCName</key>
<string>assets</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>6B9F0B13E5864452B91F13C09B7ED9EB989E82AD</string>
<key>IDESourceControlWCCName</key>
<string>autoconfig</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>C3AEE01BDA902108663DB5DB9CD7916436919463</string>
<key>IDESourceControlWCCName</key>
<string>libretrodb</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>EF363D58F01B3FB341FA6C851870E60E4F080E97</string>
<key>IDESourceControlWCCName</key>
<string>overlays</string>
</dict>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>C7C12374C7051F8843B3EFA1ACCAF2907102CCF7</string>
<key>IDESourceControlWCCName</key>
<string>retroarch</string>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,30 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "C7C12374C7051F8843B3EFA1ACCAF2907102CCF7",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"76200F0D6584D865E96F58DE862E738E88B23A3C" : 9223372036854775807,
"C7C12374C7051F8843B3EFA1ACCAF2907102CCF7" : 9223372036854775807
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "8BA6E269-73BB-4AAE-8F4A-967CCF8ACA14",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"76200F0D6584D865E96F58DE862E738E88B23A3C" : "",
"C7C12374C7051F8843B3EFA1ACCAF2907102CCF7" : "retroarch\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "RetroArch_iOS10",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "pkg\/apple\/RetroArch_iOS10.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/libretro\/libretro-super.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "76200F0D6584D865E96F58DE862E738E88B23A3C"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/libretro\/RetroArch.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "C7C12374C7051F8843B3EFA1ACCAF2907102CCF7"
}
]
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "9204BE091D319EF300BD49DB"
BuildableName = "RetroArch.app"
BlueprintName = "RetroArchiOS"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "926C77D621FD1E6500103EDE"
BuildableName = "RetroArchTV.app"
BlueprintName = "RetroArchTV"
ReferencedContainer = "container:RetroArch_iOS13.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>$(PRODUCT_NAME)</string>
<string>RetroArch</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>

View File

@ -42,10 +42,18 @@
#endif
#if TARGET_OS_IOS
@class EmulatorKeyboardController;
@interface CocoaView : UIViewController
#elif TARGET_OS_TV
@interface CocoaView : GCEventViewController
#endif
#if TARGET_OS_IOS && defined(HAVE_IOS_CUSTOMKEYBOARD)
@property(nonatomic,strong) EmulatorKeyboardController *keyboardController;
@property(nonatomic,assign) unsigned int keyboardModifierState;
#endif
+ (CocoaView*)get;
@end

View File

@ -26,20 +26,33 @@
#ifdef HAVE_COCOATOUCH
#import "../../../pkg/apple/WebServer/GCDWebUploader/GCDWebUploader.h"
#import "WebServer.h"
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= 130000
#import "RetroArch-Swift.h"
#endif
#endif
#include "../../../configuration.h"
#include "../../../retroarch.h"
#include "../../../verbosity.h"
#include "../../input/drivers/cocoa_input.h"
#include "../../input/drivers_keyboard/keyboard_event_apple.h"
static CocoaView* g_instance;
#ifdef HAVE_COCOATOUCH
void *glkitview_init(void);
@interface CocoaView()<GCDWebUploaderDelegate> {
@interface CocoaView()<GCDWebUploaderDelegate, UIGestureRecognizerDelegate
#ifdef HAVE_IOS_TOUCHMOUSE
,EmulatorTouchMouseHandlerDelegate> {
EmulatorTouchMouseHandler *mouseHandler;
}
#else
>
#endif
@end
#endif
@ -76,23 +89,12 @@ void *glkitview_init(void);
#if defined(HAVE_COCOA)
ui_window_cocoa_t cocoa_view;
cocoa_view.data = (CocoaView*)self;
#elif defined(HAVE_COCOATOUCH)
#if defined(HAVE_COCOA_METAL)
self.view = [UIView new];
#else
self.view = (BRIDGE GLKView*)glkitview_init();
#endif
#endif
#if defined(OSX)
video_driver_display_type_set(RARCH_DISPLAY_OSX);
video_driver_display_set(0);
video_driver_display_userdata_set((uintptr_t)self);
#elif TARGET_OS_IOS
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showNativeMenu)];
swipe.numberOfTouchesRequired = 4;
swipe.direction = UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:swipe];
#endif
return self;
@ -152,6 +154,35 @@ void *glkitview_init(void);
});
}
-(void) showCustomKeyboard
{
#ifdef HAVE_IOS_CUSTOMKEYBOARD
[self.keyboardController.view setHidden:false];
[self updateOverlayAndFocus];
#endif
}
-(void) hideCustomKeyboard
{
#ifdef HAVE_IOS_CUSTOMKEYBOARD
[self.keyboardController.view setHidden:true];
[self updateOverlayAndFocus];
#endif
}
-(void) updateOverlayAndFocus
{
#ifdef HAVE_IOS_CUSTOMKEYBOARD
int cmdData = self.keyboardController.view.isHidden ? 0 : 1;
command_event(CMD_EVENT_GAME_FOCUS_TOGGLE, &cmdData);
if ( self.keyboardController.view.isHidden ) {
command_event(CMD_EVENT_OVERLAY_INIT, NULL);
} else {
command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
}
#endif
}
-(BOOL)prefersHomeIndicatorAutoHidden { return YES; }
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
@ -227,12 +258,15 @@ void *glkitview_init(void);
}
[self adjustViewFrameForSafeArea];
#ifdef HAVE_IOS_CUSTOMKEYBOARD
[self.view bringSubviewToFront:self.keyboardController.view];
#endif
}
/* NOTE: This version runs on iOS6+. */
- (NSUInteger)supportedInterfaceOrientations
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return (NSUInteger)apple_frontend_settings.orientation_flags;
return (UIInterfaceOrientationMask)apple_frontend_settings.orientation_flags;
}
/* NOTE: This version runs on iOS2-iOS5, but not iOS6+. */
@ -265,6 +299,50 @@ void *glkitview_init(void);
#endif
#ifdef HAVE_COCOATOUCH
#pragma mark - UIViewController Lifecycle
-(void)loadView {
#if defined(HAVE_COCOA_METAL)
self.view = [UIView new];
#else
self.view = (BRIDGE GLKView*)glkitview_init();
#endif
}
-(void)viewDidLoad {
[super viewDidLoad];
#if TARGET_OS_IOS
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showNativeMenu)];
swipe.numberOfTouchesRequired = 4;
swipe.delegate = self;
swipe.direction = UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:swipe];
#ifdef HAVE_IOS_TOUCHMOUSE
mouseHandler = [[EmulatorTouchMouseHandler alloc] initWithView:self.view];
mouseHandler.delegate = self;
#endif
#ifdef HAVE_IOS_CUSTOMKEYBOARD
[self setupEmulatorKeyboard];
UISwipeGestureRecognizer *showKeyboardSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(showCustomKeyboard)];
showKeyboardSwipe.numberOfTouchesRequired = 3;
showKeyboardSwipe.direction = UISwipeGestureRecognizerDirectionUp;
showKeyboardSwipe.delegate = self;
[self.view addGestureRecognizer:showKeyboardSwipe];
UISwipeGestureRecognizer *hideKeyboardSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(hideCustomKeyboard)];
hideKeyboardSwipe.numberOfTouchesRequired = 3;
hideKeyboardSwipe.direction = UISwipeGestureRecognizerDirectionDown;
hideKeyboardSwipe.delegate = self;
[self.view addGestureRecognizer:hideKeyboardSwipe];
#endif
#endif
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (void)viewDidAppear:(BOOL)animated
{
#if TARGET_OS_IOS
@ -282,6 +360,47 @@ void *glkitview_init(void);
#endif
}
#if TARGET_OS_IOS && HAVE_IOS_TOUCHMOUSE
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[mouseHandler touchesBeganWithTouches:touches];
}
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[mouseHandler touchesMovedWithTouches:touches];
}
-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[mouseHandler touchesCancelledWithTouches:touches];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[mouseHandler touchesEndedWithTouches:touches];
}
#pragma mark EmulatorTouchMouseHandlerDelegate
-(void)handleMouseClickWithIsLeftClick:(BOOL)isLeftClick isPressed:(BOOL)isPressed {
cocoa_input_data_t *apple = (cocoa_input_data_t*) input_state_get_ptr()->current_data;
if (apple == NULL) {
return;
}
NSUInteger buttonIndex = isLeftClick ? 0 : 1;
if (isPressed) {
apple->mouse_buttons |= (1 << buttonIndex);
} else {
apple->mouse_buttons &= ~(1 << buttonIndex);
}
}
-(void)handleMouseMoveWithX:(CGFloat)x y:(CGFloat)y {
cocoa_input_data_t *apple = (cocoa_input_data_t*) input_state_get_ptr()->current_data;
if (apple == NULL) {
return;
}
apple->mouse_rel_x = (int16_t)x;
apple->mouse_rel_y = (int16_t)y;
}
#endif
#pragma mark GCDWebServerDelegate
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server
{
@ -309,6 +428,7 @@ void *glkitview_init(void);
}];
#endif
}
#endif
@end

View File

@ -41,6 +41,9 @@
#import <AVFoundation/AVFoundation.h>
#if defined(HAVE_COCOA_METAL) || defined(HAVE_COCOATOUCH)
#if TARGET_OS_IOS
#import "JITSupport.h"
#endif
id<ApplePlatform> apple_platform;
#else
static id apple_platform;
@ -539,6 +542,13 @@ enum
int main(int argc, char *argv[])
{
#if TARGET_OS_IOS
if (jb_enable_ptrace_hack()) {
NSLog(@"Ptrace hack complete, JIT support is enabled");
} else {
NSLog(@"Ptrace hack NOT available; Please use an app like Jitterbug.");
}
#endif
@autoreleasepool {
return UIApplicationMain(argc, argv, NSStringFromClass([RApplication class]), NSStringFromClass([RetroArch_iOS class]));
}