From 33f97ed39eb85364750fe966330ae63427e4296b Mon Sep 17 00:00:00 2001 From: gryf Date: Thu, 12 Oct 2017 17:40:30 +0200 Subject: [PATCH] Initial commit --- LICENSE | 24 ++++++++++++++ README.rst | 39 ++++++++++++++++++++++ pyrandr.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 LICENSE create mode 100644 README.rst create mode 100755 pyrandr.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..73ba6aa --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2017, Roman Dobosz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the organization nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ROMAN DOBOSZ BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..90595db --- /dev/null +++ b/README.rst @@ -0,0 +1,39 @@ +pyrandr +======= + +``pyrandr`` is a small wrapper on ``xrandr`` to provide most common usage as +simple as possible. + +Requirements +------------ + +``pyrandr`` doesn't require any other thing than python and ``xrandr`` command +line tool. Currently it is tested only on Python2.7, support for Python3 is +coming. + +Installation +------------ + +Just make ``pyrandr.py`` executable and drop somewhere in your ``$PATH``. + +Usage +----- + +Invocation is simple, executing the script: + +.. code:: shell-session + + user@localhost $ pyrandr.py + +should list all mailable outputs for your display device(s) + +Use ``--help`` to see all the other options: + +.. code:: shell-session + + (myenv)user@localhost ~/mylogs $ pyrandr.py -h + +License +------- + +This work is licensed on 3-clause BSD license. See LICENSE file for details. diff --git a/pyrandr.py b/pyrandr.py new file mode 100755 index 0000000..6db9b2d --- /dev/null +++ b/pyrandr.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Simple xrandr wrapper for organising output layout +""" +import argparse +import subprocess +import re + + +DISPLAY_RE = re.compile(r'^(?P[a-zA-Z0-9]+)\s' + r'(?Pd?i?s?connected)\s' + r'(?Pprimary\s)?' + r'(?P\d+x\d+\+\d+\+\d+\s)?' + r'.*') +RESOLUTION_RE = re.compile(r'^\s+(?P\d+)x(?P\d+)\s.*') + + +class Output(object): + def __init__(self, name, connected, primary): + self.name = name + self.connected = connected + self.primary = primary + self.active = False + + self.x = 0 + self.y = 0 + self.shift_x = 0 + self.shift_y = 0 + + def __repr__(self): + active = 'active' if self.active else 'inactive' + connected = 'conected' if self.connected else 'disconnected' + if self.connected: + return "%s %s %s (%dx%s)" % (self.name, connected, active, + self.x, self.y) + else: + return "%s %s %s" % (self.name, connected, active) + + +class Organizer(object): + def __init__(self): + self._outputs = [] + self._get_outputs() + + def __repr__(self): + return str(self._outputs) + + def _get_outputs(self): + xrandr = subprocess.check_output(['xrandr']) + + in_output = False + + for line in xrandr.split('\n'): + match = DISPLAY_RE.match(line) + if match: + data = match.groupdict() + name = data['output'] + connected = data['status'] == 'connected' + primary = bool(data['is_primary']) + self._outputs.append(Output(name, connected, primary)) + self._outputs[-1].active = bool(data['active']) + in_output = True + continue + + match = RESOLUTION_RE.match(line) + if match and in_output: + in_output = False + data = match.groupdict() + self._outputs[-1].x = int(data['width']) + self._outputs[-1].y = int(data['height']) + + continue + + def output_list(self): + _outs = {} + for out in self._outputs: + _outs[out.name] = out + + for name in sorted(_outs.keys()): + print _outs[name] + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('output', nargs='*', help='name of the output') + args = parser.parse_args() + + org = Organizer() + + if not args.output: + org.output_list() + +if __name__ == "__main__": + main()