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