blob: 5a4a80fa4e94f12614683f06e0799ea00ed945c2 [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
107class Konquerer:
108 """Controller for the KDE File Manager (kfm, or Konquerer).
109
110 See http://developer.kde.org/documentation/other/kfmclient.html
111 for more information on the Konquerer remote-control interface.
112
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):
125 # XXX currently I know no way to prevent KFM from opening a new win.
126 self.open_new(url)
127
128 def open_new(self, url):
129 self._remote("openURL %s" % url)
130
131register("kfm", Konquerer)
132
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):
186 import win32api, win32con
187 win32api.ShellExecute(0, "open", url, None, ".",
188 win32con.SW_SHOWNORMAL)
189
190 def open_new(self, url):
191 self.open(url)
192
193
194DEFAULT_BROWSER = "command-line"
195
196if sys.platform[:3] == "win":
197 del _browsers["kfm"]
198 register("windows-default", WindowsDefault)
199 DEFAULT_BROWSER = "windows-default"
200elif os.environ.get("DISPLAY"):
201 if os.environ.get("KDEDIR"):
202 DEFAULT_BROWSER = "kfm"
203 elif _iscommand("netscape"):
204 DEFAULT_BROWSER = "netscape"
205
206# If the $BROWSER environment variable is set and true, let that be
207# the name of the browser to use:
208#
209DEFAULT_BROWSER = os.environ.get("BROWSER") or DEFAULT_BROWSER
210
211
212# Now try to support the MacOS world. This is the only supported
213# controller on that platform, so don't mess with the default!
214
215try:
216 import ic
217except ImportError:
218 pass
219else:
220 class InternetConfig:
221 def open(self, url, new=0):
222 ic.launcurl(url)
223
224 def open_new(self, url):
225 self.open(url)
226
227 _browsers.clear()
228 register("internet-config", InternetConfig)
229 DEFAULT_BROWSER = "internet-config"