From b55560c01779bab6fbce33217ded75645cd5dd5a Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Fri, 23 Jan 2015 00:38:17 -0700 Subject: [PATCH] Initial commit --- xrandr_configure_outputs.py | 216 ++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 xrandr_configure_outputs.py diff --git a/xrandr_configure_outputs.py b/xrandr_configure_outputs.py new file mode 100644 index 0000000..fcd67f1 --- /dev/null +++ b/xrandr_configure_outputs.py @@ -0,0 +1,216 @@ +#!/usr/bin/python +""" +Parse xrandr current information + +TODO: +* Cannot seem to undock notebook then recover displays without causing a logout + event. Could it be that i3 is finding there are no displays? +""" + +import sys +import subprocess +import re + +dry_run = False + +class OutputInfo(): + def __init__(self): + self.clear() + + def clear(self): + self.name = None + self.connected = False + self.enabled = False + self.width = -1 + self.height = -1 + self.posx = -1 + self.posy = -1 + + def complete(self): + if self.name and (self.connected == False or self.width != -1): + return True + else: + return False + + def asDict(self): + if self.complete(): + return {'name':self.name, 'enabled':self.enabled, + 'connected':self.connected, 'width':self.width, + 'height':self.height, 'posx':self.posx, 'posy':self.posy} + else: + raise ValueError + + def parse(self, line): + tokens = line.split() + if len(tokens): + if tokens[1] == 'connected' or tokens[1] == 'disconnected': + self.clear() + self.name = tokens[0] + if tokens[1] == 'connected': + self.connected = True + token = 2 + size = [] + while token < len(tokens) and len(size) != 4: + # skip other tokens like 'primary' + size = re.split('[x+]', tokens[token]) + token += 1 + if len(size) == 4: + self.enabled = True + self.width = int(size[0]) + self.height = int(size[1]) + self.posx = int(size[2]) + self.posy = int(size[3]) + elif self.connected and not self.enabled: + size = re.split('x', tokens[0]) + if len(size) == 2: + self.width = int(size[0]) + self.height = int(size[1]) + + +def parse(): + outputs = [] + output = OutputInfo() + p = subprocess.check_output('xrandr') + for line in p.split('\n'): + output.parse(line) + if output.complete(): + outputs.append(output.asDict()) + output.clear() + return outputs + + +def xrandr_on(output): + command = ['xrandr', '--output', output['name'], '--auto', '--pos', + '%dx0' % output['posx']] + if output['posx'] == 0: + command += ['--primary'] + if not dry_run: + print 'xrandr_on:', command + subprocess.call(command) + else: + print '(dry-run) xrandr_on:', command + + +def xrandr_off(output): + command = ['xrandr', '--output', output['name'], '--off'] + if not dry_run: + subprocess.call(command) + print 'xrandr_off:', command + else: + print '(dry-run) xrandr_off:', command + + +def output_by_name(outputs, name): + for output in outputs: + if output['name'] == name: + return output + return None + + +if __name__ == '__main__': + def print_output(output): + print 'name:%s connected:%r enabled:%r width:%d height:%d pos:%dx%d' % ( + output['name'], output['connected'], output['enabled'], + output['width'], output['height'], output['posx'], + output['posy']) + + def print_outputs(header, outputs): + print '%s:' % header + for output in outputs: + print_output(output) + print + + def main(argv): + global dry_run + if len(argv) >= 2 and argv[1] == '--dry-run': + dry_run = True + + outputs = parse() + #outputs[1]['connected'] = False + #outputs[2]['connected'] = False + print_outputs('Raw outputs', outputs) + + preferred_order = list(reversed([output['name'] for output in outputs])) + print 'preferred order:', preferred_order + print + + tmp = outputs + outputs = [] + for name in preferred_order: + output = output_by_name(tmp, name) + if output: + outputs.append(output) + print_outputs('Sorted outputs', outputs) + + connected_outputs = [output for output in outputs if output['connected']] + print_outputs('Connected outputs', connected_outputs) + + enabled_outputs = [output for output in outputs if output['enabled']] + print_outputs('Enabled outputs', enabled_outputs) + + # The outputs to use will be a subset of connected outputs + touse_outputs = connected_outputs + + # If there are more then 2 connected outputs, start removing them by + # order of desirability. The LCD panel is least desirable under the + # assumption that having more than two means the LCD panel shouldn't be + # used. + while len(touse_outputs) > 2: + print 'trim', touse_outputs[-1]['name'] + touse_outputs = touse_outputs[:-1] + print_outputs('To use outputs', touse_outputs) + + if touse_outputs == enabled_outputs: + # If the touse_outputs list is the same as the enabled_outputs + # list, then presumably there is no change made + print 'Identical' + else: + # Determine which outputs to turn off, and which to turn on + off_outputs = [output for output in enabled_outputs if output not in + touse_outputs] + print_outputs('Turn these off', off_outputs) + on_outputs = [output for output in touse_outputs if output not in + enabled_outputs] + print_outputs('Turn these on', on_outputs) + + if len(enabled_outputs) < 1 or len(enabled_outputs) > 2 or \ + len(touse_outputs) < 1 or len(touse_outputs) > 2: + print 'Something is wrong; doing nothing' + return + + # Compute positions for final outputs + posx = 0 + for output in touse_outputs: + output['posx'] = posx + output['posy'] = 0 + posx += output['width'] + print_outputs('touse, with positions', touse_outputs) + + # Remove all enabled outputs save one + while len(enabled_outputs) > 1: + output = enabled_outputs[0] + enabled_outputs = enabled_outputs[1:] + xrandr_off(output) + + # Move the remaining output to a safe position + last_old_output = enabled_outputs[0] + tmp = last_old_output['posx'] + last_old_output['posx'] = posx # from above + xrandr_on(last_old_output) + last_old_output['posx'] = tmp + + # Add the first touse output + first_new_output = touse_outputs[0] + touse_outputs = touse_outputs[1:] + xrandr_on(first_new_output) + + # If last old is not to be used, remove it + if not output_by_name(touse_outputs, last_old_output['name']): + xrandr_off(last_old_output) + + # Now add the remaining touse outputs + for output in touse_outputs: + xrandr_on(output) + + + main(sys.argv) -- 2.39.2