X-Git-Url: https://git.ralfj.de/lilass.git/blobdiff_plain/4462cbde3a401b1fb0d530aa0e300c26d10075d0..a5b44dc6eece34ab1539a0e71775eadfbcf6c81c:/lilass?ds=sidebyside diff --git a/lilass b/lilass index f9363ea..3512a5f 100755 --- a/lilass +++ b/lilass @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # DSL - easy Display Setup for Laptops # Copyright (C) 2012-2015 Ralf Jung # @@ -16,20 +16,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import argparse, sys, os, re, subprocess +import argparse, sys, os, os.path, shutil, re, subprocess from enum import Enum -import gui, screen +import gui, screen, util, database frontend = gui.getFrontend("cli") # the fallback, until we got a proper frontend. This is guaranteed to be available. - - -# for auto-config: common names of internal connectors -commonInternalConnectorPrefixes = ['LVDS', 'eDP'] -commonInternalConnectorSuffices = ['', '0', '1', '-0', '-1'] -def commonInternalConnectorNames(): - for prefix in commonInternalConnectorPrefixes: - for suffix in commonInternalConnectorSuffices: - yield prefix+suffix - +cmdArgs = None # Load a section-less config file: maps parameter names to space-separated lists of strings (with shell quotation) def loadConfigFile(filename): @@ -75,11 +66,10 @@ def situationByConfig(config): raise Exception("You must specify exactly one internal connector.") internalConnectors = config['internalConnector'] else: - internalConnectors = commonInternalConnectorNames() + internalConnectors = screen.commonInternalConnectorNames() # run! return screen.ScreenSituation(internalConnectors, config.get('externalConnectors')) - # if we run top-level if __name__ == "__main__": try: @@ -93,49 +83,92 @@ if __name__ == "__main__": help="The frontend to be used for user interaction") parser.add_argument("-r", "--relative-position", dest="rel_position", choices=list(map(relPosFilter, screen.RelativeScreenPosition.__members__.keys())), - help="Set the position of external screen relative to internal one.") + help="Set the position of external screen relative to internal one, in case it is not found in the DB.") parser.add_argument("-e", "--external-only", dest="external_only", action='store_true', help="If an external screen is connected, disable all the others.") parser.add_argument("-i", "--internal-only", dest="internal_only", action='store_true', help="Enable internal screen, disable all the others.") + parser.add_argument("-s", "--silent", + dest="silent", action='store_true', + help="Prefer to be silent: Opens a UI only if the external screen is not known *and* no default configuration (-r/-e/-i) is given.") + parser.add_argument("--no-db", + dest="use_db", action='store_false', + help="Do not use the database of known screens.") + parser.add_argument("-v", "--verbose", + dest="verbose", action='store_true', + help="More verbose output on stderr.") cmdArgs = parser.parse_args() # load frontend early (for error mssages) frontend = gui.getFrontend(cmdArgs.frontend) + # find files + ## find config file + legacyConfigFilePath = os.getenv('HOME') + '/.lilass.conf' + configDirectory = util.getConfigDirectory() + configFilePath = os.path.join(configDirectory, "lilass.conf") + if os.path.isfile(legacyConfigFilePath) and not os.path.isfile(configFilePath): + # looks like we just upgraded to a new version of lilass + util.mkdirP(configDirectory) + shutil.move(legacyConfigFilePath, configFilePath) + ## find database + dataDirectory = util.getDataDirectory() + util.mkdirP(dataDirectory) + databaseFilePath = os.path.join(dataDirectory, "collected_data.sqlite") + # load configuration - config = loadConfigFile(os.getenv('HOME') + '/.lilass.conf') + config = loadConfigFile(configFilePath) # see what situation we are in situation = situationByConfig(config) # construct the ScreenSetup setup = None - if not cmdArgs.internal_only and situation.externalResolutions() is not None: - # there's an external screen connected that we may want to use - if cmdArgs.external_only: - setup = screen.ScreenSetup(intResolution = None, extResolution = situation.externalResolutions()[0]) + if situation.externalConnector is not None: + # There's an external screen connected that we may want to use. + # Fetch info about this screen from the database. + # NOTE: If it is too slow to open the DB twice (reading and saving), we can keep it open all the time + if cmdArgs.use_db: + with database.Database(databaseFilePath) as db: + situation.fetchDBInfo(db) + # what to we do? + have_default_conf = bool(cmdArgs.external_only or cmdArgs.internal_only or cmdArgs.rel_position) + no_ui = bool(have_default_conf or (situation.previousSetup and cmdArgs.silent)) + if not no_ui: + # ask the user what to do + setup = frontend.setup(situation) + if setup is None: sys.exit(1) # the user canceled + if cmdArgs.use_db: + # persists this to disk + with database.Database(databaseFilePath) as db: + situation.putDBInfo(db, setup) + elif situation.previousSetup: + # apply the old setup again + setup = situation.previousSetup + # use default config from CLI + elif cmdArgs.external_only: + setup = screen.ScreenSetup(intResolution = None, extResolution = situation.externalConnector.getPreferredResolution()) elif cmdArgs.rel_position is not None: # construct automatically, based on CLI arguments - # first, figure out the desired RelativeScreenPosition... waht a bad hack... + # first, figure out the desired RelativeScreenPosition... what a bad hack... relPos = list(filter(lambda relPosItem: relPosFilter(relPosItem[0]) == cmdArgs.rel_position, screen.RelativeScreenPosition.__members__.items())) - assert len(relPos) == 1, "CLI argument is ambigue" + assert len(relPos) == 1, "CLI argument is ambiguous" relPos = relPos[0][1] # now we construct the ScreenSetup if relPos == screen.RelativeScreenPosition.MIRROR: res = situation.commonResolutions()[0] setup = screen.ScreenSetup(res, res, relPos) else: - setup = screen.ScreenSetup(intResolution = situation.internalResolutions()[0], extResolution = situation.externalResolutions()[0], relPosition = relPos) - else: - # ask the user - setup = frontend.setup(situation) - if setup is None: sys.exit(1) # the user canceled - else: - # use first resolution of internal connector - setup = screen.ScreenSetup(intResolution = situation.internalResolutions()[0], extResolution = None) + setup = screen.ScreenSetup(intResolution = situation.internalConnector.getPreferredResolution(), + extResolution = situation.externalConnector.getPreferredResolution(), + relPosition = relPos) + # cmdArgs.internal_only: fall-through + if setup is None: + assert cmdArgs.internal_only or situation.externalConnector is None + # Nothing chosen yet? Use first resolution of internal connector. + setup = screen.ScreenSetup(intResolution = situation.internalConnector.getPreferredResolution(), extResolution = None) # call xrandr xrandrCall = situation.forXrandr(setup) @@ -147,4 +180,5 @@ if __name__ == "__main__": turnOnBacklight() except Exception as e: frontend.error(str(e)) - raise + if cmdArgs is None or cmdArgs.verbose: + raise(e)