Skip to content

Add support for iOS joystick - #3750

Open
JasonLeon01 wants to merge 1 commit into
SFML:masterfrom
JasonLeon01:iOS-Joystick
Open

Add support for iOS joystick#3750
JasonLeon01 wants to merge 1 commit into
SFML:masterfrom
JasonLeon01:iOS-Joystick

Conversation

@JasonLeon01

Copy link
Copy Markdown

Description

This PR implements the previously unimplemented iOS joystick backend using Apple's GameController framework. Controllers exposing an extended gamepad profile can now be queried through sf::Joystick and generate the existing joystick events.

The implementation:

This PR implements the previously unimplemented iOS joystick backend using Apple's GameController framework for controllers exposing GCExtendedGamepad.

It tracks controllers through connection notifications and enumeration, rejects input from stale connections, and clears input while the application is inactive. Optional Options, Home, and Xbox Share buttons are read when available, subject to system gesture handling.

The iOS build now links GameController and compiles JoystickImpl.mm with ARC.

Tasks

  • Tested on Linux
  • Tested on Windows
  • Tested on macOS
  • Tested on iOS
  • Tested on Android

How to test this PR?

Build SFML for iOS and run the following example in an iOS application linked against SFML::Graphics and SFML::Main. Observe the event output in the Xcode debug console. The window turns green while button A on joystick 0 is held, exercising the real-time polling API as well as the event API.

The existing joystick example is currently excluded from iOS builds, so enabling SFML_BUILD_EXAMPLES alone does not provide an iOS joystick test application.

  1. Launch with an extended-gamepad controller already connected, then repeat by connecting it after launch. Verify detection, a readable controller name, and the reported button count in both cases, plus a connection event when connecting after launch.
  2. Move both sticks, fully press and release both triggers, and use the D-pad. Verify the corresponding axis events and return to neutral when released.
  3. Press and release each available button. Verify the reported indices, including optional buttons on controllers and OS versions that expose them.
  4. Connect two controllers and disconnect one. The remaining controller should retain its joystick index. Reconnect or replace the disconnected controller and verify connection events and fresh input without stale pressed buttons or axis values.
  5. Hold a button or move a stick, make the app inactive, release the input, and return to the app. Verify there is no stuck input. When inspecting joystick state during an inactive update, all axes should be zero and all buttons released.
#include <SFML/Graphics.hpp>
#include <SFML/Main.hpp>

#include <iostream>
#include <optional>

void printController(unsigned int id)
{
    std::cout << "Connected " << id << ": "
              << sf::Joystick::getIdentification(id).name.toAnsiString()
              << ", buttons: " << sf::Joystick::getButtonCount(id) << '\n';
}

int main()
{
    sf::RenderWindow window(sf::VideoMode({1280, 720}), "iOS joystick test");
    window.setFramerateLimit(60);

    for (unsigned int id = 0; id < sf::Joystick::Count; ++id)
    {
        if (sf::Joystick::isConnected(id))
            printController(id);
    }

    while (window.isOpen())
    {
        while (const std::optional event = window.pollEvent())
        {
            if (event->is<sf::Event::Closed>())
                window.close();
            else if (const auto* connected = event->getIf<sf::Event::JoystickConnected>())
                printController(connected->joystickId);
            else if (const auto* disconnected = event->getIf<sf::Event::JoystickDisconnected>())
                std::cout << "Disconnected " << disconnected->joystickId << '\n';
            else if (const auto* pressed = event->getIf<sf::Event::JoystickButtonPressed>())
                std::cout << "Joystick " << pressed->joystickId << ", button "
                          << pressed->button << " pressed\n";
            else if (const auto* released = event->getIf<sf::Event::JoystickButtonReleased>())
                std::cout << "Joystick " << released->joystickId << ", button "
                          << released->button << " released\n";
            else if (const auto* moved = event->getIf<sf::Event::JoystickMoved>())
                std::cout << "Joystick " << moved->joystickId << ", axis "
                          << static_cast<int>(moved->axis) << ": " << moved->position << '\n';
        }

        const bool pressed = sf::Joystick::isConnected(0) && sf::Joystick::isButtonPressed(0, 0);
        window.clear(pressed ? sf::Color::Green : sf::Color::Black);
        window.display();
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants