LEFT = 0
RIGHT = 1
EXTERNAL_ONLY = 2
+ MIRROR = 3
+
+ __names__ = {
+ 'left': LEFT,
+ 'right': RIGHT,
+ 'external-only': EXTERNAL_ONLY,
+ 'mirror': MIRROR
+ }
# storing what's necessary for screen setup
class ScreenSetup:
args = ["--mode", res2xrandr(self.extResolution)] # set external screen to desired resolution
if self.extIsPrimary:
args.append('--primary')
+ # set position
if self.relPosition == RelativeScreenPosition.LEFT:
args += ['--left-of', intName]
elif self.relPosition == RelativeScreenPosition.RIGHT:
args += ['--right-of', intName]
+ elif self.relPosition == RelativeScreenPosition.MIRROR:
+ args += ['--same-as', intName]
+ else:
+ assert self.relPosition == RelativeScreenPosition.EXTERNAL_ONLY
return args
# Load a section-less config file: maps parameter names to space-separated lists of strings (with shell quotation)
dest="frontend",
help="The frontend to be used for user interaction")
parser.add_argument("-r", "--relative-position",
- dest="rel_position", choices=('left', 'right', 'external-only'),
+ dest="rel_position", choices=RelativeScreenPosition.__names__.keys(),
help="Position of external screen relative to internal one")
parser.add_argument("-i", "--internal-only",
dest="internal_only", action='store_true',
usedExternalConnector = findAvailableConnector(externalConnectors, connectors) # *the* external connector which is actually used
hasExternal = not cmdArgs.internal_only and usedExternalConnector is not None
if hasExternal:
+ # compute the list of resolutons available on both
+ commonRes = [res for res in connectors[usedExternalConnector] if res in connectors[internalConnector]]
# there's an external screen connected, we need to get a setup
if cmdArgs.rel_position is not None:
- # use command-line arguments (can we do this relPosition stuff more elegant?)
- if cmdArgs.rel_position == 'left':
- relPosition = RelativeScreenPosition.LEFT
- elif cmdArgs.rel_position == 'right':
- relPosition = RelativeScreenPosition.RIGHT
+ # use command-line arguments
+ relPosition = RelativeScreenPosition.__names__[cmdArgs.rel_position]
+ if relPosition == RelativeScreenPosition.MIRROR:
+ setup = ScreenSetup(relPosition, commonRes[0], commonRes[0]) # use default resolutions
else:
- relPosition = RelativeScreenPosition.EXTERNAL_ONLY
- setup = ScreenSetup(relPosition, connectors[internalConnector][0], connectors[usedExternalConnector][0]) # use default resolutions
+ setup = ScreenSetup(relPosition, connectors[internalConnector][0], connectors[usedExternalConnector][0]) # use default resolutions
else:
# use GUI
- setup = frontend.setup(connectors[internalConnector], connectors[usedExternalConnector])
+ setup = frontend.setup(connectors[internalConnector], connectors[usedExternalConnector], commonRes)
if setup is None: sys.exit(1) # the user canceled
# apply it
connectorArgs[internalConnector] = setup.getInternalArgs()
from PyQt4 import QtGui
QtGui.QMessageBox.critical(None, 'Fatal error', message)
- def setup(self, internalResolutions, externalResolutions):
+ def setup(self, internalResolutions, externalResolutions, commonRes):
from qt_dialogue import PositionSelection
- return PositionSelection(internalResolutions, externalResolutions).run()
+ return PositionSelection(internalResolutions, externalResolutions, commonRes).run()
@staticmethod
def isAvailable():
'''Displays a fatal error to the user'''
subprocess.check_call(["zenity", "--error", "--text="+message])
- def setup(self, internalResolutions, externalResolutions):
+ def setup(self, internalResolutions, externalResolutions, commonRes):
from zenity_dialogue import run
run(internalResolutions, externalResolutions)
def error(self, message):
print(message, file=sys.stderr)
- def setup(self, internalResolutions, externalResolutions):
+ def setup(self, internalResolutions, externalResolutions, commonRes):
raise Exception("Choosing the setup interactively is not supported with the CLI frontend")
@staticmethod
return layout
class PositionSelection(QtGui.QDialog):
- def __init__(self, internalResolutions, externalResolutions):
+ def __init__(self, internalResolutions, externalResolutions, commonResolutions):
# set up main window
super(PositionSelection, self).__init__()
self.setWindowTitle('DSL - easy Display Setup for Laptops')
- # position selection
+ ## position selection
posBox = QtGui.QGroupBox('Position of external screen', self)
self.posLeft = QtGui.QRadioButton('Left of internal screen', posBox)
self.posRight = QtGui.QRadioButton('Right of internal screen', posBox)
self.posRight.setChecked(True)
self.posRight.setFocus()
self.extOnly = QtGui.QRadioButton('Use external screen exclusively', posBox)
- posBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.posLeft, self.posRight, self.extOnly]))
+ self.mirror = QtGui.QRadioButton('Mirror internal screen', posBox)
+ positions = [self.posLeft, self.posRight, self.extOnly, self.mirror]
+ posBox.setLayout(makeLayout(QtGui.QVBoxLayout(), positions))
+ for pos in positions:
+ pos.toggled.connect(self.updateForm)
- # primary screen
- primBox = QtGui.QGroupBox('Which should be the primary screen?', self)
- self.extOnly.toggled.connect(primBox.setDisabled) # disable the box if there's just one screen in use
- self.primExt = QtGui.QRadioButton('The external screen', primBox)
- self.primInt = QtGui.QRadioButton('The internal screen', primBox)
+ ## primary screen
+ self.primBox = QtGui.QGroupBox('Which should be the primary screen?', self)
+ self.primExt = QtGui.QRadioButton('The external screen', self.primBox)
+ self.primInt = QtGui.QRadioButton('The internal screen', self.primBox)
self.primInt.setChecked(True)
- primBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.primExt, self.primInt]))
+ self.primBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.primExt, self.primInt]))
- # resolution selection
+ ## resolution selection
resBox = QtGui.QGroupBox('Screen resolutions', self)
- extResLabel = QtGui.QLabel('Resolution of external screen:', resBox)
+ # external screen
+ self.extResLabel = QtGui.QLabel('Resolution of external screen:', resBox)
self.extResolutions = externalResolutions
self.extResolutionsBox = QtGui.QComboBox(resBox)
for res in externalResolutions:
self.extResolutionsBox.addItem(res2user(res))
self.extResolutionsBox.setCurrentIndex(0) # select first resolution
- extRow = makeLayout(QtGui.QHBoxLayout(), [extResLabel, self.extResolutionsBox])
- intResLabel = QtGui.QLabel('Resolution of internal screen:', resBox)
- self.extOnly.toggled.connect(intResLabel.setDisabled) # disable the label if there's just one screen in use
+ self.extRow = makeLayout(QtGui.QHBoxLayout(), [self.extResLabel, self.extResolutionsBox])
+ # internal screen
+ self.intResLabel = QtGui.QLabel('Resolution of internal screen:', resBox)
self.intResolutions = internalResolutions
self.intResolutionsBox = QtGui.QComboBox(resBox)
for res in internalResolutions:
self.intResolutionsBox.addItem(res2user(res))
self.intResolutionsBox.setCurrentIndex(0) # select first resolution
- self.extOnly.toggled.connect(self.intResolutionsBox.setDisabled) # disable the box if there's just one screen in use
- intRow = makeLayout(QtGui.QHBoxLayout(), [intResLabel, self.intResolutionsBox])
- resBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [extRow, intRow]))
+ self.intRow = makeLayout(QtGui.QHBoxLayout(), [self.intResLabel, self.intResolutionsBox])
+ # both screens
+ self.mirrorResLabel = QtGui.QLabel('Resolution of both screens:', resBox)
+ self.mirrorResolutions = commonResolutions
+ self.mirrorResolutionsBox = QtGui.QComboBox(resBox)
+ for res in commonResolutions:
+ self.mirrorResolutionsBox.addItem(res2user(res))
+ self.mirrorResolutionsBox.setCurrentIndex(0) # select first resolution
+ self.mirrorRow = makeLayout(QtGui.QHBoxLayout(), [self.mirrorResLabel, self.mirrorResolutionsBox])
+ # show them all
+ resBox.setLayout(makeLayout(QtGui.QVBoxLayout(), [self.extRow, self.intRow, self.mirrorRow]))
# last row: buttons
buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, QtCore.Qt.Horizontal, self)
buttons.rejected.connect(self.reject)
# add them all to the window
- self.setLayout(makeLayout(QtGui.QVBoxLayout(), [posBox, primBox, resBox, buttons]))
+ self.setLayout(makeLayout(QtGui.QVBoxLayout(), [posBox, self.primBox, resBox, buttons]))
+
+ # make sure we are consistent
+ self.updateForm()
+
+ def updateForm(self):
+ self.primBox.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
+ self.extResolutionsBox.setEnabled(not self.mirror.isChecked())
+ self.extResLabel.setEnabled(not self.mirror.isChecked())
+ self.intResolutionsBox.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
+ self.intResLabel.setEnabled(self.posLeft.isChecked() or self.posRight.isChecked())
+ self.mirrorResolutionsBox.setEnabled(self.mirror.isChecked())
+ self.mirrorResLabel.setEnabled(self.mirror.isChecked())
def run(self):
self.exec_()
if not self.result(): return None
- return ScreenSetup(self.getRelativeScreenPosition(),
- self.intResolutions[self.intResolutionsBox.currentIndex()],
- self.extResolutions[self.extResolutionsBox.currentIndex()],
- self.primExt.isChecked())
+ if self.mirror.isChecked():
+ return ScreenSetup(RelativeScreenPosition.MIRROR,
+ self.mirrorResolutions[self.mirrorResolutionsBox.currentIndex()],
+ self.mirrorResolutions[self.mirrorResolutionsBox.currentIndex()],
+ extIsPrimary = True)
+ else:
+ return ScreenSetup(self.getRelativeScreenPosition(),
+ self.intResolutions[self.intResolutionsBox.currentIndex()],
+ self.extResolutions[self.extResolutionsBox.currentIndex()],
+ self.primExt.isChecked())
def getRelativeScreenPosition(self):
if self.posLeft.isChecked():
return RelativeScreenPosition.LEFT
elif self.posRight.isChecked():
return RelativeScreenPosition.RIGHT
- else:
+ elif self.extOnly.isChecked():
return RelativeScreenPosition.EXTERNAL_ONLY
+ else:
+ raise Exception("Nothing is checked?")