1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/desktop/__init__.py Mon Dec 03 00:37:15 2007 +0000
1.3 @@ -0,0 +1,276 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Simple desktop integration for Python. This module provides desktop environment
1.8 +detection and resource opening support for a selection of common and
1.9 +standardised desktop environments.
1.10 +
1.11 +Copyright (C) 2005, 2006, 2007 Paul Boddie <paul@boddie.org.uk>
1.12 +
1.13 +This library is free software; you can redistribute it and/or
1.14 +modify it under the terms of the GNU Lesser General Public
1.15 +License as published by the Free Software Foundation; either
1.16 +version 2.1 of the License, or (at your option) any later version.
1.17 +
1.18 +This library is distributed in the hope that it will be useful,
1.19 +but WITHOUT ANY WARRANTY; without even the implied warranty of
1.20 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1.21 +Lesser General Public License for more details.
1.22 +
1.23 +You should have received a copy of the GNU Lesser General Public
1.24 +License along with this library; if not, write to the Free Software
1.25 +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1.26 +
1.27 +--------
1.28 +
1.29 +Desktop Detection
1.30 +-----------------
1.31 +
1.32 +To detect a specific desktop environment, use the get_desktop function.
1.33 +To detect whether the desktop environment is standardised (according to the
1.34 +proposed DESKTOP_LAUNCH standard), use the is_standard function.
1.35 +
1.36 +Opening URLs
1.37 +------------
1.38 +
1.39 +To open a URL in the current desktop environment, relying on the automatic
1.40 +detection of that environment, use the desktop.open function as follows:
1.41 +
1.42 +desktop.open("http://www.python.org")
1.43 +
1.44 +To override the detected desktop, specify the desktop parameter to the open
1.45 +function as follows:
1.46 +
1.47 +desktop.open("http://www.python.org", "KDE") # Insists on KDE
1.48 +desktop.open("http://www.python.org", "GNOME") # Insists on GNOME
1.49 +
1.50 +Without overriding using the desktop parameter, the open function will attempt
1.51 +to use the "standard" desktop opening mechanism which is controlled by the
1.52 +DESKTOP_LAUNCH environment variable as described below.
1.53 +
1.54 +The DESKTOP_LAUNCH Environment Variable
1.55 +---------------------------------------
1.56 +
1.57 +The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate,
1.58 +as shown in some of the following examples:
1.59 +
1.60 +DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in
1.61 + their entirety in a KDE message box.
1.62 + (Command "kdialog" plus parameter.)
1.63 +DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to
1.64 + open URLs.
1.65 + (Command "my opener", no parameters.)
1.66 +DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to
1.67 + open URLs.
1.68 + (Command "my opener" plus parameter.)
1.69 +
1.70 +Details of the DESKTOP_LAUNCH environment variable convention can be found here:
1.71 +http://lists.freedesktop.org/archives/xdg/2004-August/004489.html
1.72 +"""
1.73 +
1.74 +__version__ = "0.3"
1.75 +
1.76 +import os
1.77 +import sys
1.78 +
1.79 +# Provide suitable process creation functions.
1.80 +
1.81 +try:
1.82 + import subprocess
1.83 + def _run(cmd, shell, wait):
1.84 + opener = subprocess.Popen(cmd, shell=shell)
1.85 + if wait: opener.wait()
1.86 + return opener.pid
1.87 +
1.88 + def _readfrom(cmd, shell):
1.89 + opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
1.90 + opener.stdin.close()
1.91 + return opener.stdout.read()
1.92 +
1.93 + def _status(cmd, shell):
1.94 + opener = subprocess.Popen(cmd, shell=shell)
1.95 + opener.wait()
1.96 + return opener.returncode == 0
1.97 +
1.98 +except ImportError:
1.99 + import popen2
1.100 + def _run(cmd, shell, wait):
1.101 + opener = popen2.Popen3(cmd)
1.102 + if wait: opener.wait()
1.103 + return opener.pid
1.104 +
1.105 + def _readfrom(cmd, shell):
1.106 + opener = popen2.Popen3(cmd)
1.107 + opener.tochild.close()
1.108 + opener.childerr.close()
1.109 + return opener.fromchild.read()
1.110 +
1.111 + def _status(cmd, shell):
1.112 + opener = popen2.Popen3(cmd)
1.113 + opener.wait()
1.114 + return opener.poll() == 0
1.115 +
1.116 +import commands
1.117 +
1.118 +# Private functions.
1.119 +
1.120 +def _get_x11_vars():
1.121 +
1.122 + "Return suitable environment definitions for X11."
1.123 +
1.124 + if not os.environ.get("DISPLAY", "").strip():
1.125 + return "DISPLAY=:0.0 "
1.126 + else:
1.127 + return ""
1.128 +
1.129 +def _is_xfce():
1.130 +
1.131 + "Return whether XFCE is in use."
1.132 +
1.133 + # XFCE detection involves testing the output of a program.
1.134 +
1.135 + try:
1.136 + return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"')
1.137 + except OSError:
1.138 + return 0
1.139 +
1.140 +def _is_x11():
1.141 +
1.142 + "Return whether the X Window System is in use."
1.143 +
1.144 + return os.environ.has_key("DISPLAY")
1.145 +
1.146 +# Introspection functions.
1.147 +
1.148 +def get_desktop():
1.149 +
1.150 + """
1.151 + Detect the current desktop environment, returning the name of the
1.152 + environment. If no environment could be detected, None is returned.
1.153 + """
1.154 +
1.155 + if os.environ.has_key("KDE_FULL_SESSION") or \
1.156 + os.environ.has_key("KDE_MULTIHEAD"):
1.157 + return "KDE"
1.158 + elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \
1.159 + os.environ.has_key("GNOME_KEYRING_SOCKET"):
1.160 + return "GNOME"
1.161 + elif sys.platform == "darwin":
1.162 + return "Mac OS X"
1.163 + elif hasattr(os, "startfile"):
1.164 + return "Windows"
1.165 + elif _is_xfce():
1.166 + return "XFCE"
1.167 +
1.168 + # KDE, GNOME and XFCE run on X11, so we have to test for X11 last.
1.169 +
1.170 + if _is_x11():
1.171 + return "X11"
1.172 + else:
1.173 + return None
1.174 +
1.175 +def use_desktop(desktop):
1.176 +
1.177 + """
1.178 + Decide which desktop should be used, based on the detected desktop and a
1.179 + supplied 'desktop' argument (which may be None). Return an identifier
1.180 + indicating the desktop type as being either "standard" or one of the results
1.181 + from the 'get_desktop' function.
1.182 + """
1.183 +
1.184 + # Attempt to detect a desktop environment.
1.185 +
1.186 + detected = get_desktop()
1.187 +
1.188 + # Start with desktops whose existence can be easily tested.
1.189 +
1.190 + if (desktop is None or desktop == "standard") and is_standard():
1.191 + return "standard"
1.192 + elif (desktop is None or desktop == "Windows") and detected == "Windows":
1.193 + return "Windows"
1.194 +
1.195 + # Test for desktops where the overriding is not verified.
1.196 +
1.197 + elif (desktop or detected) == "KDE":
1.198 + return "KDE"
1.199 + elif (desktop or detected) == "GNOME":
1.200 + return "GNOME"
1.201 + elif (desktop or detected) == "XFCE":
1.202 + return "XFCE"
1.203 + elif (desktop or detected) == "Mac OS X":
1.204 + return "Mac OS X"
1.205 + elif (desktop or detected) == "X11":
1.206 + return "X11"
1.207 + else:
1.208 + return None
1.209 +
1.210 +def is_standard():
1.211 +
1.212 + """
1.213 + Return whether the current desktop supports standardised application
1.214 + launching.
1.215 + """
1.216 +
1.217 + return os.environ.has_key("DESKTOP_LAUNCH")
1.218 +
1.219 +# Activity functions.
1.220 +
1.221 +def open(url, desktop=None, wait=0):
1.222 +
1.223 + """
1.224 + Open the 'url' in the current desktop's preferred file browser. If the
1.225 + optional 'desktop' parameter is specified then attempt to use that
1.226 + particular desktop environment's mechanisms to open the 'url' instead of
1.227 + guessing or detecting which environment is being used.
1.228 +
1.229 + Suggested values for 'desktop' are "standard", "KDE", "GNOME", "XFCE",
1.230 + "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH environment
1.231 + variable to open the specified 'url'. DESKTOP_LAUNCH should be a command,
1.232 + possibly followed by arguments, and must have any special characters
1.233 + shell-escaped.
1.234 +
1.235 + The process identifier of the "opener" (ie. viewer, editor, browser or
1.236 + program) associated with the 'url' is returned by this function. If the
1.237 + process identifier cannot be determined, None is returned.
1.238 +
1.239 + An optional 'wait' parameter is also available for advanced usage and, if
1.240 + 'wait' is set to a true value, this function will wait for the launching
1.241 + mechanism to complete before returning (as opposed to immediately returning
1.242 + as is the default behaviour).
1.243 + """
1.244 +
1.245 + # Decide on the desktop environment in use.
1.246 +
1.247 + desktop_in_use = use_desktop(desktop)
1.248 +
1.249 + if desktop_in_use == "standard":
1.250 + arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)])
1.251 + return _run(arg, 1, wait)
1.252 +
1.253 + elif desktop_in_use == "Windows":
1.254 + # NOTE: This returns None in current implementations.
1.255 + return os.startfile(url)
1.256 +
1.257 + elif desktop_in_use == "KDE":
1.258 + cmd = ["kfmclient", "exec", url]
1.259 +
1.260 + elif desktop_in_use == "GNOME":
1.261 + cmd = ["gnome-open", url]
1.262 +
1.263 + elif desktop_in_use == "XFCE":
1.264 + cmd = ["exo-open", url]
1.265 +
1.266 + elif desktop_in_use == "Mac OS X":
1.267 + cmd = ["open", url]
1.268 +
1.269 + elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"):
1.270 + cmd = [os.environ["BROWSER"], url]
1.271 +
1.272 + # Finish with an error where no suitable desktop was identified.
1.273 +
1.274 + else:
1.275 + raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use
1.276 +
1.277 + return _run(cmd, 0, wait)
1.278 +
1.279 +# vim: tabstop=4 expandtab shiftwidth=4