diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 6b05f369a..db50f0425 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -33,9 +33,9 @@ import faulthandler import json from PyQt5.QtWidgets import QApplication, QDialog, QMessageBox -from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QCursor +from PyQt5.QtGui import QDesktopServices, QPixmap, QIcon, QCursor, QWindow from PyQt5.QtCore import (pyqtSlot, qInstallMessageHandler, QTimer, QUrl, - QObject, Qt, QSocketNotifier) + QObject, Qt, QSocketNotifier, QEvent) try: import hunter except ImportError: @@ -52,7 +52,6 @@ from qutebrowser.mainwindow import mainwindow from qutebrowser.misc import (crashdialog, readline, ipc, earlyinit, savemanager, sessions) from qutebrowser.misc import utilcmds # pylint: disable=unused-import -from qutebrowser.keyinput import modeman from qutebrowser.utils import (log, version, message, utils, qtutils, urlutils, objreg, usertypes, standarddir) # We import utilcmds to run the cmdutils.register decorators. @@ -143,7 +142,7 @@ class Application(QApplication): QTimer.singleShot(0, self._process_args) log.init.debug("Initializing eventfilter...") - self._event_filter = modeman.EventFilter(self) + self._event_filter = EventFilter(self) self.installEventFilter(self._event_filter) log.init.debug("Connecting signals...") @@ -977,3 +976,92 @@ class Application(QApplication): print("Now logging late shutdown.", file=sys.stderr) hunter.trace() super().exit(status) + + +class EventFilter(QObject): + + """Global Qt event filter. + + Attributes: + _activated: Whether the EventFilter is currently active. + _handlers; A {QEvent.Type: callable} dict with the handlers for an + event. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self._activated = True + self._handlers = { + QEvent.MouseButtonDblClick: self._handle_mouse_event, + QEvent.MouseButtonPress: self._handle_mouse_event, + QEvent.MouseButtonRelease: self._handle_mouse_event, + QEvent.MouseMove: self._handle_mouse_event, + QEvent.KeyPress: self._handle_key_event, + QEvent.KeyRelease: self._handle_key_event, + } + + def _handle_key_event(self, event): + """Handle a key press/release event. + + Args: + event: The QEvent which is about to be delivered. + + Return: + True if the event should be filtered, False if it's passed through. + """ + qapp = QApplication.instance() + if qapp.activeWindow() not in objreg.window_registry.values(): + # Some other window (print dialog, etc.) is focused so we pass the + # event through. + return False + try: + man = objreg.get('mode-manager', scope='window', window='current') + return man.eventFilter(event) + except objreg.RegistryUnavailableError: + # No window available yet, or not a MainWindow + return False + + def _handle_mouse_event(self, _event): + """Handle a mouse event. + + Args: + _event: The QEvent which is about to be delivered. + + Return: + True if the event should be filtered, False if it's passed through. + """ + if QApplication.instance().overrideCursor() is None: + # Mouse cursor shown -> don't filter event + return False + else: + # Mouse cursor hidden -> filter event + return True + + def eventFilter(self, obj, event): + """Handle an event. + + Args: + obj: The object which will get the event. + event: The QEvent which is about to be delivered. + + Return: + True if the event should be filtered, False if it's passed through. + """ + try: + if not self._activated: + return False + if not isinstance(obj, QWindow): + # We already handled this same event at some point earlier, so + # we're not interested in it anymore. + return False + try: + handler = self._handlers[event.type()] + except KeyError: + return False + else: + return handler(event) + except: + # If there is an exception in here and we leave the eventfilter + # activated, we'll get an infinite loop and a stack overflow. + self._activated = False + raise diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 00c0a4d73..7d77f872c 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -21,7 +21,6 @@ import functools -from PyQt5.QtGui import QWindow from PyQt5.QtCore import pyqtSignal, Qt, QObject, QEvent from PyQt5.QtWidgets import QApplication from PyQt5.QtWebKitWidgets import QWebView @@ -120,56 +119,6 @@ def maybe_leave(win_id, mode, reason=None): log.modes.debug("{} (leave reason: {})".format(e, reason)) -class EventFilter(QObject): - - """Event filter which passes the event to the current ModeManager.""" - - def __init__(self, parent=None): - super().__init__(parent) - self._activated = True - - def eventFilter(self, obj, event): - """Forward events to the correct modeman.""" - try: - qapp = QApplication.instance() - if not self._activated: - return False - if event.type() in [QEvent.MouseButtonDblClick, - QEvent.MouseButtonPress, - QEvent.MouseButtonRelease, - QEvent.MouseMove]: - if qapp.overrideCursor() is None: - # Mouse cursor shown -> don't filter event - return False - else: - # Mouse cursor hidden -> filter event - return True - elif event.type() not in [QEvent.KeyPress, QEvent.KeyRelease]: - # We're not interested in non-key-events so we pass them - # through. - return False - if not isinstance(obj, QWindow): - # We already handled this same event at some point earlier, so - # we're not interested in it anymore. - return False - if qapp.activeWindow() not in objreg.window_registry.values(): - # Some other window (print dialog, etc.) is focused so we pass - # the event through. - return False - try: - modeman = objreg.get('mode-manager', scope='window', - window='current') - return modeman.eventFilter(event) - except objreg.RegistryUnavailableError: - # No window available yet, or not a MainWindow - return False - except: - # If there is an exception in here and we leave the eventfilter - # activated, we'll get an infinite loop and a stack overflow. - self._activated = False - raise - - class ModeManager(QObject): """Manager for keyboard modes.