Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sf::WindowBase::pollEvent triggers input monitoring prompt on macOS even though it's not needed #2843

Open
3 tasks done
orazioedoardo opened this issue Dec 17, 2023 · 4 comments

Comments

@orazioedoardo
Copy link

orazioedoardo commented Dec 17, 2023

Prerequisite Checklist

Describe your issue here

As I understand, input monitoring permission is required when using sf::Keyboard::isKeyPressed since checking for key press makes possible to create a keylogger, however when just watching for events delivered to the process looking for sf::Event::KeyPressed events, no permission is needed.

I'm not familiar with SFML but by checking around with the code I believe that the permission prompt is triggered here and the function involved is IOHIDManagerCopyDevices. If I simply uncomment HIDInputManager::initializeKeyboard here and recompile SFML I can run the minimal example with no permission prompt.

Perhaps the keyboard should not be initialized when receiving keyboard events? As it stands, it's bad UX to open an app or game and seeing a spurious prompt requesting eavesdropping capabilities for no apparent reason as soon as a key is pressed, all while keyboard input works anyway.

Your Environment

  • OS / distro / window manager: macOS 13
  • SFML version: 2.6.1
  • Compiler / toolchain: clang
  • Special compiler / CMake flags: none

Steps to reproduce

  1. Compile and run the following code
  2. Focus on the window
  3. Press Esc on the keyboard
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode({1280, 720}), "Minimal, complete and verifiable example");
    window.setFramerateLimit(60);

    while (window.isOpen())
    {
        for (sf::Event event; window.pollEvent(event);)
        {
            if (event.type == sf::Event::KeyPressed && event.key.scancode == sf::Keyboard::Scan::Escape)
                window.close();
        }

        window.clear();
        window.display();
    }
}

Expected behavior

The window just closes.

Actual behavior

The window closes, but macOS prompts to grant input monitoring permission for the app (or Terminal in this case).

@ChrisThrasher
Copy link
Member

sf::priv::HIDInputManager::HIDInputManager() HIDInputManager.mm:719
sf::priv::HIDInputManager::HIDInputManager() HIDInputManager.mm:706
sf::priv::HIDInputManager::getInstance() HIDInputManager.mm:47
sf::priv::InputImpl::localize(sf::Keyboard::Scan) InputImpl.mm:140
sf::Keyboard::localize(sf::Keyboard::Scan) Keyboard.cpp:51
+[SFOpenGLView(keyboard) convertNSKeyEventToSFMLEvent:] SFOpenGLView+keyboard.mm:187
-[SFOpenGLView(keyboard) keyDown:] SFOpenGLView+keyboard.mm:87
-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] 0x000000018549e810
-[NSWindow(NSEventRouting) sendEvent:] 0x000000018549e45c
-[NSApplication(NSEventRouting) sendEvent:] 0x0000000185b4b9f4
-[SFApplication sendEvent:] SFApplication.m:260
+[SFApplication processEvent] SFApplication.m:49
-[SFWindowController processEvent] SFWindowController.mm:586
sf::priv::WindowImplCocoa::processEvents() WindowImplCocoa.mm:386
sf::priv::WindowImpl::popEvent(sf::Event &, bool) WindowImpl.cpp:181
sf::WindowBase::pollEvent(sf::Event &) WindowBase.cpp:162
main Island.cpp:181
start 0x00000001817390e0

I ran the Island example which doesn't use sf::Keyboard::isKeyPressed to get the whole stack trace that leads to initializeKeyboard being called. Not yet sure what to make of this information but I figured this was a good piece of data to have.

@Bambo-Borris
Copy link
Contributor

Often mentioned in the Discord is avoiding sf::Keyboard::isKeyPressed() for the very reason of this permissions check being something a lot of people would like to avoid.

@orazioedoardo
Copy link
Author

@Bambo-Borris The issue here is that the permission is triggered on sf::Event::KeyPressed too.

@orazioedoardo
Copy link
Author

Not yet sure what to make of this information but I figured this was a good piece of data to have.

@ChrisThrasher Well this was very useful.

HIDInputManager::localize() is being called to convert the scancode to the keycode. It calls HIDInputManager::getInstance().localize(code), getInstance() instantiates an HIDInputManager object, calling the constructor which in turn calls HIDInputManager::initializeKeyboard() as well as HIDInputManager::buildMappings().

The latter creates the m_scancodeToKeyMapping array (and the reverse too). Turns out that initializing the keyboard is not required to build the mappings, I get key codes regardless! As the comment says, the initializeKeyboard method creates the m_keys array which ends up being used by... the isKeyPressed method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Needs Work
Development

No branches or pull requests

4 participants