blob: 2fa11487938245e66e24343edd7de2ff1e6d693a [file] [log] [blame]
Fred Drakec70b4482000-07-09 16:45:56 +00001"""Remote-control interfaces to some browsers."""
2
3import os
4import sys
5
6
7PROCESS_CREATION_DELAY = 4
8
9
10class Error(Exception):
11 pass
12
13
14_browsers = {}
15
16def register(name, klass, instance=None):
17 """Register a browser connector and, optionally, connection."""
18 _browsers[name.lower()] = [klass, instance]
19
20
21def get(name=None):
22 """Retrieve a connection to a browser by type name, or the default
23 browser."""
24 name = name or DEFAULT_BROWSER
25 try:
26 L = _browsers[name.lower()]
27 except KeyError:
28 raise ValueError, "unknown browser type: " + `name`
29 if L[1] is None:
30 L[1] = L[0]()
31 return L[1]
32
33
34# Please note: the following definition hides a builtin function.
35
36def open(url, new=0):
37 get().open(url, new)
38
39
40def open_new(url):
41 get().open_new(url)
42
43
44def _iscommand(cmd):
45 """Return true if cmd can be found on the executable search path."""
46 path = os.environ.get("PATH")
47 if not path:
48 return 0
49 for d in path.split(os.pathsep):
50 exe = os.path.join(d, cmd)
51 if os.path.isfile(exe):
52 return 1
53 return 0
54
55
56class CommandLineBrowser:
57 _browsers = []
58 if os.environ.get("DISPLAY"):
59 _browsers.extend([
60 ("netscape", "netscape %s >/dev/null &"),
61 ("mosaic", "mosaic %s >/dev/null &"),
62 ])
63 _browsers.extend([
64 ("lynx", "lynx %s"),
65 ("w3m", "w3m %s"),
66 ])
67
68 def open(self, url, new=0):
69 for exe, cmd in self._browsers:
70 if _iscommand(exe):
71 os.system(cmd % url)
72 return
73 raise Error("could not locate runnable browser")
74
75 def open_new(self, url):
76 self.open(url)
77
78register("command-line", CommandLineBrowser)
79
80
81class Netscape:
82 autoRaise = 1
83
84 def _remote(self, action):
85 raise_opt = ("-noraise", "-raise")[self.autoRaise]
86 cmd = "netscape %s -remote '%s' >/dev/null 2>&1" % (raise_opt, action)
87 rc = os.system(cmd)
88 if rc:
89 import time
90 os.system("netscape -no-about-splash &")
91 time.sleep(PROCESS_CREATION_DELAY)
92 rc = os.system(cmd)
93 return not rc
94
95 def open(self, url, new=0):
96 if new:
97 self.open_new(url)
98 else:
99 self._remote("openURL(%s)" % url)
100
101 def open_new(self, url):
102 self._remote("openURL(%s, new-window)" % url)
103
104register("netscape", Netscape)
105
106
Guido van Rossum2d3eb132001-01-14 23:09:35 +0000107class Konqueror:
108 """Controller for the KDE File Manager (kfm, or Konqueror).
Fred Drakec70b4482000-07-09 16:45:56 +0000109
110 See http://developer.kde.org/documentation/other/kfmclient.html
Guido van Rossum2d3eb132001-01-14 23:09:35 +0000111 for more information on the Konqueror remote-control interface.
Fred Drakec70b4482000-07-09 16:45:56 +0000112
113 """
114 def _remote(self, action):
115 cmd = "kfmclient %s >/dev/null 2>&1" % action
116 rc = os.system(cmd)
117 if rc:
118 import time
119 os.system("kfm -d &")
120 time.sleep(PROCESS_CREATION_DELAY)
121 rc = os.system(cmd)
122 return not rc
123
124 def open(self, url, new=1):
Tim Peterse1190062001-01-15 03:34:38 +0000125 # XXX currently I know no way to prevent KFM from opening a new win.
Fred Drakec70b4482000-07-09 16:45:56 +0000126 self.open_new(url)
127
128 def open_new(self, url):
129 self._remote("openURL %s" % url)
130
Guido van Rossum2d3eb132001-01-14 23:09:35 +0000131register("kfm", Konqueror)
Fred Drakec70b4482000-07-09 16:45:56 +0000132
133
134class Grail:
135 # There should be a way to maintain a connection to Grail, but the
136 # Grail remote control protocol doesn't really allow that at this
137 # point. It probably never will!
138
139 def _find_grail_rc(self):
140 import glob
141 import pwd
142 import socket
143 import tempfile
144 tempdir = os.path.join(tempfile.gettempdir(), ".grail-unix")
145 user = pwd.getpwuid(_os.getuid())[0]
146 filename = os.path.join(tempdir, user + "-*")
147 maybes = glob.glob(filename)
148 if not maybes:
149 return None
150 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
151 for fn in maybes:
152 # need to PING each one until we find one that's live
153 try:
154 s.connect(fn)
155 except socket.error:
156 # no good; attempt to clean it out, but don't fail:
157 try:
158 os.unlink(fn)
159 except IOError:
160 pass
161 else:
162 return s
163
164 def _remote(self, action):
165 s = self._find_grail_rc()
166 if not s:
167 return 0
168 s.send(action)
169 s.close()
170 return 1
171
172 def open(self, url, new=0):
173 if new:
174 self.open_new(url)
175 else:
176 self._remote("LOAD " + url)
177
178 def open_new(self, url):
179 self._remote("LOADNEW " + url)
180
181register("grail", Grail)
182
183
184class WindowsDefault:
185 def open(self, url, new=0):
Tim Petersf58a7aa2000-09-22 10:05:54 +0000186 os.startfile(url)
Fred Drakec70b4482000-07-09 16:45:56 +0000187
188 def open_new(self, url):
189 self.open(url)
190
191
192DEFAULT_BROWSER = "command-line"
193
194if sys.platform[:3] == "win":
195 del _browsers["kfm"]
196 register("windows-default", WindowsDefault)
197 DEFAULT_BROWSER = "windows-default"
198elif os.environ.get("DISPLAY"):
Fred Drake3944a012000-10-02 03:40:51 +0000199 if _iscommand("netscape"):
Fred Drakec70b4482000-07-09 16:45:56 +0000200 DEFAULT_BROWSER = "netscape"
201
202# If the $BROWSER environment variable is set and true, let that be
203# the name of the browser to use:
204#
205DEFAULT_BROWSER = os.environ.get("BROWSER") or DEFAULT_BROWSER
206
207
208# Now try to support the MacOS world. This is the only supported
209# controller on that platform, so don't mess with the default!
210
211try:
212 import ic
213except ImportError:
214 pass
215else:
216 class InternetConfig:
217 def open(self, url, new=0):
Guido van Rossum2595a832000-11-13 20:30:57 +0000218 ic.launchurl(url)
Fred Drakec70b4482000-07-09 16:45:56 +0000219
220 def open_new(self, url):
221 self.open(url)
222
223 _browsers.clear()
224 register("internet-config", InternetConfig)
225 DEFAULT_BROWSER = "internet-config"