Add improved checks for Nouveau/Wayland for QtWebEngine
Closes #2368 Closes #2932 See #2335
This commit is contained in:
parent
ca4a997559
commit
093f34183c
@ -179,6 +179,7 @@
|
||||
|<<fonts.web.size.default_fixed,fonts.web.size.default_fixed>>|The default font size for fixed-pitch text.
|
||||
|<<fonts.web.size.minimum,fonts.web.size.minimum>>|The hard minimum font size.
|
||||
|<<fonts.web.size.minimum_logical,fonts.web.size.minimum_logical>>|The minimum logical font size that is applied when zooming out.
|
||||
|<<force_software_rendering,force_software_rendering>>|Force software rendering for QtWebEngine.
|
||||
|<<hints.auto_follow,hints.auto_follow>>|Controls when a hint can be automatically followed without pressing Enter.
|
||||
|<<hints.auto_follow_timeout,hints.auto_follow_timeout>>|A timeout (in milliseconds) to ignore normal-mode key bindings after a successful auto-follow.
|
||||
|<<hints.border,hints.border>>|CSS border value for hints.
|
||||
@ -2262,6 +2263,22 @@ Type: <<types,Int>>
|
||||
|
||||
Default: +pass:[6]+
|
||||
|
||||
[[force_software_rendering]]
|
||||
=== force_software_rendering
|
||||
Force software rendering for QtWebEngine.
|
||||
This is needed for QtWebEngine to work with Nouveau drivers.
|
||||
|
||||
Type: <<types,Bool>>
|
||||
|
||||
Valid values:
|
||||
|
||||
* +true+
|
||||
* +false+
|
||||
|
||||
Default: +pass:[false]+
|
||||
|
||||
This setting is only available with the QtWebEngine backend.
|
||||
|
||||
[[hints.auto_follow]]
|
||||
=== hints.auto_follow
|
||||
Controls when a hint can be automatically followed without pressing Enter.
|
||||
|
@ -54,7 +54,8 @@ from qutebrowser.browser.webkit.network import networkmanager
|
||||
from qutebrowser.keyinput import macros
|
||||
from qutebrowser.mainwindow import mainwindow, prompt
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
crashsignal, earlyinit, sql, cmdhistory)
|
||||
crashsignal, earlyinit, sql, cmdhistory,
|
||||
backendproblem)
|
||||
from qutebrowser.utils import (log, version, message, utils, urlutils, objreg,
|
||||
usertypes, standarddir, error)
|
||||
# pylint: disable=unused-import
|
||||
@ -389,14 +390,17 @@ def _init_modules(args, crash_handler):
|
||||
crash_handler: The CrashHandler instance.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
log.init.debug("Initializing prompts...")
|
||||
prompt.init()
|
||||
|
||||
log.init.debug("Initializing save manager...")
|
||||
save_manager = savemanager.SaveManager(qApp)
|
||||
objreg.register('save-manager', save_manager)
|
||||
configinit.late_init(save_manager)
|
||||
|
||||
log.init.debug("Checking backend requirements...")
|
||||
backendproblem.init()
|
||||
|
||||
log.init.debug("Initializing prompts...")
|
||||
prompt.init()
|
||||
|
||||
log.init.debug("Initializing network...")
|
||||
networkmanager.init()
|
||||
|
||||
|
@ -38,7 +38,7 @@ from qutebrowser.browser.webengine import (webview, webengineelem, tabhistory,
|
||||
webenginesettings)
|
||||
from qutebrowser.misc import miscwidgets
|
||||
from qutebrowser.utils import (usertypes, qtutils, log, javascript, utils,
|
||||
message, objreg, jinja, debug, version)
|
||||
message, objreg, jinja, debug)
|
||||
|
||||
|
||||
_qute_scheme_handler = None
|
||||
@ -50,16 +50,8 @@ def init():
|
||||
# won't work...
|
||||
# https://www.riverbankcomputing.com/pipermail/pyqt/2016-September/038075.html
|
||||
global _qute_scheme_handler
|
||||
|
||||
app = QApplication.instance()
|
||||
|
||||
software_rendering = (os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or
|
||||
'QT_XCB_FORCE_SOFTWARE_OPENGL' in os.environ)
|
||||
if version.opengl_vendor() == 'nouveau' and not software_rendering:
|
||||
# FIXME:qtwebengine display something more sophisticated here
|
||||
raise browsertab.WebTabError(
|
||||
"QtWebEngine is not supported with Nouveau graphics (unless "
|
||||
"QT_XCB_FORCE_SOFTWARE_OPENGL is set as environment variable).")
|
||||
|
||||
log.init.debug("Initializing qute://* handler...")
|
||||
_qute_scheme_handler = webenginequtescheme.QuteSchemeHandler(parent=app)
|
||||
_qute_scheme_handler.install(webenginesettings.default_profile)
|
||||
|
@ -101,6 +101,15 @@ qt_args:
|
||||
https://peter.sh/experiments/chromium-command-line-switches/ for a list)
|
||||
will work.
|
||||
|
||||
force_software_rendering:
|
||||
type: Bool
|
||||
default: false
|
||||
backend: QtWebEngine
|
||||
desc: >-
|
||||
Force software rendering for QtWebEngine.
|
||||
|
||||
This is needed for QtWebEngine to work with Nouveau drivers.
|
||||
|
||||
backend:
|
||||
type:
|
||||
name: String
|
||||
|
182
qutebrowser/misc/backendproblem.py
Normal file
182
qutebrowser/misc/backendproblem.py
Normal file
@ -0,0 +1,182 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2017 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Dialogs shown when there was a problem with a backend choice."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import functools
|
||||
|
||||
import attr
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import (QApplication, QDialog, QPushButton, QHBoxLayout,
|
||||
QVBoxLayout, QLabel)
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import usertypes, objreg, version
|
||||
from qutebrowser.misc import objects
|
||||
|
||||
|
||||
_Result = usertypes.enum('_Result', ['quit', 'restart'], is_int=True,
|
||||
start=QDialog.Accepted + 1)
|
||||
|
||||
|
||||
@attr.s
|
||||
class _Button:
|
||||
|
||||
"""A button passed to BackendProblemDialog."""
|
||||
|
||||
text = attr.ib()
|
||||
setting = attr.ib()
|
||||
value = attr.ib()
|
||||
default = attr.ib(default=False)
|
||||
|
||||
|
||||
class _Dialog(QDialog):
|
||||
|
||||
"""A dialog which gets shown if there are issues with the backend."""
|
||||
|
||||
def __init__(self, because, text, backend, buttons=None, parent=None):
|
||||
super().__init__(parent)
|
||||
vbox = QVBoxLayout(self)
|
||||
|
||||
other_backend = {
|
||||
usertypes.Backend.QtWebKit: usertypes.Backend.QtWebEngine,
|
||||
usertypes.Backend.QtWebEngine: usertypes.Backend.QtWebKit,
|
||||
}[backend]
|
||||
other_setting = other_backend.name.lower()[2:]
|
||||
|
||||
label = QLabel(
|
||||
"<b>Failed to start with the {backend} backend!</b>"
|
||||
"<p>qutebrowser tried to start with the {backend} backend but "
|
||||
"failed because {because}.</p>{text}"
|
||||
"<p><b>Forcing the {other_backend.name} backend</b></p>"
|
||||
"<p>This forces usage of the {other_backend.name} backend. "
|
||||
"This sets the <i>backend = '{other_setting}'</i> setting "
|
||||
"(if you have a <i>config.py</i> file, you'll need to set "
|
||||
"this manually).</p>".format(
|
||||
backend=backend.name, because=because, text=text,
|
||||
other_backend=other_backend, other_setting=other_setting),
|
||||
wordWrap=True)
|
||||
label.setTextFormat(Qt.RichText)
|
||||
vbox.addWidget(label)
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
buttons = [] if buttons is None else buttons
|
||||
|
||||
quit_button = QPushButton("Quit")
|
||||
quit_button.clicked.connect(lambda: self.done(_Result.quit))
|
||||
hbox.addWidget(quit_button)
|
||||
|
||||
backend_button = QPushButton("Force {} backend".format(
|
||||
other_backend.name))
|
||||
backend_button.clicked.connect(functools.partial(
|
||||
self._change_setting, 'backend', other_setting))
|
||||
hbox.addWidget(backend_button)
|
||||
|
||||
for button in buttons:
|
||||
btn = QPushButton(button.text, default=button.default)
|
||||
btn.clicked.connect(functools.partial(
|
||||
self._change_setting, button.setting, button.value))
|
||||
hbox.addWidget(btn)
|
||||
|
||||
vbox.addLayout(hbox)
|
||||
|
||||
def _change_setting(self, setting, value):
|
||||
"""Change the given setting and restart."""
|
||||
config.instance.set_obj(setting, value, save_yaml=True)
|
||||
self.done(_Result.restart)
|
||||
|
||||
|
||||
def _show_dialog(*args, **kwargs):
|
||||
"""Show a dialog for a backend problem."""
|
||||
dialog = _Dialog(*args, **kwargs)
|
||||
|
||||
status = dialog.exec_()
|
||||
|
||||
if status == _Result.quit:
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
elif status == _Result.restart:
|
||||
# FIXME pass --backend webengine
|
||||
quitter = objreg.get('quitter')
|
||||
quitter.restart()
|
||||
sys.exit(usertypes.Exit.err_init)
|
||||
|
||||
|
||||
def _handle_nouveau_graphics():
|
||||
force_sw_var = 'QT_XCB_FORCE_SOFTWARE_OPENGL'
|
||||
|
||||
if version.opengl_vendor() != 'nouveau':
|
||||
return
|
||||
|
||||
if (os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or
|
||||
force_sw_var in os.environ):
|
||||
return
|
||||
|
||||
if config.force_software_rendering:
|
||||
os.environ[force_sw_var] = '1'
|
||||
return
|
||||
|
||||
button = _Button("Force software rendering", 'force_software_rendering',
|
||||
True)
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebEngine,
|
||||
because="you're using Nouveau graphics",
|
||||
text="<p>There are two ways to fix this:</p>"
|
||||
"<p><b>Forcing software rendering</b></p>"
|
||||
"<p>This allows you to use the newer QtWebEngine backend (based "
|
||||
"on Chromium) but could have noticable performance impact "
|
||||
"(depending on your hardware). "
|
||||
"This sets the <i>force_software_rendering = True</i> setting "
|
||||
"(if you have a <i>config.py</i> file, you'll need to set this "
|
||||
"manually).</p>",
|
||||
buttons=[button],
|
||||
)
|
||||
|
||||
# Should never be reached
|
||||
assert False
|
||||
|
||||
|
||||
def _handle_wayland():
|
||||
if QApplication.instance().platformName() not in ['wayland', 'wayland-egl']:
|
||||
return
|
||||
if os.environ.get('DISPLAY'):
|
||||
# When DISPLAY is set but with the wayland/wayland-egl platform plugin,
|
||||
# QtWebEngine will do the right hting.
|
||||
return
|
||||
|
||||
_show_dialog(
|
||||
backend=usertypes.Backend.QtWebEngine,
|
||||
because="you're using Wayland",
|
||||
text="<p>There are two ways to fix this:</p>"
|
||||
"<p><b>Set up XWayland</b></p>"
|
||||
"<p>This allows you to use the newer QtWebEngine backend (based "
|
||||
"on Chromium). "
|
||||
)
|
||||
|
||||
# Should never be reached
|
||||
assert False
|
||||
|
||||
|
||||
def init():
|
||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||
_handle_wayland()
|
||||
_handle_nouveau_graphics()
|
||||
else:
|
||||
assert objects.backend == usertypes.Backend.QtWebKit, objects.backend
|
Loading…
Reference in New Issue
Block a user