1 #!/usr/bin/env python 2 3 """ 4 Simple desktop integration for Python. This module provides desktop environment 5 detection and resource opening support for a selection of common and 6 standardised desktop environments. 7 8 Copyright (C) 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk> 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2.1 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 23 24 -------- 25 26 Desktop Detection 27 ----------------- 28 29 To detect a specific desktop environment, use the get_desktop function. 30 To detect whether the desktop environment is standardised (according to the 31 proposed DESKTOP_LAUNCH standard), use the is_standard function. 32 33 Opening URLs 34 ------------ 35 36 To open a URL in the current desktop environment, relying on the automatic 37 detection of that environment, use the desktop.open function as follows: 38 39 desktop.open("http://www.python.org") 40 41 To override the detected desktop, specify the desktop parameter to the open 42 function as follows: 43 44 desktop.open("http://www.python.org", "KDE") # Insists on KDE 45 desktop.open("http://www.python.org", "GNOME") # Insists on GNOME 46 47 Without overriding using the desktop parameter, the open function will attempt 48 to use the "standard" desktop opening mechanism which is controlled by the 49 DESKTOP_LAUNCH environment variable as described below. 50 51 The DESKTOP_LAUNCH Environment Variable 52 --------------------------------------- 53 54 The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate, 55 as shown in some of the following examples: 56 57 DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in 58 their entirety in a KDE message box. 59 (Command "kdialog" plus parameter.) 60 DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to 61 open URLs. 62 (Command "my opener", no parameters.) 63 DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to 64 open URLs. 65 (Command "my opener" plus parameter.) 66 67 Details of the DESKTOP_LAUNCH environment variable convention can be found here: 68 http://lists.freedesktop.org/archives/xdg/2004-August/004489.html 69 """ 70 71 __version__ = "0.2.4" 72 73 import os 74 import sys 75 76 # Provide suitable process creation functions. 77 78 try: 79 import subprocess 80 def _run(cmd, shell, wait): 81 opener = subprocess.Popen(cmd, shell=shell) 82 if wait: opener.wait() 83 return opener.pid 84 85 def _readfrom(cmd, shell): 86 opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE) 87 opener.stdin.close() 88 return opener.stdout.read() 89 90 def _status(cmd, shell): 91 opener = subprocess.Popen(cmd, shell=shell) 92 opener.wait() 93 return opener.returncode == 0 94 95 except ImportError: 96 import popen2 97 def _run(cmd, shell, wait): 98 opener = popen2.Popen3(cmd) 99 if wait: opener.wait() 100 return opener.pid 101 102 def _readfrom(cmd, shell): 103 opener = popen2.Popen3(cmd) 104 opener.tochild.close() 105 opener.childerr.close() 106 return opener.fromchild.read() 107 108 def _status(cmd, shell): 109 opener = popen2.Popen3(cmd) 110 opener.wait() 111 return opener.poll() == 0 112 113 import commands 114 115 # Introspection functions. 116 117 def get_desktop(): 118 119 """ 120 Detect the current desktop environment, returning the name of the 121 environment. If no environment could be detected, None is returned. 122 """ 123 124 if os.environ.has_key("KDE_FULL_SESSION") or \ 125 os.environ.has_key("KDE_MULTIHEAD"): 126 return "KDE" 127 elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \ 128 os.environ.has_key("GNOME_KEYRING_SOCKET"): 129 return "GNOME" 130 elif sys.platform == "darwin": 131 return "Mac OS X" 132 elif hasattr(os, "startfile"): 133 return "Windows" 134 135 # XFCE detection involves testing the output of a program. 136 137 try: 138 if _readfrom("xprop -root _DT_SAVE_MODE", shell=0).endswith(' = "xfce4"'): 139 return "XFCE" 140 except OSError: 141 pass 142 143 # XFCE runs on X11, so we have to test for X11 last. 144 145 if os.environ.has_key("DISPLAY"): 146 return "X11" 147 else: 148 return None 149 150 def use_desktop(desktop): 151 152 """ 153 Decide which desktop should be used, based on the detected desktop and a 154 supplied 'desktop' argument (which may be None). Return an identifier 155 indicating the desktop type as being either "standard" or one of the results 156 from the 'get_desktop' function. 157 """ 158 159 # Attempt to detect a desktop environment. 160 161 detected = get_desktop() 162 163 # Start with desktops whose existence can be easily tested. 164 165 if (desktop is None or desktop == "standard") and is_standard(): 166 return "standard" 167 elif (desktop is None or desktop == "Windows") and detected == "Windows": 168 return "Windows" 169 170 # Test for desktops where the overriding is not verified. 171 172 elif (desktop or detected) == "KDE": 173 return "KDE" 174 elif (desktop or detected) == "GNOME": 175 return "GNOME" 176 elif (desktop or detected) == "XFCE": 177 return "XFCE" 178 elif (desktop or detected) == "Mac OS X": 179 return "Mac OS X" 180 elif (desktop or detected) == "X11": 181 return "X11" 182 else: 183 return None 184 185 def is_standard(): 186 187 """ 188 Return whether the current desktop supports standardised application 189 launching. 190 """ 191 192 return os.environ.has_key("DESKTOP_LAUNCH") 193 194 # Activity functions. 195 196 def open(url, desktop=None, wait=0): 197 198 """ 199 Open the 'url' in the current desktop's preferred file browser. If the 200 optional 'desktop' parameter is specified then attempt to use that 201 particular desktop environment's mechanisms to open the 'url' instead of 202 guessing or detecting which environment is being used. 203 204 Suggested values for 'desktop' are "standard", "KDE", "GNOME", "XFCE", 205 "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH environment 206 variable to open the specified 'url'. DESKTOP_LAUNCH should be a command, 207 possibly followed by arguments, and must have any special characters 208 shell-escaped. 209 210 The process identifier of the "opener" (ie. viewer, editor, browser or 211 program) associated with the 'url' is returned by this function. If the 212 process identifier cannot be determined, None is returned. 213 214 An optional 'wait' parameter is also available for advanced usage and, if 215 'wait' is set to a true value, this function will wait for the launching 216 mechanism to complete before returning (as opposed to immediately returning 217 as is the default behaviour). 218 """ 219 220 # Decide on the desktop environment in use. 221 222 desktop_in_use = use_desktop(desktop) 223 224 if desktop_in_use == "standard": 225 arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)]) 226 return _run(arg, 1, wait) 227 228 elif desktop_in_use == "Windows": 229 # NOTE: This returns None in current implementations. 230 return os.startfile(url) 231 232 elif desktop_in_use == "KDE": 233 cmd = ["kfmclient", "exec", url] 234 235 elif desktop_in_use == "GNOME": 236 cmd = ["gnome-open", url] 237 238 elif desktop_in_use == "XFCE": 239 cmd = ["exo-open", url] 240 241 elif desktop_in_use == "Mac OS X": 242 cmd = ["open", url] 243 244 elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"): 245 cmd = [os.environ["BROWSER"], url] 246 247 # Finish with an error where no suitable desktop was identified. 248 249 else: 250 raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use 251 252 return _run(cmd, 0, wait) 253 254 # vim: tabstop=4 expandtab shiftwidth=4