3 # Uzbl tabbing wrapper using a fifo socket interface
4 # Copyright (c) 2009, Tom Adams <tom@holizz.com>
5 # Copyright (c) 2009, Chris van Dijk <cn.vandijk@hotmail.com>
6 # Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com>
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # Tom Adams <tom@holizz.com>
24 # Wrote the original uzbl_tabbed.py as a proof of concept.
26 # Chris van Dijk (quigybo) <cn.vandijk@hotmail.com>
27 # Made signifigant headway on the old uzbl_tabbing.py script on the
28 # uzbl wiki <http://www.uzbl.org/wiki/uzbl_tabbed>
30 # Mason Larobina <mason.larobina@gmail.com>
31 # Rewrite of the uzbl_tabbing.py script to use a fifo socket interface
32 # and inherit configuration options from the user's uzbl config.
35 # mxey <mxey@ghosthacking.net>
36 # uzbl_config path now honors XDG_CONFIG_HOME if it exists.
40 # Because this version of uzbl_tabbed is able to inherit options from your main
41 # uzbl configuration file you may wish to configure uzbl tabbed from there.
42 # Here is a list of configuration options that can be customised and some
43 # example values for each:
45 # set show_tablist = 1
46 # set show_gtk_tabs = 0
47 # set switch_to_new_tabs = 1
48 # set save_session = 1
49 # set gtk_tab_pos = (left|bottom|top|right)
50 # set max_title_len = 50
51 # set new_tab_title = New tab
52 # set status_background = #303030
53 # set session_file = $HOME/.local/share/session
54 # set tab_colours = foreground = "#999"
55 # set tab_text_colours = foreground = "#444"
56 # set selected_tab = foreground = "#aaa" background="#303030"
57 # set selected_tab_text = foreground = "green"
58 # set window_size = 800,800
60 # And the keybindings:
62 # set bind_new_tab = gn
63 # set bind_tab_from_clip = gY
64 # set bind_close_tab = gC
65 # set bind_next_tab = gt
66 # set bind_prev_tab = gT
67 # set bind_goto_tab = gi_
68 # set bind_goto_first = g<
69 # set bind_goto_last = g>
71 # And uzbl_tabbed.py takes care of the actual binding of the commands via each
72 # instances fifo socket.
76 # - new windows are not caught and opened in a new tab.
77 # - when uzbl_tabbed.py crashes it takes all the children with it.
81 # - add command line options to use a different session file, not use a
82 # session file and or open a uri on starup.
83 # - ellipsize individual tab titles when the tab-list becomes over-crowded
84 # - add "<" & ">" arrows to tablist to indicate that only a subset of the
85 # currently open tabs are being displayed on the tablist.
86 # - add the small tab-list display when both gtk tabs and text vim-like
87 # tablist are hidden (I.e. [ 1 2 3 4 5 ])
89 # - pass a uzbl socketid to uzbl_tabbed.py and have it assimilated into
90 # the collective. Resistance is futile!
111 sys.stderr.write("%s\n"%msg)
114 # ============================================================================
115 # ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
116 # ============================================================================
118 # Location of your uzbl data directory.
119 if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
120 data_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'uzbl/')
122 data_dir = os.path.join(os.environ['HOME'], '.local/share/uzbl/')
123 if not os.path.exists(data_dir):
124 error("Warning: uzbl data_dir does not exist: %r" % data_dir)
126 # Location of your uzbl configuration file.
127 if 'XDG_CONFIG_HOME' in os.environ.keys() and os.environ['XDG_CONFIG_HOME']:
128 uzbl_config = os.path.join(os.environ['XDG_CONFIG_HOME'], 'uzbl/config')
130 uzbl_config = os.path.join(os.environ['HOME'],'.config/uzbl/config')
131 if not os.path.exists(uzbl_config):
132 error("Warning: Cannot locate your uzbl_config file %r" % uzbl_config)
134 # All of these settings can be inherited from your uzbl config file.
137 'show_tablist': True,
138 'show_gtk_tabs': False,
142 'gtk_tab_pos': 'top', # (top|left|bottom|right)
143 'new_tab_title': 'New tab',
144 'switch_to_new_tabs': True,
147 'save_session': True,
149 'socket_dir': '/tmp',
150 'icon_path': os.path.join(data_dir, 'uzbl.png'),
151 'session_file': os.path.join(data_dir, 'session'),
152 'status_background': "#303030",
153 'window_size': "800,800", # in pixels
154 'monospace_size': 10,
157 'bind_new_tab': 'gn',
158 'bind_tab_from_clip': 'gY',
159 'bind_close_tab': 'gC',
160 'bind_next_tab': 'gt',
161 'bind_prev_tab': 'gT',
162 'bind_goto_tab': 'gi_',
163 'bind_goto_first': 'g<',
164 'bind_goto_last': 'g>',
166 # Add custom tab style definitions to be used by the tab colour policy
167 # handler here. Because these are added to the config dictionary like
168 # any other uzbl_tabbed configuration option remember that they can
169 # be superseeded from your main uzbl config file.
170 'tab_colours': 'foreground = "#888" background = "#303030"',
171 'tab_text_colours': 'foreground = "#bbb"',
172 'selected_tab': 'foreground = "#fff"',
173 'selected_tab_text': 'foreground = "green"',
174 'tab_indicate_https': True,
175 'https_colours': 'foreground = "#888"',
176 'https_text_colours': 'foreground = "#9c8e2d"',
177 'selected_https': 'foreground = "#fff"',
178 'selected_https_text': 'foreground = "gold"',
180 } # End of config dict.
182 # This is the tab style policy handler. Every time the tablist is updated
183 # this function is called to determine how to colourise that specific tab
184 # according the simple/complex rules as defined here. You may even wish to
185 # move this function into another python script and import it using:
186 # from mycustomtabbingconfig import colour_selector
187 # Remember to rename, delete or comment out this function if you do that.
189 def colour_selector(tabindex, currentpage, uzbl):
190 '''Tablist styling policy handler. This function must return a tuple of
191 the form (tab style, text style).'''
193 # Just as an example:
194 # if 'error' in uzbl.title:
195 # if tabindex == currentpage:
196 # return ('foreground="#fff"', 'foreground="red"')
197 # return ('foreground="#888"', 'foreground="red"')
199 # Style tabs to indicate connected via https.
200 if config['tab_indicate_https'] and uzbl.uri.startswith("https://"):
201 if tabindex == currentpage:
202 return (config['selected_https'], config['selected_https_text'])
203 return (config['https_colours'], config['https_text_colours'])
205 # Style to indicate selected.
206 if tabindex == currentpage:
207 return (config['selected_tab'], config['selected_tab_text'])
210 return (config['tab_colours'], config['tab_text_colours'])
213 # ============================================================================
214 # ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
215 # ============================================================================
218 def readconfig(uzbl_config, config):
219 '''Loads relevant config from the users uzbl config file into the global
220 config dictionary.'''
222 if not os.path.exists(uzbl_config):
223 error("Unable to load config %r" % uzbl_config)
226 # Define parsing regular expressions
227 isint = re.compile("^(\-|)[0-9]+$").match
228 findsets = re.compile("^set\s+([^\=]+)\s*\=\s*(.+)$",\
229 re.MULTILINE).findall
231 h = open(os.path.expandvars(uzbl_config), 'r')
235 for (key, value) in findsets(rawconfig):
236 key, value = key.strip(), value.strip()
237 if key not in config.keys(): continue
238 if isint(value): value = int(value)
241 # Ensure that config keys that relate to paths are expanded.
242 expand = ['fifo_dir', 'socket_dir', 'session_file', 'icon_path']
244 config[key] = os.path.expandvars(config[key])
248 '''Recursively make directories.
249 I.e. `mkdir -p /some/nonexistant/path/`'''
251 path, sep = os.path.realpath(path), os.path.sep
252 dirs = path.split(sep)
253 for i in range(2,len(dirs)+1):
254 dir = os.path.join(sep,sep.join(dirs[:i]))
255 if not os.path.exists(dir):
260 '''To infinity and beyond!'''
269 '''Generates a random md5 for socket message-termination endmarkers.'''
271 return hashlib.md5(str(random.random()*time.time())).hexdigest()
275 '''A tabbed version of uzbl using gtk.Notebook'''
278 '''Uzbl instance meta-data/meta-action object.'''
280 def __init__(self, parent, tab, fifo_socket, socket_file, pid,\
285 self.fifo_socket = fifo_socket
286 self.socket_file = socket_file
288 self.title = config['new_tab_title']
296 # Switch to tab after connection
297 self._switch = switch
298 # fifo/socket files exists and socket connected.
299 self._connected = False
303 # Message termination endmarker.
304 self._marker = gen_endmarker()
306 # Gen probe commands string
308 probe = probes.append
309 probe('print uri %d @uri %s' % (self.pid, self._marker))
310 probe('print title %d @<document.title>@ %s' % (self.pid,\
312 self._probecmds = '\n'.join(probes)
314 # Enqueue keybinding config for child uzbl instance
315 self.parent.config_uzbl(self)
318 def flush(self, timer_call=False):
319 '''Flush messages from the socket-out and fifo-out queues.'''
326 error("Flush called on dead tab.")
329 if len(self._fifoout):
330 if os.path.exists(self.fifo_socket):
331 h = open(self.fifo_socket, 'w')
332 while len(self._fifoout):
333 msg = self._fifoout.pop(0)
337 if len(self._socketout):
338 if not self._socket and os.path.exists(self.socket_file):
339 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
340 sock.connect(self.socket_file)
344 while len(self._socketout):
345 msg = self._socketout.pop(0)
346 self._socket.send("%s\n"%msg)
348 if not self._connected and timer_call:
349 if not len(self._fifoout + self._socketout):
350 self._connected = True
352 if timer_call in self.timers.keys():
353 gobject.source_remove(self.timers[timer_call])
354 del self.timers[timer_call]
357 tabs = list(self.parent.notebook)
358 tabid = tabs.index(self.tab)
359 self.parent.goto_tab(tabid)
361 return len(self._fifoout + self._socketout)
365 '''Probes the client for information about its self.'''
368 self.send(self._probecmds)
369 self._lastprobe = time.time()
372 def write(self, msg):
373 '''Child fifo write function.'''
375 self._fifoout.append(msg)
376 # Flush messages from the queue if able.
381 '''Child socket send function.'''
383 self._socketout.append(msg)
384 # Flush messages from queue if able.
389 '''Create tablist, window and notebook.'''
395 # Holds metadata on the uzbl childen open.
398 # Generates a unique id for uzbl socket filenames.
399 self.next_pid = counter().next
402 self.window = gtk.Window()
404 window_size = map(int, config['window_size'].split(','))
405 self.window.set_default_size(*window_size)
408 error("Invalid value for default_size in config file.")
410 self.window.set_title("Uzbl Browser")
411 self.window.set_border_width(0)
413 # Set main window icon
414 icon_path = config['icon_path']
415 if os.path.exists(icon_path):
416 self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
419 icon_path = '/usr/share/uzbl/examples/data/uzbl/uzbl.png'
420 if os.path.exists(icon_path):
421 self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
423 # Attach main window event handlers
424 self.window.connect("delete-event", self.quit)
427 if config['show_tablist']:
429 self.window.add(vbox)
430 ebox = gtk.EventBox()
431 self.tablist = gtk.Label()
432 self.tablist.set_use_markup(True)
433 self.tablist.set_justify(gtk.JUSTIFY_LEFT)
434 self.tablist.set_line_wrap(False)
435 self.tablist.set_selectable(False)
436 self.tablist.set_padding(2,2)
437 self.tablist.set_alignment(0,0)
438 self.tablist.set_ellipsize(pango.ELLIPSIZE_END)
439 self.tablist.set_text(" ")
441 ebox.add(self.tablist)
443 bgcolor = gtk.gdk.color_parse(config['status_background'])
444 ebox.modify_bg(gtk.STATE_NORMAL, bgcolor)
447 self.notebook = gtk.Notebook()
448 self.notebook.set_show_tabs(config['show_gtk_tabs'])
451 allposes = {'left': gtk.POS_LEFT, 'right':gtk.POS_RIGHT,
452 'top':gtk.POS_TOP, 'bottom':gtk.POS_BOTTOM}
453 if config['gtk_tab_pos'] in allposes.keys():
454 self.notebook.set_tab_pos(allposes[config['gtk_tab_pos']])
456 self.notebook.set_show_border(False)
457 self.notebook.connect("page-removed", self.tab_closed)
458 self.notebook.connect("switch-page", self.tab_changed)
460 if config['show_tablist']:
461 if config['tablist_top']:
462 vbox.pack_start(ebox, False, False, 0)
463 vbox.pack_end(self.notebook, True, True, 0)
466 vbox.pack_start(self.notebook, True, True, 0)
467 vbox.pack_end(ebox, False, False, 0)
472 self.window.add(self.notebook)
475 self.wid = self.notebook.window.xid
477 # Create the uzbl_tabbed fifo
478 fifo_filename = 'uzbltabbed_%d' % os.getpid()
479 self.fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
480 self._create_fifo_socket(self.fifo_socket)
481 self._setup_fifo_watcher(self.fifo_socket)
484 def _create_fifo_socket(self, fifo_socket):
485 '''Create interprocess communication fifo socket.'''
487 if os.path.exists(fifo_socket):
488 if not os.access(fifo_socket, os.F_OK | os.R_OK | os.W_OK):
489 os.mkfifo(fifo_socket)
492 basedir = os.path.dirname(self.fifo_socket)
493 if not os.path.exists(basedir):
495 os.mkfifo(self.fifo_socket)
497 print "Listening on %s" % self.fifo_socket
500 def _setup_fifo_watcher(self, fifo_socket):
501 '''Open fifo socket fd and setup gobject IO_IN & IO_HUP watchers.
502 Also log the creation of a fd and store the the internal
503 self._watchers dictionary along with the filename of the fd.'''
505 if fifo_socket in self._fifos.keys():
506 fd, watchers = self._fifos[fifo_socket]
508 for watcherid in watchers.keys():
509 gobject.source_remove(watchers[watcherid])
510 del watchers[watcherid]
512 del self._fifos[fifo_socket]
514 # Re-open fifo and add listeners.
515 fd = os.open(fifo_socket, os.O_RDONLY | os.O_NONBLOCK)
517 self._fifos[fifo_socket] = (fd, watchers)
518 watcher = lambda key, id: watchers.__setitem__(key, id)
520 # Watch for incoming data.
521 gid = gobject.io_add_watch(fd, gobject.IO_IN, self.main_fifo_read)
522 watcher('main-fifo-read', gid)
524 # Watch for fifo hangups.
525 gid = gobject.io_add_watch(fd, gobject.IO_HUP, self.main_fifo_hangup)
526 watcher('main-fifo-hangup', gid)
530 '''UzblTabbed main function that calls the gtk loop.'''
532 # Update tablist timer
533 #timer = "update-tablist"
534 #timerid = gobject.timeout_add(500, self.update_tablist,timer)
535 #self._timers[timer] = timerid
537 # Probe clients every second for window titles and location
538 timer = "probe-clients"
539 timerid = gobject.timeout_add(1000, self.probe_clients, timer)
540 self._timers[timer] = timerid
545 def probe_clients(self, timer_call):
546 '''Probe all uzbl clients for up-to-date window titles and uri's.'''
550 for tab in self.tabs.keys():
551 uzbl = self.tabs[tab]
554 sockd[uzbl._socket] = uzbl
556 sockets = sockd.keys()
557 (reading, _, errors) = select.select(sockets, [], sockets, 0)
561 uzbl._buffer = sock.recv(1024).replace('\n',' ')
562 temp = uzbl._buffer.split(uzbl._marker)
563 self._buffer = temp.pop()
564 cmds = [s.strip().split() for s in temp if len(s.strip())]
568 self.parse_command(cmd)
571 error("parse_command: invalid command %s" % ' '.join(cmd))
577 def main_fifo_hangup(self, fd, cb_condition):
578 '''Handle main fifo socket hangups.'''
580 # Close fd, re-open fifo_socket and watch.
581 self._setup_fifo_watcher(self.fifo_socket)
583 # And to kill any gobject event handlers calling this function:
587 def main_fifo_read(self, fd, cb_condition):
588 '''Read from main fifo socket.'''
590 self._buffer = os.read(fd, 1024)
591 temp = self._buffer.split("\n")
592 self._buffer = temp.pop()
593 cmds = [s.strip().split() for s in temp if len(s.strip())]
598 self.parse_command(cmd)
601 error("parse_command: invalid command %s" % ' '.join(cmd))
607 def parse_command(self, cmd):
608 '''Parse instructions from uzbl child processes.'''
610 # Commands ( [] = optional, {} = required )
612 # open new tab and head to optional uri.
614 # close current tab or close via tab id.
616 # open next tab or n tabs down. Supports negative indexing.
618 # open prev tab or n tabs down. Supports negative indexing.
625 # title {pid} {document-title}
626 # updates tablist title.
627 # uri {pid} {document-location}
636 elif cmd[0] == "newfromclip":
637 uri = subprocess.Popen(['xclip','-selection','clipboard','-o'],\
638 stdout=subprocess.PIPE).communicate()[0]
642 elif cmd[0] == "close":
644 self.close_tab(int(cmd[1]))
649 elif cmd[0] == "next":
651 self.next_tab(int(cmd[1]))
656 elif cmd[0] == "prev":
658 self.prev_tab(int(cmd[1]))
663 elif cmd[0] == "goto":
664 self.goto_tab(int(cmd[1]))
666 elif cmd[0] == "first":
669 elif cmd[0] == "last":
672 elif cmd[0] in ["title", "uri"]:
674 uzbl = self.get_tab_by_pid(int(cmd[1]))
676 old = getattr(uzbl, cmd[0])
677 new = ' '.join(cmd[2:])
678 setattr(uzbl, cmd[0], new)
680 self.update_tablist()
682 error("parse_command: no uzbl with pid %r" % int(cmd[1]))
684 error("parse_command: unknown command %r" % ' '.join(cmd))
687 def get_tab_by_pid(self, pid):
688 '''Return uzbl instance by pid.'''
690 for tab in self.tabs.keys():
691 if self.tabs[tab].pid == pid:
692 return self.tabs[tab]
697 def new_tab(self, uri='', switch=True):
698 '''Add a new tab to the notebook and start a new instance of uzbl.
699 Use the switch option to negate config['switch_to_new_tabs'] option
700 when you need to load multiple tabs at a time (I.e. like when
701 restoring a session from a file).'''
703 pid = self.next_pid()
706 self.notebook.append_page(tab)
709 fifo_filename = 'uzbl_fifo_%s_%0.2d' % (self.wid, pid)
710 fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
711 socket_filename = 'uzbl_socket_%s_%0.2d' % (self.wid, pid)
712 socket_file = os.path.join(config['socket_dir'], socket_filename)
714 # Create meta-instance and spawn child
715 if uri: uri = '--uri %s' % uri
716 uzbl = self.UzblInstance(self, tab, fifo_socket, socket_file, pid,\
718 self.tabs[tab] = uzbl
719 cmd = 'uzbl -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, uri)
720 subprocess.Popen([cmd], shell=True) # TODO: do i need close_fds=True ?
722 # Add gobject timer to make sure the config is pushed when fifo socket
724 timerid = gobject.timeout_add(100, uzbl.flush, "flush-initial-config")
725 uzbl.timers['flush-initial-config'] = timerid
727 self.update_tablist()
730 def config_uzbl(self, uzbl):
731 '''Send bind commands for tab new/close/next/prev to a uzbl
735 bind_format = 'bind %s = sh "echo \\\"%s\\\" > \\\"%s\\\""'
736 bind = lambda key, action: binds.append(bind_format % (key, action, \
739 # Keys are defined in the config section
740 # bind ( key , command back to fifo )
741 bind(config['bind_new_tab'], 'new')
742 bind(config['bind_tab_from_clip'], 'newfromclip')
743 bind(config['bind_close_tab'], 'close')
744 bind(config['bind_next_tab'], 'next')
745 bind(config['bind_prev_tab'], 'prev')
746 bind(config['bind_goto_tab'], 'goto %s')
747 bind(config['bind_goto_first'], 'goto 0')
748 bind(config['bind_goto_last'], 'goto -1')
750 # uzbl.send via socket or uzbl.write via fifo, I'll try send.
751 uzbl.send("\n".join(binds))
754 def goto_tab(self, n):
755 '''Goto tab n (supports negative indexing).'''
757 if 0 <= n < self.notebook.get_n_pages():
758 self.notebook.set_current_page(n)
759 self.update_tablist()
763 tabs = list(self.notebook)
766 self.notebook.set_current_page(i)
767 self.update_tablist()
773 def next_tab(self, step=1):
774 '''Switch to next tab or n tabs right.'''
777 error("next_tab: invalid step %r" % step)
780 ntabs = self.notebook.get_n_pages()
781 tabn = self.notebook.get_current_page() + step
782 self.notebook.set_current_page(tabn % ntabs)
783 self.update_tablist()
786 def prev_tab(self, step=1):
787 '''Switch to prev tab or n tabs left.'''
790 error("prev_tab: invalid step %r" % step)
793 ntabs = self.notebook.get_n_pages()
794 tabn = self.notebook.get_current_page() - step
795 while tabn < 0: tabn += ntabs
796 self.notebook.set_current_page(tabn)
797 self.update_tablist()
800 def close_tab(self, tabn=None):
801 '''Closes current tab. Supports negative indexing.'''
804 tabn = self.notebook.get_current_page()
807 tab = list(self.notebook)[tabn]
811 error("close_tab: invalid index %r" % tabn)
814 self.notebook.remove_page(tabn)
817 def tab_closed(self, notebook, tab, page_num):
818 '''Close the window if no tabs are left. Called by page-removed
821 if tab in self.tabs.keys():
822 uzbl = self.tabs[tab]
823 for timer in uzbl.timers.keys():
824 error("tab_closed: removing timer %r" % timer)
825 gobject.source_remove(uzbl.timers[timer])
836 if self.notebook.get_n_pages() == 0:
839 self.update_tablist()
842 def tab_changed(self, notebook, page, page_num):
843 '''Refresh tab list. Called by switch-page signal.'''
845 self.update_tablist()
848 def update_tablist(self):
849 '''Upate tablist status bar.'''
851 show_tablist = config['show_tablist']
852 show_gtk_tabs = config['show_gtk_tabs']
853 tab_titles = config['tab_titles']
854 if not show_tablist and not show_gtk_tabs:
857 tabs = self.tabs.keys()
858 curpage = self.notebook.get_current_page()
859 title_format = "%s - Uzbl Browser"
860 max_title_len = config['max_title_len']
864 normal = (config['tab_colours'], config['tab_text_colours'])
865 selected = (config['selected_tab'], config['selected_tab_text'])
867 tab_format = "<span %s> [ %d <span %s> %s</span> ] </span>"
869 tab_format = "<span %s> [ <span %s>%d</span> ] </span>"
872 gtk_tab_format = "%d %s"
874 for index, tab in enumerate(self.notebook):
875 if tab not in tabs: continue
876 uzbl = self.tabs[tab]
879 self.window.set_title(title_format % uzbl.title)
883 self.notebook.set_tab_label_text(tab, \
884 gtk_tab_format % (index, uzbl.title[:max_title_len]))
886 self.notebook.set_tab_label_text(tab, str(index))
889 style = colour_selector(index, curpage, uzbl)
890 (tabc, textc) = style
892 pango += tab_format % (tabc, index, textc,\
893 uzbl.title[:max_title_len])
895 pango += tab_format % (tabc, textc, index)
898 self.tablist.set_markup(pango)
903 def quit(self, *args):
904 '''Cleanup the application and quit. Called by delete-event signal.'''
906 for fifo_socket in self._fifos.keys():
907 fd, watchers = self._fifos[fifo_socket]
909 for watcherid in watchers.keys():
910 gobject.source_remove(watchers[watcherid])
911 del watchers[watcherid]
913 del self._fifos[fifo_socket]
915 for timerid in self._timers.keys():
916 gobject.source_remove(self._timers[timerid])
917 del self._timers[timerid]
919 if os.path.exists(self.fifo_socket):
920 os.unlink(self.fifo_socket)
921 print "Unlinked %s" % self.fifo_socket
923 if config['save_session']:
924 session_file = os.path.expandvars(config['session_file'])
925 if self.notebook.get_n_pages():
926 if not os.path.isfile(session_file):
927 dirname = os.path.dirname(session_file)
928 if not os.path.isdir(dirname):
931 h = open(session_file, 'w')
932 h.write('current = %s\n' % self.notebook.get_current_page())
933 tabs = self.tabs.keys()
934 for tab in list(self.notebook):
935 if tab not in tabs: continue
936 uzbl = self.tabs[tab]
937 h.write("%s\n" % uzbl.uri)
941 # Notebook has no pages so delete session file if it exists.
942 if os.path.isfile(session_file):
943 os.remove(session_file)
948 if __name__ == "__main__":
950 # Read from the uzbl config into the global config dictionary.
951 readconfig(uzbl_config, config)
955 if os.path.isfile(os.path.expandvars(config['session_file'])):
956 h = open(os.path.expandvars(config['session_file']),'r')
957 lines = [line.strip() for line in h.readlines()]
961 if line.startswith("current"):
962 current = int(line.split()[-1])
965 uzbl.new_tab(line, False)