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 | |
paul@64 | 6 | Copyright (C) 2007, 2008 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 | |
paul@64 | 31 | To obtain the root window, typically the desktop background, use the |
paul@64 | 32 | desktop.windows.root function as follows: |
paul@64 | 33 | |
paul@64 | 34 | root = desktop.windows.root() |
paul@64 | 35 | |
paulb@51 | 36 | Each window object can be inspected through a number of methods. For example: |
paulb@51 | 37 | |
paulb@51 | 38 | name = window.name() |
paulb@51 | 39 | width, height = window.size() |
paulb@51 | 40 | x, y = window.position() |
paulb@51 | 41 | child_windows = window.children() |
paulb@51 | 42 | |
paulb@51 | 43 | See the desktop.windows.Window class for more information. |
paulb@46 | 44 | """ |
paulb@46 | 45 | |
paulb@46 | 46 | from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop |
paulb@46 | 47 | |
paulb@51 | 48 | def _xwininfo(s): |
paulb@51 | 49 | d = {} |
paulb@51 | 50 | for line in s.split("\n"): |
paulb@51 | 51 | fields = line.split(":") |
paulb@51 | 52 | if len(fields) < 2: |
paulb@51 | 53 | continue |
paulb@51 | 54 | key, values = fields[0].strip(), fields[1:] |
paulb@51 | 55 | d[key] = values |
paulb@51 | 56 | return d |
paulb@51 | 57 | |
paulb@51 | 58 | def _get_int_properties(d, properties): |
paulb@51 | 59 | results = [] |
paulb@51 | 60 | for property in properties: |
paulb@51 | 61 | results.append(int(d[property][0].strip())) |
paulb@51 | 62 | return results |
paulb@51 | 63 | |
paulb@51 | 64 | # Window classes. |
paulb@51 | 65 | # NOTE: X11 is the only supported desktop so far. |
paulb@51 | 66 | |
paulb@51 | 67 | class Window: |
paulb@51 | 68 | |
paulb@51 | 69 | "A window on the desktop." |
paulb@51 | 70 | |
paulb@51 | 71 | def __init__(self, identifier): |
paulb@51 | 72 | |
paulb@51 | 73 | "Initialise the window with the given 'identifier'." |
paulb@51 | 74 | |
paulb@51 | 75 | self.identifier = identifier |
paulb@51 | 76 | |
paulb@51 | 77 | def __repr__(self): |
paulb@51 | 78 | return "Window(%r)" % self.identifier |
paulb@51 | 79 | |
paulb@61 | 80 | def _get_identifier(self): |
paulb@61 | 81 | if self.identifier is None: |
paulb@61 | 82 | return "-root" |
paulb@61 | 83 | else: |
paulb@61 | 84 | return "-id " + self.identifier |
paulb@61 | 85 | |
paulb@51 | 86 | def children(self): |
paulb@51 | 87 | |
paulb@51 | 88 | "Return a list of windows which are children of this window." |
paulb@51 | 89 | |
paulb@61 | 90 | s = _readfrom(_get_x11_vars() + "xwininfo %s -children" % self._get_identifier(), shell=1) |
paulb@51 | 91 | handles = [] |
paulb@51 | 92 | adding = 0 |
paulb@51 | 93 | for line in s.split("\n"): |
paulb@51 | 94 | if not adding and line.endswith("children:"): |
paulb@51 | 95 | adding = 1 |
paulb@51 | 96 | elif adding and line: |
paulb@51 | 97 | handles.append(line.strip().split()[0]) |
paulb@51 | 98 | return [Window(handle) for handle in handles] |
paulb@51 | 99 | |
paulb@51 | 100 | def name(self): |
paulb@51 | 101 | |
paulb@51 | 102 | "Return the name of the window." |
paulb@51 | 103 | |
paulb@61 | 104 | s = _readfrom(_get_x11_vars() + "xwininfo %s -stats" % self._get_identifier(), shell=1) |
paulb@51 | 105 | for line in s.split("\n"): |
paulb@51 | 106 | if line.startswith("xwininfo:"): |
paulb@51 | 107 | |
paulb@51 | 108 | # Format is 'xwininfo: Window id: <handle> "<name>" |
paulb@51 | 109 | |
paulb@51 | 110 | fields = line.split(":") |
paulb@51 | 111 | handle_and_name = fields[2].strip() |
paulb@51 | 112 | fields2 = handle_and_name.split(" ") |
paulb@51 | 113 | |
paulb@51 | 114 | # Get the "<name>" part, stripping off the quotes. |
paulb@51 | 115 | |
paulb@51 | 116 | return " ".join(fields2[1:]).strip('"') |
paulb@51 | 117 | |
paulb@51 | 118 | return None |
paulb@51 | 119 | |
paulb@51 | 120 | def size(self): |
paulb@51 | 121 | |
paulb@51 | 122 | "Return a tuple containing the width and height of this window." |
paulb@51 | 123 | |
paulb@61 | 124 | s = _readfrom(_get_x11_vars() + "xwininfo %s -stats" % self._get_identifier(), shell=1) |
paulb@51 | 125 | d = _xwininfo(s) |
paulb@51 | 126 | return _get_int_properties(d, ["Width", "Height"]) |
paulb@51 | 127 | |
paulb@51 | 128 | def position(self): |
paulb@51 | 129 | |
paulb@51 | 130 | "Return a tuple containing the upper left co-ordinates of this window." |
paulb@51 | 131 | |
paulb@61 | 132 | s = _readfrom(_get_x11_vars() + "xwininfo %s -stats" % self._get_identifier(), shell=1) |
paulb@51 | 133 | d = _xwininfo(s) |
paulb@51 | 134 | return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"]) |
paulb@51 | 135 | |
paulb@46 | 136 | def list(desktop=None): |
paulb@46 | 137 | |
paulb@46 | 138 | """ |
paulb@51 | 139 | Return a list of windows for the current desktop. If the optional 'desktop' |
paulb@51 | 140 | parameter is specified then attempt to use that particular desktop |
paulb@46 | 141 | environment's mechanisms to look for windows. |
paulb@46 | 142 | """ |
paulb@46 | 143 | |
paulb@46 | 144 | # NOTE: The desktop parameter is currently ignored and X11 is tested for |
paulb@46 | 145 | # NOTE: directly. |
paulb@46 | 146 | |
paulb@46 | 147 | if _is_x11(): |
paulb@46 | 148 | s = _readfrom(_get_x11_vars() + "xlsclients -a -l", shell=1) |
paulb@46 | 149 | prefix = "Window " |
paulb@46 | 150 | prefix_end = len(prefix) |
paulb@61 | 151 | |
paulb@61 | 152 | # Include the root window. |
paulb@61 | 153 | |
paulb@61 | 154 | handles = [None] |
paulb@46 | 155 | |
paulb@46 | 156 | for line in s.split("\n"): |
paulb@46 | 157 | if line.startswith(prefix): |
paulb@46 | 158 | handles.append(line[prefix_end:-1]) # NOTE: Assume ":" at end. |
paulb@46 | 159 | else: |
paulb@46 | 160 | raise OSError, "Desktop '%s' not supported" % use_desktop(desktop) |
paulb@46 | 161 | |
paulb@51 | 162 | return [Window(handle) for handle in handles] |
paulb@46 | 163 | |
paul@64 | 164 | def root(desktop=None): |
paul@64 | 165 | |
paul@64 | 166 | """ |
paul@64 | 167 | Return the root window for the current desktop. If the optional 'desktop' |
paul@64 | 168 | parameter is specified then attempt to use that particular desktop |
paul@64 | 169 | environment's mechanisms to look for windows. |
paul@64 | 170 | """ |
paul@64 | 171 | |
paul@64 | 172 | # NOTE: The desktop parameter is currently ignored and X11 is tested for |
paul@64 | 173 | # NOTE: directly. |
paul@64 | 174 | |
paul@64 | 175 | if _is_x11(): |
paul@64 | 176 | return Window(None) |
paul@64 | 177 | else: |
paul@64 | 178 | raise OSError, "Desktop '%s' not supported" % use_desktop(desktop) |
paul@64 | 179 | |
paulb@46 | 180 | # vim: tabstop=4 expandtab shiftwidth=4 |