support specifying gfx driver in profile folder

We need to set the OpenGL mode prior to Qt initialisation, but
want to fetch the current driver from the profile manager - and
the profile manager required Qt to already be set up.

Work around this by moving away from QStandardPaths in favour of
a pure Python module. The profile manager now does early setup
using winpaths, and we defer most of the setup until Qt has been
initialised.

Also we install a message handler to catch OpenGL initialisation
errors, and automatically switch to the next driver so users
don't need to manually change the driver.

The --hwaccel option has been removed, as it is no longer necessary.
This commit is contained in:
Damien Elmes 2018-08-08 23:48:25 +10:00
parent 91983ce21f
commit f2b5c8a862
2 changed files with 87 additions and 36 deletions

View file

@ -224,10 +224,38 @@ def parseArgs(argv):
parser.add_option("-b", "--base", help="path to base folder")
parser.add_option("-p", "--profile", help="profile name to load")
parser.add_option("-l", "--lang", help="interface language (en, de, etc)")
if not isMac:
parser.add_option("--hwaccel", action="store_true", help="enable hardware acceleration")
return parser.parse_args(argv[1:])
def setupGL(pm):
if isMac:
return
mode = pm.glMode()
# work around pyqt loading wrong GL library
if isLin:
import ctypes
ctypes.CDLL('libGL.so.1', ctypes.RTLD_GLOBAL)
# catch opengl errors
def msgHandler(type, ctx, msg):
if "Failed to create OpenGL context" in msg:
QMessageBox.critical(None, "Error", "Error loading '%s' graphics driver. Please start Anki again to try next driver." % mode)
pm.nextGlMode()
return
else:
print("qt:", msg)
qInstallMessageHandler(msgHandler)
print("Hardware acceleration set to", mode)
if mode == "auto":
return
elif isLin:
os.environ["QT_XCB_FORCE_SOFTWARE_OPENGL"] = "1"
else:
os.environ["QT_OPENGL"] = mode
def run():
try:
_run()
@ -256,17 +284,12 @@ def _run(argv=None, exec=True):
opts.base = opts.base or ""
opts.profile = opts.profile or ""
if not isMac and not opts.hwaccel:
print("Hardware acceleration disabled.")
if isWin:
os.environ["QT_OPENGL"] = "software"
else:
os.environ["QT_XCB_FORCE_SOFTWARE_OPENGL"] = "1"
# profile manager
from aqt.profiles import ProfileManager
pm = ProfileManager(opts.base)
# work around pyqt loading wrong GL library
if isLin:
import ctypes
ctypes.CDLL('libGL.so.1', ctypes.RTLD_GLOBAL)
# gl workarounds
setupGL(pm)
# opt in to full hidpi support?
if not os.environ.get("ANKI_NOHIGHDPI"):
@ -293,9 +316,10 @@ No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
environment points to a valid, writable folder.""")
return
# profile manager
from aqt.profiles import ProfileManager
pm = ProfileManager(opts.base, opts.profile)
pm.setupMeta()
if opts.profile:
pm.openProfile(opts.profile)
# i18n
setupLang(pm, app, opts.lang)

View file

@ -61,14 +61,18 @@ profileConf = dict(
class ProfileManager:
def __init__(self, base=None, profile=None):
def __init__(self, base=None):
self.name = None
self.db = None
# instantiate base folder
self._setBaseFolder(base)
def setupMeta(self):
# load metadata
self.firstRun = self._loadMeta()
# did the user request a profile to start up with?
# profile load on startup
def openProfile(self, profile):
if profile:
if profile not in self.profiles():
QMessageBox.critical(None, "Error", "Requested profile does not exist.")
@ -101,27 +105,18 @@ a flash drive.""" % self.base)
if isMac:
return os.path.expanduser("~/Documents/Anki")
elif isWin:
loc = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
return os.path.join(loc, "Anki")
from aqt.winpaths import get_personal
return os.path.join(get_personal(), "Anki")
else:
p = os.path.expanduser("~/Anki")
if os.path.isdir(p):
return p
else:
loc = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
if loc[:-1] == QStandardPaths.writableLocation(
QStandardPaths.HomeLocation):
# occasionally "documentsLocation" will return the home
# folder because the Documents folder isn't configured
# properly; fall back to an English path
return os.path.expanduser("~/Documents/Anki")
else:
return os.path.join(loc, "Anki")
def maybeMigrateFolder(self):
oldBase = self._oldFolderLocation()
if not os.path.exists(self.base) and os.path.isdir(oldBase):
if oldBase and not os.path.exists(self.base) and os.path.isdir(oldBase):
shutil.move(oldBase, self.base)
# Profile load/save
@ -271,12 +266,8 @@ and no other programs are accessing your profile folders, then try again."""))
def _defaultBase(self):
if isWin:
loc = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
# the returned value seem to automatically include the app name, but we use Anki2 rather
# than Anki
assert loc.endswith("/Anki")
loc += "2"
return loc
from aqt.winpaths import get_appdata
return os.path.join(get_appdata(), "Anki2")
elif isMac:
return os.path.expanduser("~/Library/Application Support/Anki2")
else:
@ -407,3 +398,39 @@ please see:
self.db.execute(sql, self._pickle(self.meta), "_global")
self.db.commit()
anki.lang.setLang(code, local=False)
# OpenGL
######################################################################
def _glPath(self):
return os.path.join(self.base, "gldriver")
def glMode(self):
assert not isMac
path = self._glPath()
if not os.path.exists(path):
return "software"
mode = open(path, "r").read().strip()
if mode == "angle" and isWin:
return mode
elif mode == "software":
return mode
return "auto"
def setGlMode(self, mode):
open(self._glPath(), "w").write(mode)
def nextGlMode(self):
mode = self.glMode()
if mode == "software":
self.setGlMode("auto")
elif mode == "auto":
if isWin:
self.setGlMode("angle")
else:
self.setGlMode("software")
elif mode == "angle":
self.setGlMode("software")