Apple introduced the GCVirtualController in iOS 15 which is a
software emulation of a real controller. The virtual controllers
can be configurable with different inputs. See more info at:
https://developer.apple.com/documentation/gamecontroller/gcvirtualcontroller
A simple gamepad configuration with a dPad and A and B buttons
is added. The user can enable/disable the virtual game controller
swiping two fingers right to left, or through the port-specific
option dialog.
Some game engines requires the dpad to control a character. The GRIM
engine is an example of this where the user steer the character by
the arrow keys or dpad controller.
The "touchpad" mode and "click-and-drag" mode was mutual exclusive
when enabling "click-and-drag" using swipe gesture.
The difference between "touchpad" mode and "click-and-drag" mode
is how the button down/up events are sent. In touchpad mode the
button down and button up events are sent on touches ended, while
in click-and-drag the button down event is sent on touches began
and button up on touches ended.
Include the newly added ios7_options implementation to the project.
Change the file type to .mm which is Objective C++ to be able to use
the @availble mechanism.
Implement virtual functions and fix build errors in initial code.
Also add help section for the tvOS port when building for tvOS.
Add ios7_options to POTFILES to get automatic translation on the
help section.
On iOS 12 and below, the one delegate methods are called. On iOS13.2
and above if both sets are implemented only the new delegate methods
are called, which prevent getting the deprecation warning.
The timeHandler was driven by calls to the pollEvent callback function.
Each time pollEvent was called the timeHandler called the TimeManager
handle function to advance in time and make sure scheduled tasks were
triggered.
This worked good for most game engines but some, e.g. the Hypno engine
was using the TimeManager to schedule tasks without calling pollEvent
since it was expecting nor handling events at the specific point in
time.
Since iOS have threads the timerHandler can be called from a separate
thread and not rely on pollEvent.
Implement timerHandler to use a Timer Dispatch Source which and make
it operate on a background thread rather than the main thread.
Read more on Dispatch Sources here:
https://developer.apple.com/library/archive/documentation/General/
Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html
Previously no log file was used as it attempted to create it in
a directory not accessible by the application. The commit also fixes
accessing the log file from the Options dialog (it needs the
sandboxed path and not the full path).
This was used in the past to make sure the code can be compiled
with old compilers that do not support using @available. But we
already dropped support for those old compilers, and in many
places already used @available without checking first that it can
be used.
The main change in to use the interface orientation and not the device
orientation. This may be different for example when locking the orientation.
This also changes the way orientation changes are detected using the
documented method. However this means dropping support for iOS 7 as this
method is only available since iOS 8, and alternative methods available in
iOS 7 have been deprecated in iOS 13.
Another change is to properly detect the interface orientation instead of
infering it from the view bounds, which was incorrect on some devices.
This commit fixes the compiler warnings regarding:
- The local declaration of 'view' hides instance variable [-Wshadow-ivar]
- Some of the gamepad controller buttons is only available in specific
versions of iOS and tvOS.
- Use of non-standard escape character '\E'. \E is a GNU shortcut.
Add isInGame property to track if the launcher is shown of if a game is
running. Handle press on menu key different depending on if launcher is
shown or not. If launcher is shown suspend the application to return to
Apple TV Home Screen since that is the parent view of the launcher. If
in game pause the game and show menu. This is according to Apple
guidelines which can ge read here:
https://developer.apple.com/design/human-interface-guidelines/inputs/remotes
The keyboard can be presented and dismissed without being triggered by
the showKeyboard/hideKeyboard functions e.g. by pressing the menu button
on the Apple TV remote while the keyboard is shown.
If the keyboard visibility is not set entirely by the showKeyboard/
hideKeyboard functions that means that the _keyboardVisible state
variable can be out of sync.
Check if the keyboard is shown based on if the inputView is the first
responder or not. The check has to be made on the main thread.
All buttons and triggers on MFi game controllers are pressure sensitive
which means that when pressing buttons the registered
valueChangedHandler function is called multiple times providing updates
on the pressure value the button is pressed with. This causes multiple
kInputJoystickButtonDown events to be sent to the EventManager.
In adventure games the pressure value is not relevant and could cause
problems for the user that it triggers multiple presses on e.g. the B
button which often is mapped to the right mouse button. In some games
a click on the right mouse button changes what action that should be
performed.
Keep track on if the joystick buttons A or B (often mapped as left and
right mouse buttons) are being pressed. If the button is already
pressed do not add a new event until the button isn't pressed anymore.
To not interfere with any open dialog, don't send key events while the
keyboard is visible.
The inputAccessoryView is only shown if no hardware keyboard is
connected. Some hardware keyboards doesn't necessary have all keys,
e.g. the Apple magic keyboard to the iPads which lacks the escape
key and all function keys.
To give the user the possibility to use these buttons, always show
the inputAccessoryView.
The UIAlertView was deprecated in iOS 9 and therefore not supported in
tvOS. Replace the UIAlertView with the suggested UIAlertAction, which is
supported by both iOS and tvOS.
Define a macro to find the root view controller of a view. Use the view
controller of the iPhoneView to present the alert.
There's a difference between UITextFields and UITextViews that the
delegate function textView:shouldChangeTextInRange:replacementText:
is called when pressing the backward button on a keyboard also when
the textView is empty. This is not the case for UITextFields, the
function textField:shouldChangeTextInRange:replacementText: is not
called if the textField is empty which is problematic in the cases
where there's already text in the open dialog (e.g. the save dialog
when the user wants to overwrite an existing slot). There's currently
no possibility to propagate existing text elements from dialog into
the textField. To be able to handle the cases where the user wants to
delete existing texts when the textField is empty the inputView has
to implement the UITextInput protocol function deleteBackward that is
called every time the backward key is pressed.
The UITextView is becoming focused by default in iOS and brings up the
keyboard for user input. This is not the case in tvOS. UITextView in
tvOS is not becoming focused by default and if manually setting it to
focused it will still not bring up the keyboard screen.
The UITextField is however becoming focused in both iOS and tvOS and
requires basically the same implementation. So the UITextView is
replaced with UITextField to bring up keyboard in both iOS and tvOS.
The UIToolbar class is not supported in tvOS. Instead implement the
toolbar as a UITabBar. The UITabBar is set directly as the
inputAccessoryView to the keyboard view in tvOS while in iOS it's put in
a UIScrollView (as the previous UIToolbar) to be able to scale the
inputAccessoryView better for small screens.
The UITabBar behaves a little bit different on iOS and tvOS where in
tvOS the delegate function
-(void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
is called when navigated to a specific toolbar item, while in iOS called
when clicking on an item. To get the tvOS to trigger action on presses,
add a gesture recognizer to handle touch events.
Since the keyboard view on Apple TV always full screen prompted texts
gets hidden behind the keyboard. Delay the showing of the keyboard to
allow the user to understand what's requested as input.
The Apple TV remote has a touch area which functions as a touch
controller. It also has a few buttons that can be programmed for
different actions.
The "touchpad mode" is not really relevant for the Apple TV remote.
However the pointer should be moved when swiping on the touch area
on the remote. Since we don't want to generate actions on touchBegan
and touchEnded for the remote, make sure these are only triggered if
the touch is made on direct contact with the screen, UITouchTypeDirect.
Implement the button handling by implement the microGamepad profile in
the GamepadController class. Only buttons A, X and the menu buttons are
relevant since the touch area is using the touch controller class.
The tvOS simulator however doesn't call the lambda functions defined for
the microGamepad buttons, hence the implementation of the "pressesBegan"
and "pressesEnded" which is called instead when running the simulator.
Implement common handling of the menu button. If the menu button is
pressed the soft keyboard will be shown. If pressing the menu button
again the soft keyboard will be hidden. If pressing the menu button
a third time the application will be suspended.
Implement new gesture recognizers that can be used with the Apple TV
remote. Up/down/left/right actions are triggered on press on the arrow
buttons, or tap on the edges of the touch area.
A long press, 5 seconds, of "Play/Pause" button toggles Mouse-click-and-
drag mode needed by some games.
iOS and tvOS shares a lot of code. However some there are parts that are
specific to iOS, for instance handling of UI device orientation and
certain types of gestures.
Currently there are also some limitations on the Apple TV that needs to
be flagged to the engine. There is no support for virtual keyboard, no
clipboard support and no possibility to open URLs.
Put code specific for iOS within the ObjC platfrom macro TARGET_OS_IOS.
The code specific for tvOS are put within the macro TARGET_OS_TV.
This enables some game engines only supporting 32 bit color formats,
e.g. 11th hour and Broken Sword 2.5.
Add support for the pixel formats RGBA8888 and ABGR8888. The pixel
formats are defined in big endian while iOS utilizes little endian,
thus requiring converting some formats to get correct color
representation.
Use the Apple Accelerate framework converting the format requiring
to minimize the CPU load since this is done every frame.
Joystick button up events was not sent to the EventManager due to a
missing break in the kInputJoystickButtonUp case. This caused button
presses in the launcher and dialogs not to be triggered.
Adding the break enables use of joystick buttons in ScummVM GUIs.
"Touchpad mode" is a mode where the mouse cursor is moved based on touch
movements rather on clicks. The problem was that when "touchpad mode"
was enabled it was very hard to click on items because the cursor moved
on every single click.
Make the action occur based on the current pointer position rather on
the touch location when in "touchpad mode".
Make the movement more intuitive when in "touchpad mode" by calculating
the delta of locations of touches and update the pointerPosition based
on that. That will give a feeling of using a real touchpad where the
location of where the touch occur doesn't matter for the cursor.
This will solve issue #13917
Add support for Extended Gamepad controllers. What defines extended
gamepad controllers can be found here:
https://developer.apple.com/documentation/gamecontroller/gcextendedgamepad
Support has been added for controlling the pointer position using the
left thumbstick, left clicks using the A-button and right clicks using
the B-button. Also the Main menu can be accessed using the Home/Menu
button.
The thumbstick values are received when changed, however if holding the
thumbstick in the same position the valueChangedHandler will not be
called. Therefore store the X- and Y-axis values and begin to poll
readings of the stored values for as long as the thumbstick is out of
the center position.
Joystick actions are suitable for joysticks and gamepads where the
movements are updated by a controller stick. On gamepads that's usually
a thumbstick.
Add joystick events which can be triggered by each implemented
controller that should utilize the ScummVM Joystick events.
Add support for mouses using the GameController framework. This requires
iOS 14 and up. The trackpad on the magic keyboard to iPads is connected
as a mouse and of course other connected mouses.
The mouse movements triggers calls to the mouseMovedHandler code
block. The calls delivers delta movements on the X and Y axis from the
last pointer position. It doesn't keep track on where the pointer is in
the view. That's where pointerPosition property in the iPhoneView comes
into place.
Move touch inputs to a TouchController class to move some logic from the
iPhoneView class. Only do this for touches on screen since connected
trackpads can generate touches as well. The latter ones are of type
UITouchTypeIndirectPointer while touches on screen are of type
UITouchTypeDirect. They are separated thanks to the preference key
UIApplicationSupportsIndirectInputEvents set to YES in Info.plist.
Without the preference above, there is no way to distinguish touches
from screen from a trackpad.
Add a GameController base class which handles user inputs from a
controller. The input is either a pointer move or a button action.
If the input is a pointer move, make sure that the move is within
valid coordinates in the game (respecting the resolution which is
most probably lower than the view resolution).
Setting this property to true indicates the view controller’s preference
to lock the pointer, although the system may not honor the request.
For the system to consider locking the pointer:
The scene must be full screen, not in Split View or Slide Over, with no
other apps in Slide Over.
The scene must be in the UISceneActivationStateForegroundActive state.
The ScummVM iOS7 client fulfills the above so the pointer is locked.
Locking the pointer hides the OS cursor (the dot), however that's wanted
since the ScummVM engine draws its own pointer.