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