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