From 1d12c563323bc3ce09823ca35ba66fba6491fc69 Mon Sep 17 00:00:00 2001 From: Subs Date: Thu, 13 Oct 2022 17:02:26 +0200 Subject: [PATCH 1/5] [DRM] Use a GFX card with connected monitors --- extlibs/headers/drm/drm-common.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/extlibs/headers/drm/drm-common.c b/extlibs/headers/drm/drm-common.c index 700914739..7fdd422b0 100644 --- a/extlibs/headers/drm/drm-common.c +++ b/extlibs/headers/drm/drm-common.c @@ -170,6 +170,26 @@ static int get_resources(int fd, drmModeRes **resources) return 0; } +static int has_monitor_connected(int fd, drmModeRes* resources) +{ + int i; + drmModeConnector *connector; + for (i = 0; i < resources->count_connectors; i++) + { + connector = drmModeGetConnector(fd, resources->connectors[i]); + if (connector->connection == DRM_MODE_CONNECTED) + { + /* There is a monitor connected */ + drmModeFreeConnector(connector); + connector = NULL; + return 1; + } + drmModeFreeConnector(connector); + connector = NULL; + } + return 0; +} + #define MAX_DRM_DEVICES 64 static int find_drm_device(drmModeRes **resources) @@ -199,7 +219,11 @@ static int find_drm_device(drmModeRes **resources) if (fd < 0) continue; ret = get_resources(fd, resources); - if (!ret) + if(getenv("SFML_DRM_DEBUG")) + { + printf("DRM device used: %d\n", i); + } + if(!ret && has_monitor_connected(fd, *resources) != 0) break; close(fd); fd = -1; From 65ef0619c8e8415b2a89fbe327844a7e9af88a79 Mon Sep 17 00:00:00 2001 From: Subs Date: Thu, 13 Oct 2022 16:21:56 +0200 Subject: [PATCH 2/5] [DRM] Use the current resolution In order to prevent an unnecessary modeswitch and use the current TTY resolution, don't use the preferred mode reported by DRM. This is useful for people who set a custom resolution at boot and are forced into the monitor's preferred resolution when a SFML DRM app starts --- extlibs/headers/drm/drm-common.c | 40 ++++++++---------------------- src/SFML/Window/DRM/DRMContext.cpp | 20 +++++++++------ 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/extlibs/headers/drm/drm-common.c b/extlibs/headers/drm/drm-common.c index 7fdd422b0..9a870054a 100644 --- a/extlibs/headers/drm/drm-common.c +++ b/extlibs/headers/drm/drm-common.c @@ -309,34 +309,6 @@ int init_drm(struct drm *drm, const char *device, const char *mode_str, printf("requested mode not found, using default mode!\n"); } - /* find preferred mode or the highest resolution mode: */ - if (!drm->mode) - { - for (i = 0, area = 0; i < connector->count_modes; i++) - { - drmModeModeInfo *current_mode = &connector->modes[i]; - - if (current_mode->type & DRM_MODE_TYPE_PREFERRED) - { - drm->mode = current_mode; - break; - } - - int current_area = current_mode->hdisplay * current_mode->vdisplay; - if (current_area > area) - { - drm->mode = current_mode; - area = current_area; - } - } - } - - if (!drm->mode) - { - printf("could not find mode!\n"); - return -1; - } - /* find encoder: */ for (i = 0; i < resources->count_encoders; i++) { @@ -373,7 +345,17 @@ int init_drm(struct drm *drm, const char *device, const char *mode_str, // get original display mode so we can restore display mode after program exits drm->original_crtc = drmModeGetCrtc(drm->fd, drm->crtc_id); - if(getenv("SFML_DRM_DEBUG")) + /* Let's use the current mode rather than the preferred one if the user didn't + * specify a mode with env vars + */ + if (!drm->mode) + { + if(getenv("SFML_DRM_DEBUG")) + printf("DRM using the current mode\n"); + drm->mode = &(drm->original_crtc->mode); + } + + if (getenv("SFML_DRM_DEBUG")) { printf("DRM Mode used: %s@%d\n", drm->mode->name, drm->mode->vrefresh); } diff --git a/src/SFML/Window/DRM/DRMContext.cpp b/src/SFML/Window/DRM/DRMContext.cpp index d73409413..d92a10a4e 100644 --- a/src/SFML/Window/DRM/DRMContext.cpp +++ b/src/SFML/Window/DRM/DRMContext.cpp @@ -94,14 +94,18 @@ namespace if (!initialized) return; - drmModeSetCrtc(drmNode.fd, - drmNode.original_crtc->crtc_id, - drmNode.original_crtc->buffer_id, - drmNode.original_crtc->x, - drmNode.original_crtc->y, - &drmNode.connector_id, - 1, - &drmNode.original_crtc->mode); + /* Avoid a modeswitch if possible */ + if (drmNode.mode != &drmNode.original_crtc->mode) + drmModeSetCrtc(drmNode.fd, + drmNode.original_crtc->crtc_id, + drmNode.original_crtc->buffer_id, + drmNode.original_crtc->x, + drmNode.original_crtc->y, + &drmNode.connector_id, + 1, + &drmNode.original_crtc->mode); + else if (getenv("SFML_DRM_DEBUG")) + printf("DRM keeping the same mode since using the original one\n"); drmModeFreeConnector(drmNode.saved_connector); drmModeFreeEncoder(drmNode.saved_encoder); From b11328f386f58e8c6ce66ee9051b1524d5cae61e Mon Sep 17 00:00:00 2001 From: kimci86 Date: Mon, 10 Oct 2022 00:31:53 +0200 Subject: [PATCH 3/5] Fix duplicated KeyPressed events with X11 Co-authored-by: Edgaru089 --- src/SFML/Window/Unix/WindowImplX11.cpp | 28 +++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 59a0fbace..b78548717 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // defines min/max as macros, so has to come before that #include #include #include @@ -68,6 +69,7 @@ namespace { sf::priv::WindowImplX11* fullscreenWindow = NULL; std::vector allWindows; + std::bitset<256> isKeyFiltered; sf::Mutex allWindowsMutex; sf::String windowManagerName; @@ -1901,10 +1903,30 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) event.key.control = windowEvent.xkey.state & ControlMask; event.key.shift = windowEvent.xkey.state & ShiftMask; event.key.system = windowEvent.xkey.state & Mod4Mask; - pushEvent(event); - // Generate a TextEntered event - if (!XFilterEvent(&windowEvent, None)) + const bool filtered = XFilterEvent(&windowEvent, None); + + // Generate a KeyPressed event if needed + if (filtered) + { + pushEvent(event); + isKeyFiltered.set(windowEvent.xkey.keycode); + } + else + { + // Push a KeyPressed event if the key has never been filtered before + // (a KeyPressed event would have already been pushed if it had been filtered). + // + // Some dummy IMs (like the built-in one you get by setting XMODIFIERS=@im=none) + // never filter events away, and we have to take care of that. + // + // In addition, ignore text-only KeyPress events generated by IMs (with keycode set to 0). + if (!isKeyFiltered.test(windowEvent.xkey.keycode) && windowEvent.xkey.keycode != 0) + pushEvent(event); + } + + // Generate TextEntered events if needed + if (!filtered) { #ifdef X_HAVE_UTF8_STRING if (m_inputContext) From f796205a2173f8dfbf75b0a19b3a2de64796cdb8 Mon Sep 17 00:00:00 2001 From: kimci86 Date: Tue, 11 Oct 2022 22:46:46 +0200 Subject: [PATCH 4/5] Process events in order to avoid X11 input method being confused --- src/SFML/Window/Unix/WindowImplX11.cpp | 105 +++++++++++++------------ src/SFML/Window/Unix/WindowImplX11.hpp | 39 +++++---- 2 files changed, 73 insertions(+), 71 deletions(-) diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index b78548717..fb2a6ff77 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -84,21 +84,6 @@ namespace static const unsigned int maxTrialsCount = 5; - // Predicate we use to find key repeat events in processEvent - struct KeyRepeatFinder - { - KeyRepeatFinder(unsigned int initalKeycode, Time initialTime) : keycode(initalKeycode), time(initialTime) {} - - // Predicate operator that checks event type, keycode and timestamp - bool operator()(const XEvent& event) - { - return ((event.type == KeyPress) && (event.xkey.keycode == keycode) && (event.xkey.time - time < 2)); - } - - unsigned int keycode; - Time time; - }; - // Filter the events received by windows (only allow those matching a specific window) Bool checkEvent(::Display*, XEvent* event, XPointer userData) { @@ -834,14 +819,61 @@ void WindowImplX11::processEvents() // Pick out the events that are interesting for this window while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast(m_window))) - m_events.push_back(event); - - // Handle the events for this window that we just picked out - while (!m_events.empty()) { - event = m_events.front(); - m_events.pop_front(); - processEvent(event); + // This function implements a workaround to properly discard + // repeated key events when necessary. The problem is that the + // system's key events policy doesn't match SFML's one: X server will generate + // both repeated KeyPress and KeyRelease events when maintaining a key down, while + // SFML only wants repeated KeyPress events. Thus, we have to: + // - Discard duplicated KeyRelease events when m_keyRepeat is true + // - Discard both duplicated KeyPress and KeyRelease events when m_keyRepeat is false + + bool processThisEvent = true; + + // Detect repeated key events + while (event.type == KeyRelease) + { + XEvent nextEvent; + if (XCheckIfEvent(m_display, &nextEvent, checkEvent, reinterpret_cast(m_window))) + { + if ((nextEvent.type == KeyPress) && (nextEvent.xkey.keycode == event.xkey.keycode) && + (event.xkey.time <= nextEvent.xkey.time) && (nextEvent.xkey.time <= event.xkey.time + 1)) + { + // This sequence of events comes from maintaining a key down + if (m_keyRepeat) + { + // Ignore the KeyRelease event and process the KeyPress event + event = nextEvent; + break; + } + else + { + // Ignore both events + processThisEvent = false; + break; + } + } + else + { + // This sequence of events does not come from maintaining a key down, + // so process the KeyRelease event normally, + processEvent(event); + // but loop because the next event can be the first half + // of a sequence coming from maintaining a key down. + event = nextEvent; + } + } + else + { + // No event after this KeyRelease event so assume it can be processed. + break; + } + } + + if (processThisEvent) + { + processEvent(event); + } } // Process clipboard window events @@ -1725,35 +1757,6 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) { using namespace WindowsImplX11Impl; - // This function implements a workaround to properly discard - // repeated key events when necessary. The problem is that the - // system's key events policy doesn't match SFML's one: X server will generate - // both repeated KeyPress and KeyRelease events when maintaining a key down, while - // SFML only wants repeated KeyPress events. Thus, we have to: - // - Discard duplicated KeyRelease events when KeyRepeatEnabled is true - // - Discard both duplicated KeyPress and KeyRelease events when KeyRepeatEnabled is false - - // Detect repeated key events - if (windowEvent.type == KeyRelease) - { - // Find the next KeyPress event with matching keycode and time - std::deque::iterator iter = std::find_if( - m_events.begin(), - m_events.end(), - KeyRepeatFinder(windowEvent.xkey.keycode, windowEvent.xkey.time) - ); - - if (iter != m_events.end()) - { - // If we don't want repeated events, remove the next KeyPress from the queue - if (!m_keyRepeat) - m_events.erase(iter); - - // This KeyRelease is a repeated event and we don't want it - return false; - } - } - // Convert the X11 event to a sf::Event switch (windowEvent.type) { diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp index a998d8d60..aca1cd04f 100644 --- a/src/SFML/Window/Unix/WindowImplX11.hpp +++ b/src/SFML/Window/Unix/WindowImplX11.hpp @@ -300,26 +300,25 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - ::Window m_window; ///< X identifier defining our window - ::Display* m_display; ///< Pointer to the display - int m_screen; ///< Screen identifier - XIM m_inputMethod; ///< Input method linked to the X display - XIC m_inputContext; ///< Input context used to get unicode input in our window - std::deque m_events; ///< Queue we use to store pending events for this window - bool m_isExternal; ///< Tell whether the window has been created externally or by SFML - RRMode m_oldVideoMode; ///< Video mode in use before we switch to fullscreen - RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen - ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one - ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid - bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? - Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) - bool m_useSizeHints; ///< Is the size of the window fixed with size hints? - bool m_fullscreen; ///< Is the window in fullscreen? - bool m_cursorGrabbed; ///< Is the mouse cursor trapped? - bool m_windowMapped; ///< Has the window been mapped by the window manager? - Pixmap m_iconPixmap; ///< The current icon pixmap if in use - Pixmap m_iconMaskPixmap; ///< The current icon mask pixmap if in use - ::Time m_lastInputTime; ///< Last time we received user input + ::Window m_window; ///< X identifier defining our window + ::Display* m_display; ///< Pointer to the display + int m_screen; ///< Screen identifier + XIM m_inputMethod; ///< Input method linked to the X display + XIC m_inputContext; ///< Input context used to get unicode input in our window + bool m_isExternal; ///< Tell whether the window has been created externally or by SFML + RRMode m_oldVideoMode; ///< Video mode in use before we switch to fullscreen + RRCrtc m_oldRRCrtc; ///< RRCrtc in use before we switch to fullscreen + ::Cursor m_hiddenCursor; ///< As X11 doesn't provide cursor hiding, we must create a transparent one + ::Cursor m_lastCursor; ///< Last cursor used -- this data is not owned by the window and is required to be always valid + bool m_keyRepeat; ///< Is the KeyRepeat feature enabled? + Vector2i m_previousSize; ///< Previous size of the window, to find if a ConfigureNotify event is a resize event (could be a move event only) + bool m_useSizeHints; ///< Is the size of the window fixed with size hints? + bool m_fullscreen; ///< Is the window in fullscreen? + bool m_cursorGrabbed; ///< Is the mouse cursor trapped? + bool m_windowMapped; ///< Has the window been mapped by the window manager? + Pixmap m_iconPixmap; ///< The current icon pixmap if in use + Pixmap m_iconMaskPixmap; ///< The current icon mask pixmap if in use + ::Time m_lastInputTime; ///< Last time we received user input }; } // namespace priv From c1064bae925693667c7a34cad264bd5ea3ece177 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Tue, 25 Oct 2022 09:42:08 -0600 Subject: [PATCH 5/5] Lock 2.6.x branch to macOS 11 images The macos-latest image recently upgraded to macOS 12. This includes the iOS 16 SDK which deprecates some features which we're using. The short term fix is to just keep using macOS 11 for 2.6.x. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cce2bbd9..52896d8d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - { name: Windows VS2022 Clang, os: windows-2022, flags: -T ClangCL } - { name: Linux GCC, os: ubuntu-latest } - { name: Linux Clang, os: ubuntu-latest, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } - - { name: MacOS XCode, os: macos-latest } + - { name: MacOS XCode, os: macos-11 } config: - { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE } - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE } @@ -24,9 +24,9 @@ jobs: include: - platform: { name: Windows VS2022, os: windows-2022 } config: { name: Unity, flags: -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON } - - platform: { name: MacOS XCode, os: macos-latest } + - platform: { name: MacOS XCode, os: macos-11 } config: { name: Frameworks, flags: -DSFML_BUILD_FRAMEWORKS=TRUE } - - platform: { name: MacOS XCode, os: macos-latest } + - platform: { name: MacOS XCode, os: macos-11 } config: { name: iOS, flags: -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchains/iOS.toolchain.cmake -DIOS_PLATFORM=SIMULATOR } - platform: { name: Android, os: ubuntu-latest } config: { name: x86, flags: -DCMAKE_ANDROID_ARCH_ABI=x86 -DCMAKE_SYSTEM_NAME=Android -DSFML_BUILD_TEST_SUITE=FALSE -DCMAKE_ANDROID_NDK=$GITHUB_WORKSPACE/android-ndk-r18b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 }