paulb@46 | 1 | #!/usr/bin/env python |
paulb@46 | 2 | |
paulb@46 | 3 | """ |
paulb@46 | 4 | Simple desktop window enumeration for Python. |
paulb@46 | 5 | |
paulb@51 | 6 | Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> |
paulb@46 | 7 | |
paulb@46 | 8 | This library is free software; you can redistribute it and/or |
paulb@46 | 9 | modify it under the terms of the GNU Lesser General Public |
paulb@46 | 10 | License as published by the Free Software Foundation; either |
paulb@46 | 11 | version 2.1 of the License, or (at your option) any later version. |
paulb@46 | 12 | |
paulb@46 | 13 | This library is distributed in the hope that it will be useful, |
paulb@46 | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
paulb@46 | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
paulb@46 | 16 | Lesser General Public License for more details. |
paulb@46 | 17 | |
paulb@46 | 18 | You should have received a copy of the GNU Lesser General Public |
paulb@46 | 19 | License along with this library; if not, write to the Free Software |
paulb@46 | 20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
paulb@46 | 21 | |
paulb@46 | 22 | -------- |
paulb@46 | 23 | |
paulb@46 | 24 | Finding Open Windows on the Desktop |
paulb@46 | 25 | ----------------------------------- |
paulb@46 | 26 | |
paulb@46 | 27 | To obtain a list of windows, use the desktop.windows.list function as follows: |
paulb@46 | 28 | |
paulb@51 | 29 | windows = desktop.windows.list() |
paulb@51 | 30 | |
paulb@51 | 31 | Each window object can be inspected through a number of methods. For example: |
paulb@51 | 32 | |
paulb@51 | 33 | name = window.name() |
paulb@51 | 34 | width, height = window.size() |
paulb@51 | 35 | x, y = window.position() |
paulb@51 | 36 | child_windows = window.children() |
paulb@51 | 37 | |
paulb@51 | 38 | See the desktop.windows.Window class for more information. |
paulb@46 | 39 | """ |
paulb@46 | 40 | |
paulb@46 | 41 | from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop |
paulb@46 | 42 | |
paulb@51 | 43 | def _xwininfo(s): |
paulb@51 | 44 | d = {} |
paulb@51 | 45 | for line in s.split("\n"): |
paulb@51 | 46 | fields = line.split(":") |
paulb@51 | 47 | if len(fields) < 2: |
paulb@51 | 48 | continue |
paulb@51 | 49 | key, values = fields[0].strip(), fields[1:] |
paulb@51 | 50 | d[key] = values |
paulb@51 | 51 | return d |
paulb@51 | 52 | |
paulb@51 | 53 | def _get_int_properties(d, properties): |
paulb@51 | 54 | results = [] |
paulb@51 | 55 | for property in properties: |
paulb@51 | 56 | results.append(int(d[property][0].strip())) |
paulb@51 | 57 | return results |
paulb@51 | 58 | |
paulb@51 | 59 | # Window classes. |
paulb@51 | 60 | # NOTE: X11 is the only supported desktop so far. |
paulb@51 | 61 | |
paulb@51 | 62 | class Window: |
paulb@51 | 63 | |
paulb@51 | 64 | "A window on the desktop." |
paulb@51 | 65 | |
paulb@51 | 66 | def __init__(self, identifier): |
paulb@51 | 67 | |
paulb@51 | 68 | "Initialise the window with the given 'identifier'." |
paulb@51 | 69 | |
paulb@51 | 70 | self.identifier = identifier |
paulb@51 | 71 | |
paulb@51 | 72 | def __repr__(self): |
paulb@51 | 73 | return "Window(%r)" % self.identifier |
paulb@51 | 74 | |
paulb@51 | 75 | def children(self): |
paulb@51 | 76 | |
paulb@51 | 77 | "Return a list of windows which are children of this window." |
paulb@51 | 78 | |
paulb@51 | 79 | s = _readfrom(_get_x11_vars() + "xwininfo -id %s -children" % self.identifier, shell=1) |
paulb@51 | 80 | handles = [] |
paulb@51 | 81 | adding = 0 |
paulb@51 | 82 | for line in s.split("\n"): |
paulb@51 | 83 | if not adding and line.endswith("children:"): |
paulb@51 | 84 | adding = 1 |
paulb@51 | 85 | elif adding and line: |
paulb@51 | 86 | handles.append(line.strip().split()[0]) |
paulb@51 | 87 | return [Window(handle) for handle in handles] |
paulb@51 | 88 | |
paulb@51 | 89 | def name(self): |
paulb@51 | 90 | |
paulb@51 | 91 | "Return the name of the window." |
paulb@51 | 92 | |
paulb@51 | 93 | s = _readfrom(_get_x11_vars() + "xwininfo -id %s -stats" % self.identifier, shell=1) |
paulb@51 | 94 | for line in s.split("\n"): |
paulb@51 | 95 | if line.startswith("xwininfo:"): |
paulb@51 | 96 | |
paulb@51 | 97 | # Format is 'xwininfo: Window id: <handle> "<name>" |
paulb@51 | 98 | |
paulb@51 | 99 | fields = line.split(":") |
paulb@51 | 100 | handle_and_name = fields[2].strip() |
paulb@51 | 101 | fields2 = handle_and_name.split(" ") |
paulb@51 | 102 | |
paulb@51 | 103 | # Get the "<name>" part, stripping off the quotes. |
paulb@51 | 104 | |
paulb@51 | 105 | return " ".join(fields2[1:]).strip('"') |
paulb@51 | 106 | |
paulb@51 | 107 | return None |
paulb@51 | 108 | |
paulb@51 | 109 | def size(self): |
paulb@51 | 110 | |
paulb@51 | 111 | "Return a tuple containing the width and height of this window." |
paulb@51 | 112 | |
paulb@51 | 113 | s = _readfrom(_get_x11_vars() + "xwininfo -id %s -stats" % self.identifier, shell=1) |
paulb@51 | 114 | d = _xwininfo(s) |
paulb@51 | 115 | return _get_int_properties(d, ["Width", "Height"]) |
paulb@51 | 116 | |
paulb@51 | 117 | def position(self): |
paulb@51 | 118 | |
paulb@51 | 119 | "Return a tuple containing the upper left co-ordinates of this window." |
paulb@51 | 120 | |
paulb@51 | 121 | s = _readfrom(_get_x11_vars() + "xwininfo -id %s -stats" % self.identifier, shell=1) |
paulb@51 | 122 | d = _xwininfo(s) |
paulb@51 | 123 | return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"]) |
paulb@51 | 124 | |
paulb@46 | 125 | def list(desktop=None): |
paulb@46 | 126 | |
paulb@46 | 127 | """ |
paulb@51 | 128 | Return a list of windows for the current desktop. If the optional 'desktop' |
paulb@51 | 129 | parameter is specified then attempt to use that particular desktop |
paulb@46 | 130 | environment's mechanisms to look for windows. |
paulb@46 | 131 | """ |
paulb@46 | 132 | |
paulb@46 | 133 | # NOTE: The desktop parameter is currently ignored and X11 is tested for |
paulb@46 | 134 | # NOTE: directly. |
paulb@46 | 135 | |
paulb@46 | 136 | if _is_x11(): |
paulb@46 | 137 | s = _readfrom(_get_x11_vars() + "xlsclients -a -l", shell=1) |
paulb@46 | 138 | prefix = "Window " |
paulb@46 | 139 | prefix_end = len(prefix) |
paulb@46 | 140 | handles = [] |
paulb@46 | 141 | |
paulb@46 | 142 | for line in s.split("\n"): |
paulb@46 | 143 | if line.startswith(prefix): |
paulb@46 | 144 | handles.append(line[prefix_end:-1]) # NOTE: Assume ":" at end. |
paulb@46 | 145 | else: |
paulb@46 | 146 | raise OSError, "Desktop '%s' not supported" % use_desktop(desktop) |
paulb@46 | 147 | |
paulb@51 | 148 | return [Window(handle) for handle in handles] |
paulb@46 | 149 | |
paulb@46 | 150 | # vim: tabstop=4 expandtab shiftwidth=4 |