aboutsummaryrefslogblamecommitdiffstats
path: root/xorg-xrandr/setres/get.py
blob: 47a6428561ddeb3767af29601d98df95185051d5 (plain) (tree)























































































































































































































































                                                                                                       
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.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(' ', '')
            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