-#!/usr/bin/python3
+#!/usr/bin/env python3
# DSL - easy Display Setup for Laptops
# Copyright (C) 2012-2015 Ralf Jung <post@ralfj.de>
#
# 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):
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:
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') + '/.dsl.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)
turnOnBacklight()
except Exception as e:
frontend.error(str(e))
- raise
+ if cmdArgs is None or cmdArgs.verbose:
+ raise(e)