From f2b5c8a8624912cae677674f2a1bc186e6694061 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 8 Aug 2018 23:48:25 +1000 Subject: [PATCH] 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. --- aqt/__init__.py | 54 +++++++++++++++++++++++++++----------- aqt/profiles.py | 69 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 87 insertions(+), 36 deletions(-) diff --git a/aqt/__init__.py b/aqt/__init__.py index 13db43a7e..dc2505790 100644 --- a/aqt/__init__.py +++ b/aqt/__init__.py @@ -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) diff --git a/aqt/profiles.py b/aqt/profiles.py index b5c94abad..3c1da4e48 100644 --- a/aqt/profiles.py +++ b/aqt/profiles.py @@ -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") + return os.path.expanduser("~/Documents/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")