summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2023-06-08 11:39:28 +0200
committerFlorian Bruhin <me@the-compiler.org>2023-06-08 11:39:28 +0200
commit30bfd90e59357aa6e4aee20f2e21d787eb4ee4b8 (patch)
tree1a953b699923b6d05c77c1a26e80ff76c3d2110e
parent16093eaf981cec0d71331679302470cad575d5af (diff)
downloadqutebrowser-30bfd90e59357aa6e4aee20f2e21d787eb4ee4b8.tar.gz
qutebrowser-30bfd90e59357aa6e4aee20f2e21d787eb4ee4b8.zip
Add initial nativeeventfilter
See #7732
-rw-r--r--qutebrowser/app.py3
-rw-r--r--qutebrowser/misc/nativeeventfilter.py127
2 files changed, 129 insertions, 1 deletions
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
index db7eea608..664fb4fa9 100644
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -65,7 +65,7 @@ from qutebrowser.keyinput import macros, eventfilter
from qutebrowser.mainwindow import mainwindow, prompt, windowundo
from qutebrowser.misc import (ipc, savemanager, sessions, crashsignal,
earlyinit, sql, cmdhistory, backendproblem,
- objects, quitter)
+ objects, quitter, nativeeventfilter)
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
resources, usertypes, standarddir,
error, qtutils, debug)
@@ -523,6 +523,7 @@ def _init_modules(*, args):
log.init.debug("Misc initialization...")
macros.init()
windowundo.init()
+ nativeeventfilter.init()
class Application(QApplication):
diff --git a/qutebrowser/misc/nativeeventfilter.py b/qutebrowser/misc/nativeeventfilter.py
new file mode 100644
index 000000000..feb0f7301
--- /dev/null
+++ b/qutebrowser/misc/nativeeventfilter.py
@@ -0,0 +1,127 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2023 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+#
+# This file is part of qutebrowser.
+#
+# qutebrowser is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# qutebrowser is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with qutebrowser. If not, see <https://www.gnu.org/licenses/>.
+
+"""Native Qt event filter.
+
+This entire file is a giant WORKAROUND for https://bugreports.qt.io/browse/QTBUG-114334.
+"""
+
+import ctypes
+
+from qutebrowser.qt.core import QAbstractNativeEventFilter
+
+from qutebrowser.misc import objects
+
+
+# Needs to be saved to avoid garbage collection
+_instance = None
+
+
+class xcb_generic_event_t(ctypes.Structure):
+ """https://xcb.freedesktop.org/manual/structxcb__generic__event__t.html"""
+ _fields_ = [
+ ("response_type", ctypes.c_uint8),
+ ("pad0", ctypes.c_uint8),
+ ("sequence", ctypes.c_uint16),
+ ("pad", ctypes.c_uint32 * 7),
+ ("full_sequence", ctypes.c_uint32),
+ ]
+
+class xcb_ge_generic_event_t(ctypes.Structure):
+ """https://xcb.freedesktop.org/manual/structxcb__ge__generic__event__t.html"""
+ _fields_ = [
+ ("response_type", ctypes.c_uint8),
+ ("extension", ctypes.c_uint8),
+ ("sequence", ctypes.c_uint16),
+ ("length", ctypes.c_uint32),
+ ("event_type", ctypes.c_uint16),
+ ("pad0", ctypes.c_uint8 * 22),
+ ("full_sequence", ctypes.c_uint32),
+ ]
+
+
+_XCB_GE_GENERIC = 35
+_PROBLEMATIC_XINPUT_EVENTS = [
+ 27, # XCB_INPUT_GESTURE_PINCH_BEGIN
+ 28, # XCB_INPUT_GESTURE_PINCH_UPDATE
+ 29, # XCB_INPUT_GESTURE_PINCH_END
+ 30, # XCB_INPUT_GESTURE_SWIPE_BEGIN
+ 31, # XCB_INPUT_GESTURE_SWIPE_UPDATE
+ 32, # XCB_INPUT_GESTURE_SWIPE_END
+]
+
+class xcb_query_extension_reply_t(ctypes.Structure):
+ """https://xcb.freedesktop.org/manual/structxcb__query__extension__reply__t.html"""
+ _fields_ = [
+ ('response_type', ctypes.c_uint8),
+ ('pad0', ctypes.c_uint8),
+ ('sequence', ctypes.c_uint16),
+ ('length', ctypes.c_uint32),
+ ('present', ctypes.c_uint8),
+ ('major_opcode', ctypes.c_uint8),
+ ('first_event', ctypes.c_uint8),
+ ('first_error', ctypes.c_uint8),
+ ]
+
+
+class NativeEventFilter(QAbstractNativeEventFilter):
+
+ def __init__(self) -> None:
+ super().__init__()
+ xcb = ctypes.cdll.LoadLibrary('libxcb.so.1')
+ xcb.xcb_connect.restype = ctypes.POINTER(ctypes.c_void_p)
+ xcb.xcb_query_extension_reply.restype = ctypes.POINTER(xcb_query_extension_reply_t)
+
+ conn = xcb.xcb_connect(None, None)
+ assert conn
+ assert not xcb.xcb_connection_has_error(conn)
+
+ # Get major opcode ID of Xinput extension
+ name = b'XInputExtension'
+ cookie = xcb.xcb_query_extension(conn, len(name), name)
+ reply = xcb.xcb_query_extension_reply(conn, cookie, None)
+ assert reply
+
+ if not reply.contents.present:
+ self.xinput_opcode = None
+ else:
+ self.xinput_opcode = reply.contents.major_opcode
+
+ xcb.xcb_disconnect(conn)
+
+ def nativeEventFilter(self, evtype: bytes, message: int) -> Tuple[bool, int]:
+ assert evtype == b'xcb_generic_event_t', evtype
+ event = ctypes.cast(int(message), ctypes.POINTER(xcb_generic_event_t)).contents
+
+ if event.response_type == _XCB_GE_GENERIC:
+ event = ctypes.cast(int(message), ctypes.POINTER(xcb_ge_generic_event_t)).contents
+ if (
+ event.extension == self.xinput_opcode and
+ event.event_type in _PROBLEMATIC_XINPUT_EVENTS
+ ):
+ print("Ignoring problematic XInput event", event.event_type)
+ return (True, 0)
+
+ return (False, 0)
+
+
+def init() -> None:
+ global _instance
+ _instance = NativeEventFilter()
+ objects.qapp.installNativeEventFilter(_instance)