1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
# 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_ge_generic_event_t(ctypes.Structure):
"""See https://xcb.freedesktop.org/manual/structxcb__ge__generic__event__t.html.
Also used for xcb_generic_event_t as the structures overlap:
https://xcb.freedesktop.org/manual/structxcb__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]:
# We're only installed when the platform plugin is xcb
assert evtype == b'xcb_generic_event_t', evtype
# We cast to xcb_ge_generic_event_t, which overlaps with xcb_generic_event_t.
# .extension and .event_type will only make sense if this is an
# XCB_GE_GENERIC event, but this is the first thing we check in the 'if'
# below anyways.
event = ctypes.cast(int(message), ctypes.POINTER(xcb_ge_generic_event_t)).contents
if (
event.response_type == _XCB_GE_GENERIC and
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)
|