diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 677556042..f417c1e6f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,8 +18,8 @@ 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++, gcovr_options: '--gcov-executable="llvm-cov-$CLANG_VERSION gcov"' }
-        - { name: MacOS,                os: macos-latest }
-        - { name: MacOS Xcode,          os: macos-latest, flags: -GXcode }
+        - { name: MacOS,                os: macos-11 }
+        - { name: MacOS Xcode,          os: macos-11, flags: -GXcode }
         config:
         - { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE }
         - { name: Static, flags: -DBUILD_SHARED_LIBS=FALSE }
@@ -34,9 +34,9 @@ jobs:
         - platform: { name: Windows VS2022, os: windows-2022 }
           config: { name: Unity, flags: -DBUILD_SHARED_LIBS=TRUE -DCMAKE_UNITY_BUILD=ON }
           type: { name: Debug, flags: -DCMAKE_BUILD_TYPE=Debug -DSFML_ENABLE_COVERAGE=TRUE }
-        - platform: { name: MacOS, os: macos-latest }
+        - platform: { name: MacOS, os: macos-11 }
           config: { name: Frameworks, flags: -DSFML_BUILD_FRAMEWORKS=TRUE }
-        - platform: { name: MacOS, os: macos-latest }
+        - platform: { name: MacOS, 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-r23b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 }
diff --git a/extlibs/headers/drm/drm-common.c b/extlibs/headers/drm/drm-common.c
index 700914739..9a870054a 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;
@@ -285,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++)
 	{
@@ -349,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 824e7f9ba..8c2e90d42 100644
--- a/src/SFML/Window/DRM/DRMContext.cpp
+++ b/src/SFML/Window/DRM/DRMContext.cpp
@@ -91,14 +91,18 @@ void cleanup()
     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);
diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp
index bef1f0922..4427c747a 100644
--- a/src/SFML/Window/Unix/WindowImplX11.cpp
+++ b/src/SFML/Window/Unix/WindowImplX11.cpp
@@ -46,6 +46,7 @@
 #include <X11/keysym.h>
 
 #include <algorithm>
+#include <bitset>
 #include <cassert>
 #include <cstring>
 #include <fcntl.h>
@@ -77,6 +78,7 @@ namespace WindowsImplX11Impl
 {
 sf::priv::WindowImplX11*              fullscreenWindow = nullptr;
 std::vector<sf::priv::WindowImplX11*> allWindows;
+std::bitset<256>                      isKeyFiltered;
 std::recursive_mutex                  allWindowsMutex;
 sf::String                            windowManagerName;
 
@@ -88,23 +90,6 @@ constexpr unsigned long eventMask = FocusChangeMask | ButtonPressMask | ButtonRe
 
 constexpr 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)
 {
@@ -831,14 +816,61 @@ void WindowImplX11::processEvents()
 
     // Pick out the events that are interesting for this window
     while (XCheckIfEvent(m_display, &event, &checkEvent, reinterpret_cast<XPointer>(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<XPointer>(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
@@ -1731,33 +1763,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
-        auto it = std::find_if(m_events.begin(),
-                               m_events.end(),
-                               KeyRepeatFinder(windowEvent.xkey.keycode, windowEvent.xkey.time));
-
-        if (it != m_events.end())
-        {
-            // If we don't want repeated events, remove the next KeyPress from the queue
-            if (!m_keyRepeat)
-                m_events.erase(it);
-
-            // 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)
     {
@@ -1913,10 +1918,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)
diff --git a/src/SFML/Window/Unix/WindowImplX11.hpp b/src/SFML/Window/Unix/WindowImplX11.hpp
index 1bd9c7404..66b8f6629 100644
--- a/src/SFML/Window/Unix/WindowImplX11.hpp
+++ b/src/SFML/Window/Unix/WindowImplX11.hpp
@@ -297,16 +297,15 @@ 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<XEvent> 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
+    ::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)