Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 4b4cc11115
Fetching contributors…

Cannot retrieve contributors at this time

executable file 281 lines (204 sloc) 8.91 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
#!/usr/bin/env python

"""
Simple desktop integration for Python. This module provides desktop environment
detection and resource opening support for a selection of common and
standardised desktop environments.

Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.

--------

Desktop Detection
-----------------

To detect a specific desktop environment, use the get_desktop function.
To detect whether the desktop environment is standardised (according to the
proposed DESKTOP_LAUNCH standard), use the is_standard function.

Opening URLs
------------

To open a URL in the current desktop environment, relying on the automatic
detection of that environment, use the desktop.open function as follows:

desktop.open("http://www.python.org")

To override the detected desktop, specify the desktop parameter to the open
function as follows:

desktop.open("http://www.python.org", "KDE") # Insists on KDE
desktop.open("http://www.python.org", "GNOME") # Insists on GNOME

Without overriding using the desktop parameter, the open function will attempt
to use the "standard" desktop opening mechanism which is controlled by the
DESKTOP_LAUNCH environment variable as described below.

The DESKTOP_LAUNCH Environment Variable
---------------------------------------

The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate,
as shown in some of the following examples:

DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in
their entirety in a KDE message box.
(Command "kdialog" plus parameter.)
DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to
open URLs.
(Command "my opener", no parameters.)
DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to
open URLs.
(Command "my opener" plus parameter.)

Details of the DESKTOP_LAUNCH environment variable convention can be found here:
http://lists.freedesktop.org/archives/xdg/2004-August/004489.html

Other Modules
-------------

The desktop.dialog module provides support for opening dialogue boxes.
The desktop.windows module permits the inspection of desktop windows.
"""

__version__ = "0.4"

import os
import sys

# Provide suitable process creation functions.

try:
    import subprocess
    def _run(cmd, shell, wait):
        opener = subprocess.Popen(cmd, shell=shell)
        if wait: opener.wait()
        return opener.pid

    def _readfrom(cmd, shell):
        opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        opener.stdin.close()
        return opener.stdout.read()

    def _status(cmd, shell):
        opener = subprocess.Popen(cmd, shell=shell)
        opener.wait()
        return opener.returncode == 0

except ImportError:
    import popen2
    def _run(cmd, shell, wait):
        opener = popen2.Popen3(cmd)
        if wait: opener.wait()
        return opener.pid

    def _readfrom(cmd, shell):
        opener = popen2.Popen3(cmd)
        opener.tochild.close()
        opener.childerr.close()
        return opener.fromchild.read()

    def _status(cmd, shell):
        opener = popen2.Popen3(cmd)
        opener.wait()
        return opener.poll() == 0

import commands

# Private functions.

def _get_x11_vars():

    "Return suitable environment definitions for X11."

    if not os.environ.get("DISPLAY", "").strip():
        return "DISPLAY=:0.0 "
    else:
        return ""

def _is_xfce():

    "Return whether XFCE is in use."

    # XFCE detection involves testing the output of a program.

    try:
        return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"')
    except OSError:
        return 0

def _is_x11():

    "Return whether the X Window System is in use."

    return os.environ.has_key("DISPLAY")

# Introspection functions.

def get_desktop():

    """
Detect the current desktop environment, returning the name of the
environment. If no environment could be detected, None is returned.
"""

    if os.environ.has_key("KDE_FULL_SESSION") or \
        os.environ.has_key("KDE_MULTIHEAD"):
        return "KDE"
    elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \
        os.environ.has_key("GNOME_KEYRING_SOCKET"):
        return "GNOME"
    elif sys.platform == "darwin":
        return "Mac OS X"
    elif hasattr(os, "startfile"):
        return "Windows"
    elif _is_xfce():
        return "XFCE"

    # KDE, GNOME and XFCE run on X11, so we have to test for X11 last.

    if _is_x11():
        return "X11"
    else:
        return None

def use_desktop(desktop):

    """
Decide which desktop should be used, based on the detected desktop and a
supplied 'desktop' argument (which may be None). Return an identifier
indicating the desktop type as being either "standard" or one of the results
from the 'get_desktop' function.
"""

    # Attempt to detect a desktop environment.

    detected = get_desktop()

    # Start with desktops whose existence can be easily tested.

    if (desktop is None or desktop == "standard") and is_standard():
        return "standard"
    elif (desktop is None or desktop == "Windows") and detected == "Windows":
        return "Windows"

    # Test for desktops where the overriding is not verified.

    elif (desktop or detected) == "KDE":
        return "KDE"
    elif (desktop or detected) == "GNOME":
        return "GNOME"
    elif (desktop or detected) == "XFCE":
        return "XFCE"
    elif (desktop or detected) == "Mac OS X":
        return "Mac OS X"
    elif (desktop or detected) == "X11":
        return "X11"
    else:
        return None

def is_standard():

    """
Return whether the current desktop supports standardised application
launching.
"""

    return os.environ.has_key("DESKTOP_LAUNCH")

# Activity functions.

def open(url, desktop=None, wait=0):

    """
Open the 'url' in the current desktop's preferred file browser. If the
optional 'desktop' parameter is specified then attempt to use that
particular desktop environment's mechanisms to open the 'url' instead of
guessing or detecting which environment is being used.

Suggested values for 'desktop' are "standard", "KDE", "GNOME", "XFCE",
"Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH environment
variable to open the specified 'url'. DESKTOP_LAUNCH should be a command,
possibly followed by arguments, and must have any special characters
shell-escaped.

The process identifier of the "opener" (ie. viewer, editor, browser or
program) associated with the 'url' is returned by this function. If the
process identifier cannot be determined, None is returned.

An optional 'wait' parameter is also available for advanced usage and, if
'wait' is set to a true value, this function will wait for the launching
mechanism to complete before returning (as opposed to immediately returning
as is the default behaviour).
"""

    # Decide on the desktop environment in use.

    desktop_in_use = use_desktop(desktop)

    if desktop_in_use == "standard":
        arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)])
        return _run(arg, 1, wait)

    elif desktop_in_use == "Windows":
        # NOTE: This returns None in current implementations.
        return os.startfile(url)

    elif desktop_in_use == "KDE":
        cmd = ["kfmclient", "exec", url]

    elif desktop_in_use == "GNOME":
        cmd = ["gnome-open", url]

    elif desktop_in_use == "XFCE":
        cmd = ["exo-open", url]

    elif desktop_in_use == "Mac OS X":
        cmd = ["open", url]

    elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"):
        cmd = [os.environ["BROWSER"], url]

    # Finish with an error where no suitable desktop was identified.

    else:
        raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use

    return _run(cmd, 0, wait)

# vim: tabstop=4 expandtab shiftwidth=4
Something went wrong with that request. Please try again.