Add ios virtual controller support.

This commit is contained in:
Roland Vigh 2024-10-07 13:31:00 +02:00
parent 1bffd6c73b
commit 7903c5fe85
17 changed files with 641 additions and 0 deletions

View File

@ -1576,6 +1576,13 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_controller", false);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_left_thumbstick", true);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_right_thumbstick", true);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_a", true);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_b", true);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_x", true);
GLOBAL_DEF_BASIC("input_devices/virtual_controller/ios/enable_button_y", true);
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());

View File

@ -188,6 +188,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_emulating_mouse_from_touch"), &Input::is_emulating_mouse_from_touch);
ClassDB::bind_method(D_METHOD("set_emulate_touch_from_mouse", "enable"), &Input::set_emulate_touch_from_mouse);
ClassDB::bind_method(D_METHOD("is_emulating_touch_from_mouse"), &Input::is_emulating_touch_from_mouse);
ClassDB::bind_method(D_METHOD("get_virtual_controller"), &Input::get_virtual_controller);
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
@ -1054,6 +1055,10 @@ bool Input::is_emulating_touch_from_mouse() const {
return emulate_touch_from_mouse;
}
VirtualController *Input::get_virtual_controller() {
return OS::get_singleton()->get_virtual_controller();
}
// Calling this whenever the game window is focused helps unsticking the "touch mouse"
// if the OS or its abstraction class hasn't properly reported that touch pointers raised
void Input::ensure_touch_mouse_raised() {

View File

@ -32,6 +32,7 @@
#define INPUT_H
#include "core/input/input_event.h"
#include "core/input/virtual_controller.h"
#include "core/object/object.h"
#include "core/os/keyboard.h"
#include "core/os/thread_safe.h"
@ -383,6 +384,8 @@ public:
void set_use_accumulated_input(bool p_enable);
bool is_using_accumulated_input();
VirtualController *get_virtual_controller();
void release_pressed_events();
void set_event_dispatch_function(EventDispatchFunc p_function);

View File

@ -0,0 +1,49 @@
/**************************************************************************/
/* virtual_controller.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "virtual_controller.h"
void VirtualController::_bind_methods() {
ClassDB::bind_method(D_METHOD("enable"), &VirtualController::enable);
ClassDB::bind_method(D_METHOD("disable"), &VirtualController::disable);
ClassDB::bind_method(D_METHOD("is_enabled"), &VirtualController::is_enabled);
ClassDB::bind_method(D_METHOD("set_enabled_left_thumbstick", "enable"), &VirtualController::set_enabled_left_thumbstick);
ClassDB::bind_method(D_METHOD("is_enabled_left_thumbstick"), &VirtualController::is_enabled_left_thumbstick);
ClassDB::bind_method(D_METHOD("set_enabled_right_thumbstick", "enable"), &VirtualController::set_enabled_right_thumbstick);
ClassDB::bind_method(D_METHOD("is_enabled_right_thumbstick"), &VirtualController::is_enabled_right_thumbstick);
ClassDB::bind_method(D_METHOD("set_enabled_button_a", "enable"), &VirtualController::set_enabled_button_a);
ClassDB::bind_method(D_METHOD("is_enabled_button_a"), &VirtualController::is_enabled_button_a);
ClassDB::bind_method(D_METHOD("set_enabled_button_b", "enable"), &VirtualController::set_enabled_button_b);
ClassDB::bind_method(D_METHOD("is_enabled_button_b"), &VirtualController::is_enabled_button_b);
ClassDB::bind_method(D_METHOD("set_enabled_button_x", "enable"), &VirtualController::set_enabled_button_x);
ClassDB::bind_method(D_METHOD("is_enabled_button_x"), &VirtualController::is_enabled_button_x);
ClassDB::bind_method(D_METHOD("set_enabled_button_y", "enable"), &VirtualController::set_enabled_button_y);
ClassDB::bind_method(D_METHOD("is_enabled_button_y"), &VirtualController::is_enabled_button_y);
}

View File

@ -0,0 +1,60 @@
/**************************************************************************/
/* virtual_controller.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef VIRTUAL_CONTROLLER_H
#define VIRTUAL_CONTROLLER_H
#include "core/object/class_db.h"
class VirtualController : public Object {
GDCLASS(VirtualController, Object);
protected:
static void _bind_methods();
public:
virtual void enable() = 0;
virtual void disable() = 0;
virtual bool is_enabled() = 0;
virtual void set_enabled_left_thumbstick(bool p_enabled) = 0;
virtual bool is_enabled_left_thumbstick() = 0;
virtual void set_enabled_right_thumbstick(bool p_enabled) = 0;
virtual bool is_enabled_right_thumbstick() = 0;
virtual void set_enabled_button_a(bool p_enabled) = 0;
virtual bool is_enabled_button_a() = 0;
virtual void set_enabled_button_b(bool p_enabled) = 0;
virtual bool is_enabled_button_b() = 0;
virtual void set_enabled_button_x(bool p_enabled) = 0;
virtual bool is_enabled_button_x() = 0;
virtual void set_enabled_button_y(bool p_enabled) = 0;
virtual bool is_enabled_button_y() = 0;
};
#endif // VIRTUAL_CONTROLLER_H

View File

@ -700,6 +700,10 @@ void OS::benchmark_dump() {
#endif
}
VirtualController *OS::get_virtual_controller() const {
return nullptr;
}
OS::OS() {
singleton = this;

View File

@ -32,6 +32,7 @@
#define OS_H
#include "core/config/engine.h"
#include "core/input/virtual_controller.h"
#include "core/io/logger.h"
#include "core/io/remote_filesystem_client.h"
#include "core/os/time_enums.h"
@ -344,6 +345,8 @@ public:
// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
virtual void load_platform_gdextensions() const {}
virtual VirtualController *get_virtual_controller() const;
OS();
virtual ~OS();
};

View File

@ -42,6 +42,7 @@
#include "core/input/input.h"
#include "core/input/input_map.h"
#include "core/input/shortcut.h"
#include "core/input/virtual_controller.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/io/dtls_server.h"
@ -298,6 +299,8 @@ void register_core_types() {
GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right");
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
GDREGISTER_ABSTRACT_CLASS(VirtualController);
worker_thread_pool = memnew(WorkerThreadPool);
OS::get_singleton()->benchmark_end_measure("Core", "Register Types");

View File

@ -195,6 +195,13 @@
By default, the deadzone is automatically calculated from the average of the action deadzones. However, you can override the deadzone to be whatever you want (on the range of 0 to 1).
</description>
</method>
<method name="get_virtual_controller">
<return type="VirtualController" />
<description>
Return platform-specific virtual controller or null. (See [VirtualController])
[b]Note:[/b] Currently implemented only on iOS 15.0+.
</description>
</method>
<method name="is_action_just_pressed" qualifiers="const">
<return type="bool" />
<param index="0" name="action" type="StringName" />

View File

@ -1456,6 +1456,28 @@
<member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false">
If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data.
</member>
<member name="input_devices/virtual_controller/ios/enable_button_a" type="bool" setter="" getter="" default="true">
If [code]true[/code], the button "A" shows in the virtual controller.
</member>
<member name="input_devices/virtual_controller/ios/enable_button_b" type="bool" setter="" getter="" default="true">
If [code]true[/code], the button "B" shows in the virtual controller.
</member>
<member name="input_devices/virtual_controller/ios/enable_button_x" type="bool" setter="" getter="" default="true">
If [code]true[/code], the button "X" shows in the virtual controller.
</member>
<member name="input_devices/virtual_controller/ios/enable_button_y" type="bool" setter="" getter="" default="true">
If [code]true[/code], the button "Y" shows in the virtual controller.
</member>
<member name="input_devices/virtual_controller/ios/enable_controller" type="bool" setter="" getter="" default="false">
If [code]true[/code], if there are no physical controllers connected, the game shows the virtual controller.
[b]Note:[/b] Currently implemented only on iOS 15.0+.
</member>
<member name="input_devices/virtual_controller/ios/enable_left_thumbstick" type="bool" setter="" getter="" default="true">
If [code]true[/code], the left thumbstick shows in the virtual controller.
</member>
<member name="input_devices/virtual_controller/ios/enable_right_thumbstick" type="bool" setter="" getter="" default="true">
If [code]true[/code], the right thumbstick shows in the virtual controller.
</member>
<member name="internationalization/locale/fallback" type="String" setter="" getter="" default="&quot;en&quot;">
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
</member>

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VirtualController" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
A software emulation of a real controller that you configure specifically for your game.
</brief_description>
<description>
Use a virtual controller to display software controls that you can customize over your game.
You create a virtual controller from a configuration where you choose the input elements to display.
When you connect the controller to the device, users interact with it similarly to a real controller.
[b]Note:[/b] Currently implemented only on iOS 15.0+.
</description>
<tutorials>
</tutorials>
<methods>
<method name="disable">
<return type="void" />
<description>
Disconnects the virtual controller from the device and removes it from the screen.
</description>
</method>
<method name="enable">
<return type="void" />
<description>
If there are no physical controllers connected, connects the virtual controller to the device and displays it on the screen.
</description>
</method>
<method name="is_enabled">
<return type="bool" />
<description>
Returns [code]true[/code] if the virtual controller is enabled.
</description>
</method>
<method name="is_enabled_button_a">
<return type="bool" />
<description>
Returns [code]true[/code] if the button "A" is enabled.
</description>
</method>
<method name="is_enabled_button_b">
<return type="bool" />
<description>
Returns [code]true[/code] if the button "B" is enabled.
</description>
</method>
<method name="is_enabled_button_x">
<return type="bool" />
<description>
Returns [code]true[/code] if the button "X" is enabled.
</description>
</method>
<method name="is_enabled_button_y">
<return type="bool" />
<description>
Returns [code]true[/code] if the button "Y" is enabled.
</description>
</method>
<method name="is_enabled_left_thumbstick">
<return type="bool" />
<description>
Returns [code]true[/code] if the left thumbstick is enabled.
</description>
</method>
<method name="is_enabled_right_thumbstick">
<return type="bool" />
<description>
Returns [code]true[/code] if the right thumbstick is enabled.
</description>
</method>
<method name="set_enabled_button_a">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a button "A" element. Default is [code]true[/code].
</description>
</method>
<method name="set_enabled_button_b">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a button "B" element. Default is [code]true[/code].
</description>
</method>
<method name="set_enabled_button_x">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a button "A" element. Default is [code]true[/code].
</description>
</method>
<method name="set_enabled_button_y">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a button "Y" element. Default is [code]true[/code].
</description>
</method>
<method name="set_enabled_left_thumbstick">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a left thumbstick element. Default is [code]true[/code].
</description>
</method>
<method name="set_enabled_right_thumbstick">
<return type="void" />
<param index="0" name="enable" type="bool" />
<description>
Changes the visibility of a right thumbstick element. Default is [code]true[/code].
</description>
</method>
</methods>
</class>

View File

@ -78,6 +78,7 @@ ios_lib = [
"keyboard_input_view.mm",
"key_mapping_ios.mm",
"ios_terminal_logger.mm",
"virtual_controller_ios.mm",
]
env_ios = env.Clone()

View File

@ -184,6 +184,8 @@ void JoypadIOS::start_processing() {
} else {
[self addiOSJoypad:controller];
}
OS_IOS::get_singleton()->controller_connected();
}
- (void)controllerWasDisconnected:(NSNotification *)notification {
@ -203,6 +205,8 @@ void JoypadIOS::start_processing() {
// and remove it from our dictionary
[self.connectedJoypads removeObjectForKey:key];
}
OS_IOS::get_singleton()->controller_disconnected();
}
- (GCControllerPlayerIndex)getFreePlayerIndex {

View File

@ -35,6 +35,7 @@
#import "ios.h"
#import "joypad_ios.h"
#import "virtual_controller_ios.h"
#import "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
@ -62,6 +63,8 @@ private:
MainLoop *main_loop = nullptr;
IOSVirtualController *virtual_controller = nullptr;
virtual void initialize_core() override;
virtual void initialize() override;
@ -127,11 +130,17 @@ public:
virtual bool _check_internal_feature_support(const String &p_feature) override;
virtual VirtualController *get_virtual_controller() const override;
void on_focus_out();
void on_focus_in();
void on_enter_background();
void on_exit_background();
void controller_connected();
void controller_disconnected();
};
#endif // IOS_ENABLED

View File

@ -131,9 +131,15 @@ void OS_IOS::initialize_modules() {
Engine::get_singleton()->add_singleton(Engine::Singleton("iOS", ios));
joypad_ios = memnew(JoypadIOS);
virtual_controller = memnew(IOSVirtualController);
}
void OS_IOS::deinitialize_modules() {
if (virtual_controller) {
memdelete(virtual_controller);
}
if (joypad_ios) {
memdelete(joypad_ios);
}
@ -180,6 +186,10 @@ void OS_IOS::start() {
if (joypad_ios) {
joypad_ios->start_processing();
}
if (virtual_controller) {
virtual_controller->update_state();
}
}
void OS_IOS::finalize() {
@ -591,6 +601,10 @@ bool OS_IOS::_check_internal_feature_support(const String &p_feature) {
return false;
}
VirtualController *OS_IOS::get_virtual_controller() const {
return virtual_controller;
}
void OS_IOS::on_focus_out() {
if (is_focused) {
is_focused = false;
@ -647,4 +661,16 @@ void OS_IOS::on_exit_background() {
}
}
void OS_IOS::controller_connected() {
if (virtual_controller) {
virtual_controller->controller_connected();
}
}
void OS_IOS::controller_disconnected() {
if (virtual_controller) {
virtual_controller->controller_disconnected();
}
}
#endif // IOS_ENABLED

View File

@ -0,0 +1,80 @@
/**************************************************************************/
/* virtual_controller_ios.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "core/input/virtual_controller.h"
#import <GameController/GameController.h>
class IOSVirtualController : public VirtualController {
private:
#if defined(__IPHONE_15_0)
API_AVAILABLE(ios(15.0))
GCVirtualController *gcv_controller;
#endif
public:
IOSVirtualController();
~IOSVirtualController();
virtual void enable() override;
virtual void disable() override;
virtual bool is_enabled() override;
virtual void set_enabled_left_thumbstick(bool p_enabled) override;
virtual bool is_enabled_left_thumbstick() override;
virtual void set_enabled_right_thumbstick(bool p_enabled) override;
virtual bool is_enabled_right_thumbstick() override;
virtual void set_enabled_button_a(bool p_enabled) override;
virtual bool is_enabled_button_a() override;
virtual void set_enabled_button_b(bool p_enabled) override;
virtual bool is_enabled_button_b() override;
virtual void set_enabled_button_x(bool p_enabled) override;
virtual bool is_enabled_button_x() override;
virtual void set_enabled_button_y(bool p_enabled) override;
virtual bool is_enabled_button_y() override;
void controller_connected();
void controller_disconnected();
void update_state();
private:
void connect_controller();
void disconnect_controller();
void initialize();
void elements_changed(GCInputElementName name, bool hidden);
void read_project_settings();
private:
bool enabled = false;
bool enabled_left_thumbstick = true;
bool enabled_right_thumbstick = true;
bool enabled_button_a = true;
bool enabled_button_b = true;
bool enabled_button_x = true;
bool enabled_button_y = true;
};

View File

@ -0,0 +1,246 @@
/**************************************************************************/
/* virtual_controller_ios.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "virtual_controller_ios.h"
#include "core/config/project_settings.h"
IOSVirtualController::IOSVirtualController() {
initialize();
}
IOSVirtualController::~IOSVirtualController() {
if (@available(iOS 15.0, *)) {
gcv_controller = nullptr;
}
}
void IOSVirtualController::initialize() {
if (@available(iOS 15.0, *)) {
if (!gcv_controller) {
read_project_settings();
GCVirtualControllerConfiguration *config = [[GCVirtualControllerConfiguration alloc] init];
NSMutableSet *elements = [[NSMutableSet alloc] init];
if (is_enabled_left_thumbstick()) {
[elements addObject:GCInputLeftThumbstick];
}
if (is_enabled_right_thumbstick()) {
[elements addObject:GCInputRightThumbstick];
}
if (is_enabled_button_a()) {
[elements addObject:GCInputButtonA];
}
if (is_enabled_button_b()) {
[elements addObject:GCInputButtonB];
}
if (is_enabled_button_x()) {
[elements addObject:GCInputButtonX];
}
if (is_enabled_button_y()) {
[elements addObject:GCInputButtonY];
}
config.elements = elements;
dispatch_async(dispatch_get_main_queue(), ^{
gcv_controller = [[GCVirtualController alloc] initWithConfiguration:config];
});
}
}
}
void IOSVirtualController::read_project_settings() {
enabled = GLOBAL_GET("input_devices/virtual_controller/ios/enable_controller");
enabled_left_thumbstick = GLOBAL_GET("input_devices/virtual_controller/ios/enable_left_thumbstick");
enabled_right_thumbstick = GLOBAL_GET("input_devices/virtual_controller/ios/enable_right_thumbstick");
enabled_button_a = GLOBAL_GET("input_devices/virtual_controller/ios/enable_button_a");
enabled_button_b = GLOBAL_GET("input_devices/virtual_controller/ios/enable_button_b");
enabled_button_x = GLOBAL_GET("input_devices/virtual_controller/ios/enable_button_x");
enabled_button_y = GLOBAL_GET("input_devices/virtual_controller/ios/enable_button_y");
}
void IOSVirtualController::elements_changed(GCInputElementName name, bool hidden) {
if (@available(iOS 15.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
if (gcv_controller) {
[gcv_controller updateConfigurationForElement:name
configuration:^(GCVirtualControllerElementConfiguration *configuration) {
configuration.hidden = hidden;
return configuration;
}];
}
});
}
}
void IOSVirtualController::enable() {
enabled = true;
update_state();
}
void IOSVirtualController::disable() {
enabled = false;
update_state();
}
void IOSVirtualController::update_state() {
if (is_enabled()) {
connect_controller();
} else {
disconnect_controller();
}
}
bool IOSVirtualController::is_enabled() {
return enabled;
}
void IOSVirtualController::connect_controller() {
if (@available(iOS 15.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
if (GCController.controllers.count == 0 && gcv_controller != nil) {
[gcv_controller connectWithReplyHandler:nil];
}
});
}
}
void IOSVirtualController::disconnect_controller() {
if (@available(iOS 15.0, *)) {
dispatch_async(dispatch_get_main_queue(), ^{
if (gcv_controller) {
[gcv_controller disconnect];
}
});
}
}
void IOSVirtualController::controller_connected() {
if (@available(iOS 15.0, *)) {
if (gcv_controller != nil) {
BOOL hasPhysicalController = NO;
for (GCController *controller in GCController.controllers) {
if (controller != gcv_controller.controller) {
hasPhysicalController = YES;
break;
}
}
if (hasPhysicalController) {
disconnect_controller();
}
}
}
}
void IOSVirtualController::controller_disconnected() {
if (is_enabled()) {
connect_controller();
}
}
void IOSVirtualController::set_enabled_left_thumbstick(bool p_enabled) {
if (enabled_left_thumbstick != p_enabled) {
enabled_left_thumbstick = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputLeftThumbstick, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_left_thumbstick() {
return enabled_left_thumbstick;
}
void IOSVirtualController::set_enabled_right_thumbstick(bool p_enabled) {
if (enabled_right_thumbstick != p_enabled) {
enabled_right_thumbstick = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputRightThumbstick, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_right_thumbstick() {
return enabled_right_thumbstick;
}
void IOSVirtualController::set_enabled_button_a(bool p_enabled) {
if (enabled_button_a != p_enabled) {
enabled_button_a = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputButtonA, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_button_a() {
return enabled_button_a;
}
void IOSVirtualController::set_enabled_button_b(bool p_enabled) {
if (enabled_button_b != p_enabled) {
enabled_button_b = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputButtonB, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_button_b() {
return enabled_button_b;
}
void IOSVirtualController::set_enabled_button_x(bool p_enabled) {
if (enabled_button_x != p_enabled) {
enabled_button_x = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputButtonX, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_button_x() {
return enabled_button_x;
}
void IOSVirtualController::set_enabled_button_y(bool p_enabled) {
if (enabled_button_y != p_enabled) {
enabled_button_y = p_enabled;
if (@available(iOS 15.0, *)) {
elements_changed(GCInputButtonY, !p_enabled);
}
}
}
bool IOSVirtualController::is_enabled_button_y() {
return enabled_button_y;
}