class XScreen: def __init__(self, line): self.number = int(line.split(':')[0].split(' ')[1]) self.minimum = None self.current = None self.maximum = None self.connectors = [] for part in line.split(':')[1][1:].split(', '): words = part.split(' ') if words[0] == 'minimum': self.minimum = (int(words[1]), int(words[3])) elif words[0] == 'current': self.minimum = (int(words[1]), int(words[3])) elif words[0] == 'maximum': self.minimum = (int(words[1]), int(words[3])) def __getitem__(self, value): if isinstance(value, str): for connector in self.connectors: if connector.name == value: return connector elif value is True: for connector in self.connectors: if connector.primary: return connector else: return self.connectors[value] return None class XConnector: def __init__(self, line): line = line.split(' ') self.want_mode = None self.want_rate = None self.name = line[0] self.connected = line[1] == 'connected' self.primary = False self.size = None self.position = None self.mode = None self.rr = None self.physsize = None self.modes = [] self.identifier = None self.timestamp = None self.subpixel = None self.gamma = None self.brightness = None self.clones = None self.crtc = None self.crtcs = None self.transform = None self.filter = None # TODO self.edid = None # TODO BACKLIGHT # TODO Backlight # TODO scaling mode # TODO Broadcast RGB # TODO audio if not self.connected: return i = 2 if i == len(line): return if line[i] == 'primary': self.primary = True i += 1 if i == len(line): return word = line[i].replace('-', '+-').replace('+--', '+-') if len(word.replace('+', '')) + 2 == len(word) and len(word.replace('x', '')) + 1 == len(word): if word.replace('+', '').replace('x', '').isdigit(): i += 1 words = word.split('+') self.position = (int(words[1]), int(words[2])) words = words[0].split('x') self.size = (int(words[0]), int(words[1])) if i == len(line): return if line[i].startswith('(0x') and line[i].endswith(')'): self.mode = int(line[i][3 : -1], 16) i += 1 while not line[i].startswith('('): if self.rr is None: self.rr = line[i] else: self.rr += ' ' + line[i] i += 1 while not line[i].endswith(')'): i += 1 i += 1 line = [word[:-2] for word in line[i:] if word.endswith('mm')] if len(line) == 2: self.physsize = (int(line[0]), int(line[1])) def __getitem__(self, name): for mode in self.modes: if mode.name == name: return mode return None def best_rate(self, mode): best = None if isinstance(mode, str): mode = tuple(int(x) for x in mode.split('x')) for m in self.modes: if m.size == mode: cand = m.vclock if cand is None: continue if best is None or float(cand) > float(best): best = cand return best class XMode: def __init__(self, line): line = line.split(' ') [w, h] = line[0].split('x') self.size = (int(w), int(h)) self.name = int(line[1][3 : -1], 16) self.tclock = line[2][:-3] # MHz self.hsync = None self.vsync = None self.current = False self.preferred = False for word in line[3]: if word.endswith('HSync'): self.hsync = word[0] elif word.endswith('VSync'): self.vsync = word[0] elif word == '*current': self.current = True elif word == '+preferred': self.preferred = True def set_h(self, line): line = line.split(' ') self.width = int(line[2]) self.hstart = int(line[4]) self.hend = int(line[6]) self.htotal = int(line[8]) self.hskew = int(line[10]) self.hclock = line[12][:-3] # KHz def set_v(self, line): line = line.split(' ') self.height = int(line[2]) self.vstart = int(line[4]) self.vend = int(line[6]) self.vtotal = int(line[8]) self.vclock = line[10][:-2] # Hz def get_setup(): from subprocess import Popen, PIPE def is_screen_line(line): if ':' not in line: return False line = line.split(':')[0].split(' ') if len(line) != 2: return False return line[0] == 'Screen' and line[1].isdigit() def is_connector_line(line): return ' ' in line and line.split(' ')[1] in ('connected', 'disconnected') def is_mode_line(line): words = line.split(' ') if len(words) < 2: return False if len(words[0]) - 1 != len(words[0].replace('x', '')): return False if not words[0].replace('x', '').isdigit(): return False if words[0][0] == 'x' or words[0][-1] == 'x': return False if not words[1].startswith('(0x'): return False return words[1].endswith(')') screens = [] screen = None connector = None mode = None transx = None on_edid = False xrandr_output = Popen('env LANG=C xrandr --verbose'.split(' '), stdout = PIPE) xrandr_output = xrandr_output.communicate()[0].decode('utf-8', 'strict').split('\n') for line in xrandr_output: if on_edid and line.startswith('\t\t') and ':' not in line: connector.edid += line.replace(' ', '').replace('\t', '') on_edid = None elif transx is not None: transx.append(line.strip()) if len(transx) == 3: connector.transform = tuple(tuple(float(x) for x in r.split(' ')) for r in transx) transx = None elif is_screen_line(line): screen = XScreen(line) screens.append(screen) elif is_connector_line(line): connector = XConnector(line) screen.connectors.append(connector) else: line = line.replace('\t', ' ').strip() while ' ' in line: line = line.replace(' ', ' ') words = line.split(' ') if is_mode_line(line): mode = XMode(line) connector.modes.append(mode) elif line.startswith('h: '): mode.set_h(line) elif line.startswith('v: '): mode.set_v(line) elif line.startswith('Identifier:'): connector.identifier = int(words[1][2:], 16) elif line.startswith('Timestamp:'): connector.timestamp = int(words[1]) elif line.startswith('Subpixel:'): connector.subpixel = ' '.join(words[1:]) elif line.startswith('Gamma:'): connector.gamma = tuple(float(x) for x in words[1].split(':')) elif line.startswith('Brightness:'): connector.brightness = float(words[1]) elif line.startswith('Clones:'): connector.clones = words[1:] elif line.startswith('CRTCs:'): connector.crtc = int(words[1]) elif line.startswith('CRTCs:'): connector.crtcs = tuple(int(x) for x in words[1:]) elif line.startswith('Transform:'): transx = [' '.join(words[1:])] elif line.startswith('EDID:'): connector.edid = ''.join(words[1:]) on_edid = None on_edid = on_edid is None return screens