summaryrefslogtreecommitdiff
path: root/.config/uzbl/scripts/uzbl_tabbed.py
diff options
context:
space:
mode:
Diffstat (limited to '.config/uzbl/scripts/uzbl_tabbed.py')
-rwxr-xr-x.config/uzbl/scripts/uzbl_tabbed.py1474
1 files changed, 0 insertions, 1474 deletions
diff --git a/.config/uzbl/scripts/uzbl_tabbed.py b/.config/uzbl/scripts/uzbl_tabbed.py
deleted file mode 100755
index cd5ef4f..0000000
--- a/.config/uzbl/scripts/uzbl_tabbed.py
+++ /dev/null
@@ -1,1474 +0,0 @@
-#!/usr/bin/env python
-
-# Uzbl tabbing wrapper using a fifo socket interface
-# Copyright (c) 2009, Tom Adams <tom@holizz.com>
-# Copyright (c) 2009, Chris van Dijk <cn.vandijk@hotmail.com>
-# Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-# Author(s):
-# Tom Adams <tom@holizz.com>
-# Wrote the original uzbl_tabbed.py as a proof of concept.
-#
-# Chris van Dijk (quigybo) <cn.vandijk@hotmail.com>
-# Made signifigant headway on the old uzbl_tabbing.py script on the
-# uzbl wiki <http://www.uzbl.org/wiki/uzbl_tabbed>
-#
-# Mason Larobina <mason.larobina@gmail.com>
-# Rewrite of the uzbl_tabbing.py script to use a fifo socket interface
-# and inherit configuration options from the user's uzbl config.
-#
-# Contributor(s):
-# mxey <mxey@ghosthacking.net>
-# uzbl_config path now honors XDG_CONFIG_HOME if it exists.
-#
-# Romain Bignon <romain@peerfuse.org>
-# Fix for session restoration code.
-#
-# Jake Probst <jake.probst@gmail.com>
-# Wrote a patch that overflows tabs in the tablist on to new lines when
-# running of room.
-#
-# Devon Jones <devon.jones@gmail.com>
-# Fifo command bring_to_front which brings the gtk window to focus.
-
-
-# Dependencies:
-# pygtk - python bindings for gtk.
-# pango - python bindings needed for text rendering & layout in gtk widgets.
-# pygobject - GLib's GObject bindings for python.
-#
-# Optional dependencies:
-# simplejson - save uzbl_tabbed.py sessions & presets in json.
-#
-# Note: I haven't included version numbers with this dependency list because
-# I've only ever tested uzbl_tabbed.py on the latest stable versions of these
-# packages in Gentoo's portage. Package names may vary on different systems.
-
-
-# Configuration:
-# Because this version of uzbl_tabbed is able to inherit options from your main
-# uzbl configuration file you may wish to configure uzbl tabbed from there.
-# Here is a list of configuration options that can be customised and some
-# example values for each:
-#
-# General tabbing options:
-# show_tablist = 1
-# show_gtk_tabs = 0
-# tablist_top = 1
-# gtk_tab_pos = (top|left|bottom|right)
-# gtk_refresh = 1000
-# switch_to_new_tabs = 1
-# capture_new_windows = 1
-# multiline_tabs = 1
-#
-# Tab title options:
-# tab_titles = 1
-# new_tab_title = Loading
-# max_title_len = 50
-# show_ellipsis = 1
-#
-# Session options:
-# save_session = 1
-# json_session = 0
-# session_file = $HOME/.local/share/uzbl/session
-#
-# Inherited uzbl options:
-# fifo_dir = /tmp
-# socket_dir = /tmp
-# icon_path = $HOME/.local/share/uzbl/uzbl.png
-# status_background = #303030
-#
-# Misc options:
-# window_size = 800,800
-# verbose = 0
-#
-# And the key bindings:
-# bind_new_tab = gn
-# bind_tab_from_clip = gY
-# bind_tab_from_uri = go _
-# bind_close_tab = gC
-# bind_next_tab = gt
-# bind_prev_tab = gT
-# bind_goto_tab = gi_
-# bind_goto_first = g<
-# bind_goto_last = g>
-# bind_clean_slate = gQ
-# bind_exit = gZ
-#
-# Session preset key bindings:
-# bind_save_preset = gsave _
-# bind_load_preset = gload _
-# bind_del_preset = gdel _
-# bind_list_presets = glist
-#
-# And uzbl_tabbed.py takes care of the actual binding of the commands via each
-# instances fifo socket.
-#
-# Custom tab styling:
-# tab_colours = foreground = "#888" background = "#303030"
-# tab_text_colours = foreground = "#bbb"
-# selected_tab = foreground = "#fff"
-# selected_tab_text = foreground = "green"
-# tab_indicate_https = 1
-# https_colours = foreground = "#888"
-# https_text_colours = foreground = "#9c8e2d"
-# selected_https = foreground = "#fff"
-# selected_https_text = foreground = "gold"
-#
-# How these styling values are used are soley defined by the syling policy
-# handler below (the function in the config section). So you can for example
-# turn the tab text colour Firetruck-Red in the event "error" appears in the
-# tab title or some other arbitrary event. You may wish to make a trusted
-# hosts file and turn tab titles of tabs visiting trusted hosts purple.
-
-
-# Issues:
-# - new windows are not caught and opened in a new tab.
-# - when uzbl_tabbed.py crashes it takes all the children with it.
-# - when a new tab is opened when using gtk tabs the tab button itself
-# grabs focus from its child for a few seconds.
-# - when switch_to_new_tabs is not selected the notebook page is
-# maintained but the new window grabs focus (try as I might to stop it).
-
-
-# Todo:
-# - add command line options to use a different session file, not use a
-# session file and or open a uri on starup.
-# - ellipsize individual tab titles when the tab-list becomes over-crowded
-# - add "<" & ">" arrows to tablist to indicate that only a subset of the
-# currently open tabs are being displayed on the tablist.
-# - add the small tab-list display when both gtk tabs and text vim-like
-# tablist are hidden (I.e. [ 1 2 3 4 5 ])
-# - check spelling.
-# - pass a uzbl socketid to uzbl_tabbed.py and have it assimilated into
-# the collective. Resistance is futile!
-
-
-import pygtk
-import gtk
-import subprocess
-import os
-import re
-import time
-import getopt
-import pango
-import select
-import sys
-import gobject
-import socket
-import random
-import hashlib
-import atexit
-import types
-
-from gobject import io_add_watch, source_remove, timeout_add, IO_IN, IO_HUP
-from signal import signal, SIGTERM, SIGINT
-from optparse import OptionParser, OptionGroup
-
-
-pygtk.require('2.0')
-
-_SCRIPTNAME = os.path.basename(sys.argv[0])
-def error(msg):
- sys.stderr.write("%s: error: %s\n" % (_SCRIPTNAME, msg))
-
-# ============================================================================
-# ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
-# ============================================================================
-
-def xdghome(key, default):
- '''Attempts to use the environ XDG_*_HOME paths if they exist otherwise
- use $HOME and the default path.'''
-
- xdgkey = "XDG_%s_HOME" % key
- if xdgkey in os.environ.keys() and os.environ[xdgkey]:
- return os.environ[xdgkey]
-
- return os.path.join(os.environ['HOME'], default)
-
-# Setup xdg paths.
-DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/')
-CONFIG_DIR = os.path.join(xdghome('CONFIG', '.config/'), 'uzbl/')
-
-# Ensure uzbl xdg paths exist
-for path in [DATA_DIR, CONFIG_DIR]:
- if not os.path.exists(path):
- os.makedirs(path)
-
-# Path to uzbl config
-UZBL_CONFIG = os.path.join(CONFIG_DIR, 'config')
-if not os.path.exists(UZBL_CONFIG):
- error("cannot find uzbl config file at %r" % UZBL_CONFIG)
- sys.exit(1)
-
-# All of these settings can be inherited from your uzbl config file.
-config = {
- # Tab options
- 'show_tablist': True, # Show text uzbl like statusbar tab-list
- 'show_gtk_tabs': False, # Show gtk notebook tabs
- 'tablist_top': True, # Display tab-list at top of window
- 'gtk_tab_pos': 'top', # Gtk tab position (top|left|bottom|right)
- 'gtk_refresh': 1000, # Tablist refresh millisecond interval
- 'switch_to_new_tabs': True, # Upon opening a new tab switch to it
- 'capture_new_windows': True, # Use uzbl_tabbed to catch new windows
- 'multiline_tabs': True, # Tabs overflow onto new tablist lines.
-
- # Tab title options
- 'tab_titles': True, # Display tab titles (else only tab-nums)
- 'new_tab_title': 'Loading', # New tab title
- 'max_title_len': 50, # Truncate title at n characters
- 'show_ellipsis': True, # Show ellipsis when truncating titles
-
- # Session options
- 'save_session': True, # Save session in file when quit
- 'json_session': False, # Use json to save session.
- 'saved_sessions_dir': os.path.join(DATA_DIR, 'sessions/'),
- 'session_file': os.path.join(DATA_DIR, 'session'),
-
- # Inherited uzbl options
- 'fifo_dir': '/tmp', # Path to look for uzbl fifo.
- 'socket_dir': '/tmp', # Path to look for uzbl socket.
- 'icon_path': os.path.join(DATA_DIR, 'uzbl.png'),
- 'status_background': "#303030", # Default background for all panels.
-
- # Misc options
- 'window_size': "800,800", # width,height in pixels.
- 'verbose': False, # Print verbose output.
-
- # Key bindings
- 'bind_new_tab': 'gn', # Open new tab.
- 'bind_tab_from_clip': 'gY', # Open tab from clipboard.
- 'bind_tab_from_uri': 'go _', # Open new tab and goto entered uri.
- 'bind_close_tab': 'gC', # Close tab.
- 'bind_next_tab': 'gt', # Next tab.
- 'bind_prev_tab': 'gT', # Prev tab.
- 'bind_goto_tab': 'gi_', # Goto tab by tab-number (in title).
- 'bind_goto_first': 'g<', # Goto first tab.
- 'bind_goto_last': 'g>', # Goto last tab.
- 'bind_clean_slate': 'gQ', # Close all tabs and open new tab.
- 'bind_exit': 'gZ', # Exit nicely.
-
- # Session preset key bindings
- 'bind_save_preset': 'gsave _', # Save session to file %s.
- 'bind_load_preset': 'gload _', # Load preset session from file %s.
- 'bind_del_preset': 'gdel _', # Delete preset session %s.
- 'bind_list_presets': 'glist', # List all session presets.
-
- # Add custom tab style definitions to be used by the tab colour policy
- # handler here. Because these are added to the config dictionary like
- # any other uzbl_tabbed configuration option remember that they can
- # be superseeded from your main uzbl config file.
- 'tab_colours': 'foreground = "#888" background = "#303030"',
- 'tab_text_colours': 'foreground = "#bbb"',
- 'selected_tab': 'foreground = "#fff"',
- 'selected_tab_text': 'foreground = "green"',
- 'tab_indicate_https': True,
- 'https_colours': 'foreground = "#888"',
- 'https_text_colours': 'foreground = "#9c8e2d"',
- 'selected_https': 'foreground = "#fff"',
- 'selected_https_text': 'foreground = "gold"',
-
-} # End of config dict.
-
-# This is the tab style policy handler. Every time the tablist is updated
-# this function is called to determine how to colourise that specific tab
-# according the simple/complex rules as defined here. You may even wish to
-# move this function into another python script and import it using:
-# from mycustomtabbingconfig import colour_selector
-# Remember to rename, delete or comment out this function if you do that.
-
-def colour_selector(tabindex, currentpage, uzbl):
- '''Tablist styling policy handler. This function must return a tuple of
- the form (tab style, text style).'''
-
- # Just as an example:
- # if 'error' in uzbl.title:
- # if tabindex == currentpage:
- # return ('foreground="#fff"', 'foreground="red"')
- # return ('foreground="#888"', 'foreground="red"')
-
- # Style tabs to indicate connected via https.
- if config['tab_indicate_https'] and uzbl.uri.startswith("https://"):
- if tabindex == currentpage:
- return (config['selected_https'], config['selected_https_text'])
- return (config['https_colours'], config['https_text_colours'])
-
- # Style to indicate selected.
- if tabindex == currentpage:
- return (config['selected_tab'], config['selected_tab_text'])
-
- # Default tab style.
- return (config['tab_colours'], config['tab_text_colours'])
-
-# ============================================================================
-# ::: End of configuration section :::::::::::::::::::::::::::::::::::::::::::
-# ============================================================================
-
-def echo(msg):
- if config['verbose']:
- sys.stderr.write("%s: %s\n" % (_SCRIPTNAME, msg))
-
-
-def readconfig(uzbl_config, config):
- '''Loads relevant config from the users uzbl config file into the global
- config dictionary.'''
-
- if not os.path.exists(uzbl_config):
- error("Unable to load config %r" % uzbl_config)
- return None
-
- # Define parsing regular expressions
- isint = re.compile("^(\-|)[0-9]+$").match
- findsets = re.compile("^set\s+([^\=]+)\s*\=\s*(.+)$",\
- re.MULTILINE).findall
-
- h = open(os.path.expandvars(uzbl_config), 'r')
- rawconfig = h.read()
- h.close()
-
- configkeys, strip = config.keys(), str.strip
- for (key, value) in findsets(rawconfig):
- key, value = strip(key), strip(value)
- if key not in configkeys: continue
- if isint(value): value = int(value)
- config[key] = value
-
- # Ensure that config keys that relate to paths are expanded.
- pathkeys = ['fifo_dir', 'socket_dir', 'session_file', 'icon_path',
- 'saved_sessions_dir']
- for key in pathkeys:
- config[key] = os.path.expandvars(config[key])
-
-
-def counter():
- '''To infinity and beyond!'''
-
- i = 0
- while True:
- i += 1
- yield i
-
-
-def escape(s):
- '''Replaces html markup in tab titles that screw around with pango.'''
-
- for (split, glue) in [('&','&amp;'), ('<', '&lt;'), ('>', '&gt;')]:
- s = s.replace(split, glue)
- return s
-
-
-def gen_endmarker():
- '''Generates a random md5 for socket message-termination endmarkers.'''
-
- return hashlib.md5(str(random.random()*time.time())).hexdigest()
-
-
-class UzblTabbed:
- '''A tabbed version of uzbl using gtk.Notebook'''
-
- class UzblInstance:
- '''Uzbl instance meta-data/meta-action object.'''
-
- def __init__(self, parent, tab, fifo_socket, socket_file, pid,\
- uri, title, switch):
-
- self.parent = parent
- self.tab = tab
- self.fifo_socket = fifo_socket
- self.socket_file = socket_file
- self.pid = pid
- self.title = title
- self.uri = uri
- self.timers = {}
- self._lastprobe = 0
- self._fifoout = []
- self._socketout = []
- self._socket = None
- self._buffer = ""
- # Switch to tab after loading
- self._switch = switch
- # fifo/socket files exists and socket connected.
- self._connected = False
- # The kill switch
- self._kill = False
-
- # Message termination endmarker.
- self._marker = gen_endmarker()
-
- # Gen probe commands string
- probes = []
- probe = probes.append
- probe('print uri %d @uri %s' % (self.pid, self._marker))
- probe('print title %d @<document.title>@ %s' % (self.pid,\
- self._marker))
- self._probecmds = '\n'.join(probes)
-
- # Enqueue keybinding config for child uzbl instance
- self.parent.config_uzbl(self)
-
-
- def flush(self, timer_call=False):
- '''Flush messages from the socket-out and fifo-out queues.'''
-
- if self._kill:
- if self._socket:
- self._socket.close()
- self._socket = None
-
- error("Flush called on dead tab.")
- return False
-
- if len(self._fifoout):
- if os.path.exists(self.fifo_socket):
- h = open(self.fifo_socket, 'w')
- while len(self._fifoout):
- msg = self._fifoout.pop(0)
- h.write("%s\n"%msg)
- h.close()
-
- if len(self._socketout):
- if not self._socket and os.path.exists(self.socket_file):
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- sock.connect(self.socket_file)
- self._socket = sock
-
- if self._socket:
- while len(self._socketout):
- msg = self._socketout.pop(0)
- self._socket.send("%s\n"%msg)
-
- if not self._connected and timer_call:
- if not len(self._fifoout + self._socketout):
- self._connected = True
-
- if timer_call in self.timers.keys():
- source_remove(self.timers[timer_call])
- del self.timers[timer_call]
-
- if self._switch:
- self.grabfocus()
-
- return len(self._fifoout + self._socketout)
-
-
- def grabfocus(self):
- '''Steal parent focus and switch the notebook to my own tab.'''
-
- tabs = list(self.parent.notebook)
- tabid = tabs.index(self.tab)
- self.parent.goto_tab(tabid)
-
-
- def probe(self):
- '''Probes the client for information about its self.'''
-
- if self._connected:
- self.send(self._probecmds)
- self._lastprobe = time.time()
-
-
- def write(self, msg):
- '''Child fifo write function.'''
-
- self._fifoout.append(msg)
- # Flush messages from the queue if able.
- return self.flush()
-
-
- def send(self, msg):
- '''Child socket send function.'''
-
- self._socketout.append(msg)
- # Flush messages from queue if able.
- return self.flush()
-
-
- def __init__(self):
- '''Create tablist, window and notebook.'''
-
- # Store information about the applications fifo_socket.
- self._fifo = None
-
- self._timers = {}
- self._buffer = ""
- self._killed = False
-
- # A list of the recently closed tabs
- self._closed = []
-
- # Holds metadata on the uzbl childen open.
- self.tabs = {}
-
- # Generates a unique id for uzbl socket filenames.
- self.next_pid = counter().next
-
- # Create main window
- self.window = gtk.Window()
- try:
- window_size = map(int, config['window_size'].split(','))
- self.window.set_default_size(*window_size)
-
- except:
- error("Invalid value for default_size in config file.")
-
- self.window.set_title("Uzbl Browser")
- self.window.set_border_width(0)
-
- # Set main window icon
- icon_path = config['icon_path']
- if os.path.exists(icon_path):
- self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
-
- else:
- icon_path = '/usr/share/uzbl/examples/data/uzbl/uzbl.png'
- if os.path.exists(icon_path):
- self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path))
-
- # Attach main window event handlers
- self.window.connect("delete-event", self.quitrequest)
-
- # Create tab list
- if config['show_tablist']:
- vbox = gtk.VBox()
- self.window.add(vbox)
- ebox = gtk.EventBox()
- self.tablist = gtk.Label()
-
- self.tablist.set_use_markup(True)
- self.tablist.set_justify(gtk.JUSTIFY_LEFT)
- self.tablist.set_line_wrap(False)
- self.tablist.set_selectable(False)
- self.tablist.set_padding(2,2)
- self.tablist.set_alignment(0,0)
- self.tablist.set_ellipsize(pango.ELLIPSIZE_END)
- self.tablist.set_text(" ")
- self.tablist.show()
- ebox.add(self.tablist)
- ebox.show()
- bgcolor = gtk.gdk.color_parse(config['status_background'])
- ebox.modify_bg(gtk.STATE_NORMAL, bgcolor)
-
- # Create notebook
- self.notebook = gtk.Notebook()
- self.notebook.set_show_tabs(config['show_gtk_tabs'])
-
- # Set tab position
- allposes = {'left': gtk.POS_LEFT, 'right':gtk.POS_RIGHT,
- 'top':gtk.POS_TOP, 'bottom':gtk.POS_BOTTOM}
- if config['gtk_tab_pos'] in allposes.keys():
- self.notebook.set_tab_pos(allposes[config['gtk_tab_pos']])
-
- self.notebook.set_show_border(False)
- self.notebook.set_scrollable(True)
- self.notebook.set_border_width(0)
-
- self.notebook.connect("page-removed", self.tab_closed)
- self.notebook.connect("switch-page", self.tab_changed)
- self.notebook.connect("page-added", self.tab_opened)
-
- self.notebook.show()
- if config['show_tablist']:
- if config['tablist_top']:
- vbox.pack_start(ebox, False, False, 0)
- vbox.pack_end(self.notebook, True, True, 0)
-
- else:
- vbox.pack_start(self.notebook, True, True, 0)
- vbox.pack_end(ebox, False, False, 0)
-
- vbox.show()
-
- else:
- self.window.add(self.notebook)
-
- self.window.show()
- self.wid = self.notebook.window.xid
- # Generate the fifo socket filename.
- fifo_filename = 'uzbltabbed_%d' % os.getpid()
- self.fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
-
- # Now initialise the fifo socket at self.fifo_socket
- self.init_fifo_socket()
-
- # If we are using sessions then load the last one if it exists.
- if config['save_session']:
- self.load_session()
-
-
- def run(self):
- '''UzblTabbed main function that calls the gtk loop.'''
-
- if not len(self.tabs):
- self.new_tab()
-
- gtk_refresh = int(config['gtk_refresh'])
- if gtk_refresh < 100:
- gtk_refresh = 100
-
- # Update tablist timer
- timerid = timeout_add(gtk_refresh, self.update_tablist)
- self._timers["update-tablist"] = timerid
-
- # Probe clients every second for window titles and location
- timerid = timeout_add(gtk_refresh, self.probe_clients)
- self._timers["probe-clients"] = timerid
-
- # Make SIGTERM act orderly.
- signal(SIGTERM, lambda signum, stack_frame: self.terminate(SIGTERM))
-
- # Catch keyboard interrupts
- signal(SIGINT, lambda signum, stack_frame: self.terminate(SIGINT))
-
- try:
- gtk.main()
-
- except:
- error("encounted error %r" % sys.exc_info()[1])
-
- # Unlink fifo socket
- self.unlink_fifo_socket()
-
- # Attempt to close all uzbl instances nicely.
- self.quitrequest()
-
- # Allow time for all the uzbl instances to quit.
- time.sleep(1)
-
- raise
-
-
- def terminate(self, termsig=None):
- '''Handle termination signals and exit safely and cleanly.'''
-
- # Not required but at least it lets the user know what killed his
- # browsing session.
- if termsig == SIGTERM:
- error("caught SIGTERM signal")
-
- elif termsig == SIGINT:
- error("caught keyboard interrupt")
-
- else:
- error("caught unknown signal")
-
- error("commencing infanticide!")
-
- # Sends the exit signal to all uzbl instances.
- self.quitrequest()
-
-
- def init_fifo_socket(self):
- '''Create interprocess communication fifo socket.'''
-
- if os.path.exists(self.fifo_socket):
- if not os.access(self.fifo_socket, os.F_OK | os.R_OK | os.W_OK):
- os.mkfifo(self.fifo_socket)
-
- else:
- basedir = os.path.dirname(self.fifo_socket)
- if not os.path.exists(basedir):
- os.makedirs(basedir)
-
- os.mkfifo(self.fifo_socket)
-
- # Add event handlers for IO_IN & IO_HUP events.
- self.setup_fifo_watchers()
-
- echo("listening at %r" % self.fifo_socket)
-
- # Add atexit register to destroy the socket on program termination.
- atexit.register(self.unlink_fifo_socket)
-
-
- def unlink_fifo_socket(self):
- '''Unlink the fifo socket. Note: This function is called automatically
- on exit by an atexit register.'''
-
- # Make sure the fifo_socket fd is closed.
- self.close_fifo()
-
- # And unlink if the real fifo_socket exists.
- if os.path.exists(self.fifo_socket):
- os.unlink(self.fifo_socket)
- echo("unlinked %r" % self.fifo_socket)
-
-
- def close_fifo(self):
- '''Remove all event handlers watching the fifo and close the fd.'''
-
- # Already closed
- if self._fifo is None: return
-
- (fd, watchers) = self._fifo
- os.close(fd)
-
- # Stop all gobject io watchers watching the fifo.
- for gid in watchers:
- source_remove(gid)
-
- self._fifo = None
-
-
- def setup_fifo_watchers(self):
- '''Open fifo socket fd and setup gobject IO_IN & IO_HUP event
- handlers.'''
-
- # Close currently open fifo_socket fd and kill all watchers
- self.close_fifo()
-
- fd = os.open(self.fifo_socket, os.O_RDONLY | os.O_NONBLOCK)
-
- # Add gobject io event handlers to the fifo socket.
- watchers = [io_add_watch(fd, IO_IN, self.main_fifo_read),\
- io_add_watch(fd, IO_HUP, self.main_fifo_hangup)]
-
- self._fifo = (fd, watchers)
-
-
- def main_fifo_hangup(self, fd, cb_condition):
- '''Handle main fifo socket hangups.'''
-
- # Close old fd, open new fifo socket and add io event handlers.
- self.setup_fifo_watchers()
-
- # Kill the gobject event handler calling this handler function.
- return False
-
-
- def main_fifo_read(self, fd, cb_condition):
- '''Read from main fifo socket.'''
-
- self._buffer = os.read(fd, 1024)
- temp = self._buffer.split("\n")
- self._buffer = temp.pop()
- cmds = [s.strip().split() for s in temp if len(s.strip())]
-
- for cmd in cmds:
- try:
- #print cmd
- self.parse_command(cmd)
-
- except:
- error("parse_command: invalid command %s" % ' '.join(cmd))
- raise
-
- return True
-
-
- def probe_clients(self):
- '''Probe all uzbl clients for up-to-date window titles and uri's.'''
-
- save_session = config['save_session']
-
- sockd = {}
- tabskeys = self.tabs.keys()
- notebooklist = list(self.notebook)
-
- for tab in notebooklist:
- if tab not in tabskeys: continue
- uzbl = self.tabs[tab]
- uzbl.probe()
- if uzbl._socket:
- sockd[uzbl._socket] = uzbl
-
- sockets = sockd.keys()
- (reading, _, errors) = select.select(sockets, [], sockets, 0)
-
- for sock in reading:
- uzbl = sockd[sock]
- uzbl._buffer = sock.recv(1024).replace('\n',' ')
- temp = uzbl._buffer.split(uzbl._marker)
- self._buffer = temp.pop()
- cmds = [s.strip().split() for s in temp if len(s.strip())]
- for cmd in cmds:
- try:
- #print cmd
- self.parse_command(cmd)
-
- except:
- error("parse_command: invalid command %s" % ' '.join(cmd))
- raise
-
- return True
-
-
- def parse_command(self, cmd):
- '''Parse instructions from uzbl child processes.'''
-
- # Commands ( [] = optional, {} = required )
- # new [uri]
- # open new tab and head to optional uri.
- # close [tab-num]
- # close current tab or close via tab id.
- # next [n-tabs]
- # open next tab or n tabs down. Supports negative indexing.
- # prev [n-tabs]
- # open prev tab or n tabs down. Supports negative indexing.
- # goto {tab-n}
- # goto tab n.
- # first
- # goto first tab.
- # last
- # goto last tab.
- # title {pid} {document-title}
- # updates tablist title.
- # uri {pid} {document-location}
- # updates tablist uri
- # bring_to_front
- # brings the gtk window to focus.
- # exit
- # exits uzbl_tabbed.py
-
- if cmd[0] == "new":
- if len(cmd) == 2:
- self.new_tab(cmd[1])
-
- else:
- self.new_tab()
-
- elif cmd[0] == "newfromclip":
- uri = subprocess.Popen(['xclip','-selection','clipboard','-o'],\
- stdout=subprocess.PIPE).communicate()[0]
- if uri:
- self.new_tab(uri)
-
- elif cmd[0] == "close":
- if len(cmd) == 2:
- self.close_tab(int(cmd[1]))
-
- else:
- self.close_tab()
-
- elif cmd[0] == "next":
- if len(cmd) == 2:
- self.next_tab(int(cmd[1]))
-
- else:
- self.next_tab()
-
- elif cmd[0] == "prev":
- if len(cmd) == 2:
- self.prev_tab(int(cmd[1]))
-
- else:
- self.prev_tab()
-
- elif cmd[0] == "goto":
- self.goto_tab(int(cmd[1]))
-
- elif cmd[0] == "first":
- self.goto_tab(0)
-
- elif cmd[0] == "last":
- self.goto_tab(-1)
-
- elif cmd[0] in ["title", "uri"]:
- if len(cmd) > 2:
- uzbl = self.get_tab_by_pid(int(cmd[1]))
- if uzbl:
- old = getattr(uzbl, cmd[0])
- new = ' '.join(cmd[2:])
- setattr(uzbl, cmd[0], new)
- if old != new:
- self.update_tablist()
-
- else:
- error("parse_command: no uzbl with pid %r" % int(cmd[1]))
-
- elif cmd[0] == "preset":
- if len(cmd) < 3:
- error("parse_command: invalid preset command")
-
- elif cmd[1] == "save":
- path = os.path.join(config['saved_sessions_dir'], cmd[2])
- self.save_session(path)
-
- elif cmd[1] == "load":
- path = os.path.join(config['saved_sessions_dir'], cmd[2])
- self.load_session(path)
-
- elif cmd[1] == "del":
- path = os.path.join(config['saved_sessions_dir'], cmd[2])
- if os.path.isfile(path):
- os.remove(path)
-
- else:
- error("parse_command: preset %r does not exist." % path)
-
- elif cmd[1] == "list":
- uzbl = self.get_tab_by_pid(int(cmd[2]))
- if uzbl:
- if not os.path.isdir(config['saved_sessions_dir']):
- js = "js alert('No saved presets.');"
- uzbl.send(js)
-
- else:
- listdir = os.listdir(config['saved_sessions_dir'])
- listdir = "\\n".join(listdir)
- js = "js alert('Session presets:\\n\\n%s');" % listdir
- uzbl.send(js)
-
- else:
- error("parse_command: unknown tab pid.")
-
- else:
- error("parse_command: unknown parse command %r"\
- % ' '.join(cmd))
-
- elif cmd[0] == "bring_to_front":
- self.window.present()
-
- elif cmd[0] == "clean":
- self.clean_slate()
-
- elif cmd[0] == "exit":
- self.quitrequest()
-
- else:
- error("parse_command: unknown command %r" % ' '.join(cmd))
-
-
- def get_tab_by_pid(self, pid):
- '''Return uzbl instance by pid.'''
-
- for (tab, uzbl) in self.tabs.items():
- if uzbl.pid == pid:
- return uzbl
-
- return False
-
-
- def new_tab(self, uri='', title='', switch=None):
- '''Add a new tab to the notebook and start a new instance of uzbl.
- Use the switch option to negate config['switch_to_new_tabs'] option
- when you need to load multiple tabs at a time (I.e. like when
- restoring a session from a file).'''
-
- pid = self.next_pid()
- tab = gtk.Socket()
- tab.show()
- self.notebook.append_page(tab)
- sid = tab.get_id()
- uri = uri.strip()
-
- fifo_filename = 'uzbl_fifo_%s_%0.2d' % (self.wid, pid)
- fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
- socket_filename = 'uzbl_socket_%s_%0.2d' % (self.wid, pid)
- socket_file = os.path.join(config['socket_dir'], socket_filename)
-
- if switch is None:
- switch = config['switch_to_new_tabs']
-
- if not title:
- title = config['new_tab_title']
-
- uzbl = self.UzblInstance(self, tab, fifo_socket, socket_file, pid,\
- uri, title, switch)
-
- if len(uri):
- uri = "--uri %r" % uri
-
- self.tabs[tab] = uzbl
- cmd = 'uzbl -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, uri)
- subprocess.Popen([cmd], shell=True) # TODO: do i need close_fds=True ?
-
- # Add gobject timer to make sure the config is pushed when fifo socket
- # has been created.
- timerid = timeout_add(100, uzbl.flush, "flush-initial-config")
- uzbl.timers['flush-initial-config'] = timerid
-
- self.update_tablist()
-
-
- def clean_slate(self):
- '''Close all open tabs and open a fresh brand new one.'''
-
- self.new_tab()
- tabs = self.tabs.keys()
- for tab in list(self.notebook)[:-1]:
- if tab not in tabs: continue
- uzbl = self.tabs[tab]
- uzbl.send("exit")
-
-
- def config_uzbl(self, uzbl):
- '''Send bind commands for tab new/close/next/prev to a uzbl
- instance.'''
-
- binds = []
- bind_format = r'bind %s = sh "echo \"%s\" > \"%s\""'
- bind = lambda key, action: binds.append(bind_format % (key, action,\
- self.fifo_socket))
-
- sets = []
- set_format = r'set %s = sh \"echo \\"%s\\" > \\"%s\\""'
- set = lambda key, action: binds.append(set_format % (key, action,\
- self.fifo_socket))
-
- # Bind definitions here
- # bind(key, command back to fifo)
- bind(config['bind_new_tab'], 'new')
- bind(config['bind_tab_from_clip'], 'newfromclip')
- bind(config['bind_tab_from_uri'], 'new %s')
- bind(config['bind_close_tab'], 'close')
- bind(config['bind_next_tab'], 'next')
- bind(config['bind_prev_tab'], 'prev')
- bind(config['bind_goto_tab'], 'goto %s')
- bind(config['bind_goto_first'], 'goto 0')
- bind(config['bind_goto_last'], 'goto -1')
- bind(config['bind_clean_slate'], 'clean')
- bind(config['bind_save_preset'], 'preset save %s')
- bind(config['bind_load_preset'], 'preset load %s')
- bind(config['bind_del_preset'], 'preset del %s')
- bind(config['bind_list_presets'], 'preset list %d' % uzbl.pid)
- bind(config['bind_exit'], 'exit')
-
- # Set definitions here
- # set(key, command back to fifo)
- if config['capture_new_windows']:
- set("new_window", r'new $8')
-
- # Send config to uzbl instance via its socket file.
- uzbl.send("\n".join(binds+sets))
-
-
- def goto_tab(self, index):
- '''Goto tab n (supports negative indexing).'''
-
- tabs = list(self.notebook)
- if 0 <= index < len(tabs):
- self.notebook.set_current_page(index)
- self.update_tablist()
- return None
-
- try:
- tab = tabs[index]
- # Update index because index might have previously been a
- # negative index.
- index = tabs.index(tab)
- self.notebook.set_current_page(index)
- self.update_tablist()
-
- except IndexError:
- pass
-
-
- def next_tab(self, step=1):
- '''Switch to next tab or n tabs right.'''
-
- if step < 1:
- error("next_tab: invalid step %r" % step)
- return None
-
- ntabs = self.notebook.get_n_pages()
- tabn = (self.notebook.get_current_page() + step) % ntabs
- self.notebook.set_current_page(tabn)
- self.update_tablist()
-
-
- def prev_tab(self, step=1):
- '''Switch to prev tab or n tabs left.'''
-
- if step < 1:
- error("prev_tab: invalid step %r" % step)
- return None
-
- ntabs = self.notebook.get_n_pages()
- tabn = self.notebook.get_current_page() - step
- while tabn < 0: tabn += ntabs
- self.notebook.set_current_page(tabn)
- self.update_tablist()
-
-
- def close_tab(self, tabn=None):
- '''Closes current tab. Supports negative indexing.'''
-
- if tabn is None:
- tabn = self.notebook.get_current_page()
-
- else:
- try:
- tab = list(self.notebook)[tabn]
-
- except IndexError:
- error("close_tab: invalid index %r" % tabn)
- return None
-
- self.notebook.remove_page(tabn)
-
-
- def tab_opened(self, notebook, tab, index):
- '''Called upon tab creation. Called by page-added signal.'''
-
- if config['switch_to_new_tabs']:
- self.notebook.set_focus_child(tab)
-
- else:
- oldindex = self.notebook.get_current_page()
- oldtab = self.notebook.get_nth_page(oldindex)
- self.notebook.set_focus_child(oldtab)
-
-
- def tab_closed(self, notebook, tab, index):
- '''Close the window if no tabs are left. Called by page-removed
- signal.'''
-
- if tab in self.tabs.keys():
- uzbl = self.tabs[tab]
- for (timer, gid) in uzbl.timers.items():
- error("tab_closed: removing timer %r" % timer)
- source_remove(gid)
- del uzbl.timers[timer]
-
- if uzbl._socket:
- uzbl._socket.close()
- uzbl._socket = None
-
- uzbl._fifoout = []
- uzbl._socketout = []
- uzbl._kill = True
- self._closed.append((uzbl.uri, uzbl.title))
- self._closed = self._closed[-10:]
- del self.tabs[tab]
-
- if self.notebook.get_n_pages() == 0:
- if not self._killed and config['save_session']:
- if os.path.exists(config['session_file']):
- os.remove(config['session_file'])
-
- self.quit()
-
- self.update_tablist()
-
- return True
-
-
- def tab_changed(self, notebook, page, index):
- '''Refresh tab list. Called by switch-page signal.'''
-
- tab = self.notebook.get_nth_page(index)
- self.notebook.set_focus_child(tab)
- self.update_tablist(index)
- return True
-
-
- def update_tablist(self, curpage=None):
- '''Upate tablist status bar.'''
-
- show_tablist = config['show_tablist']
- show_gtk_tabs = config['show_gtk_tabs']
- tab_titles = config['tab_titles']
- show_ellipsis = config['show_ellipsis']
- multiline_tabs = config['multiline_tabs']
-
- if multiline_tabs:
- multiline = []
-
- if not show_tablist and not show_gtk_tabs:
- return True
-
- tabs = self.tabs.keys()
- if curpage is None:
- curpage = self.notebook.get_current_page()
-
- title_format = "%s - Uzbl Browser"
- max_title_len = config['max_title_len']
-
- if show_tablist:
- pango = ""
- normal = (config['tab_colours'], config['tab_text_colours'])
- selected = (config['selected_tab'], config['selected_tab_text'])
-
- if tab_titles:
- tab_format = "<span %s> [ %d <span %s> %s</span> ] </span>"
-
- else:
- tab_format = "<span %s> [ <span %s>%d</span> ] </span>"
-
- if show_gtk_tabs:
- gtk_tab_format = "%d %s"
-
- for index, tab in enumerate(self.notebook):
- if tab not in tabs: continue
- uzbl = self.tabs[tab]
-
- if index == curpage:
- self.window.set_title(title_format % uzbl.title)
-
- # Unicode heavy strings do not like being truncated/sliced so by
- # re-encoding the string sliced of limbs are removed.
- tabtitle = uzbl.title[:max_title_len + int(show_ellipsis)]
- if type(tabtitle) != types.UnicodeType:
- tabtitle = unicode(tabtitle, 'utf-8', 'ignore')
-
- tabtitle = tabtitle.encode('utf-8', 'ignore').strip()
-
- if show_ellipsis and len(tabtitle) != len(uzbl.title):
- tabtitle += "\xe2\x80\xa6"
-
- if show_gtk_tabs:
- if tab_titles:
- self.notebook.set_tab_label_text(tab,
- gtk_tab_format % (index, tabtitle))
-
- else:
- self.notebook.set_tab_label_text(tab, str(index))
-
- if show_tablist:
- style = colour_selector(index, curpage, uzbl)
- (tabc, textc) = style
-
- if multiline_tabs:
- opango = pango
-
- if tab_titles:
- pango += tab_format % (tabc, index, textc,
- escape(tabtitle))
-
- else:
- pango += tab_format % (tabc, textc, index)
-
- self.tablist.set_markup(pango)
- listwidth = self.tablist.get_layout().get_pixel_size()[0]
- winwidth = self.window.get_size()[0]
-
- if listwidth > (winwidth - 20):
- multiline.append(opango)
- pango = tab_format % (tabc, index, textc,
- escape(tabtitle))
-
- elif tab_titles:
- pango += tab_format % (tabc, index, textc,
- escape(tabtitle))
-
- else:
- pango += tab_format % (tabc, textc, index)
-
- if show_tablist:
- if multiline_tabs:
- multiline.append(pango)
- self.tablist.set_markup('&#10;'.join(multiline))
-
- else:
- self.tablist.set_markup(pango)
-
- return True
-
-
- def save_session(self, session_file=None):
- '''Save the current session to file for restoration on next load.'''
-
- strip = str.strip
-
- if session_file is None:
- session_file = config['session_file']
-
- tabs = self.tabs.keys()
- state = []
- for tab in list(self.notebook):
- if tab not in tabs: continue
- uzbl = self.tabs[tab]
- if not uzbl.uri: continue
- state += [(uzbl.uri, uzbl.title),]
-
- session = {'curtab': self.notebook.get_current_page(),
- 'tabs': state}
-
- if config['json_session']:
- raw = json.dumps(session)
-
- else:
- lines = ["curtab = %d" % session['curtab'],]
- for (uri, title) in session['tabs']:
- lines += ["%s\t%s" % (strip(uri), strip(title)),]
-
- raw = "\n".join(lines)
-
- if not os.path.isfile(session_file):
- dirname = os.path.dirname(session_file)
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
-
- h = open(session_file, 'w')
- h.write(raw)
- h.close()
-
-
- def load_session(self, session_file=None):
- '''Load a saved session from file.'''
-
- default_path = False
- strip = str.strip
- json_session = config['json_session']
- delete_loaded = False
-
- if session_file is None:
- default_path = True
- delete_loaded = True
- session_file = config['session_file']
-
- if not os.path.isfile(session_file):
- return False
-
- h = open(session_file, 'r')
- raw = h.read()
- h.close()
- if json_session:
- if sum([1 for s in raw.split("\n") if strip(s)]) != 1:
- error("Warning: The session file %r does not look json. "\
- "Trying to load it as a non-json session file."\
- % session_file)
- json_session = False
-
- if json_session:
- try:
- session = json.loads(raw)
- curtab, tabs = session['curtab'], session['tabs']
-
- except:
- error("Failed to load jsonifed session from %r"\
- % session_file)
- return None
-
- else:
- tabs = []
- strip = str.strip
- curtab, tabs = 0, []
- lines = [s for s in raw.split("\n") if strip(s)]
- if len(lines) < 2:
- error("Warning: The non-json session file %r looks invalid."\
- % session_file)
- return None
-
- try:
- for line in lines:
- if line.startswith("curtab"):
- curtab = int(line.split()[-1])
-
- else:
- uri, title = line.split("\t",1)
- tabs += [(strip(uri), strip(title)),]
-
- except:
- error("Warning: failed to load session file %r" % session_file)
- return None
-
- session = {'curtab': curtab, 'tabs': tabs}
-
- # Now populate notebook with the loaded session.
- for (index, (uri, title)) in enumerate(tabs):
- self.new_tab(uri=uri, title=title, switch=(curtab==index))
-
- # A saved session has been loaded now delete it.
- if delete_loaded and os.path.exists(session_file):
- os.remove(session_file)
-
- # There may be other state information in the session dict of use to
- # other functions. Of course however the non-json session object is
- # just a dummy object of no use to no one.
- return session
-
-
- def quitrequest(self, *args):
- '''Attempt to close all uzbl instances nicely and exit.'''
-
- self._killed = True
-
- if config['save_session']:
- if len(list(self.notebook)) > 1:
- self.save_session()
-
- else:
- # Notebook has one page open so delete the session file.
- if os.path.isfile(config['session_file']):
- os.remove(config['session_file'])
-
- for (tab, uzbl) in self.tabs.items():
- uzbl.send("exit")
-
- # Add a gobject timer to make sure the application force-quits after a
- # reasonable period. Calling quit when all the tabs haven't had time to
- # close should be a last resort.
- timer = "force-quit"
- timerid = timeout_add(5000, self.quit, timer)
- self._timers[timer] = timerid
-
-
- def quit(self, *args):
- '''Cleanup and quit. Called by delete-event signal.'''
-
- # Close the fifo socket, remove any gobject io event handlers and
- # delete socket.
- self.unlink_fifo_socket()
-
- # Remove all gobject timers that are still ticking.
- for (timerid, gid) in self._timers.items():
- source_remove(gid)
- del self._timers[timerid]
-
- try:
- gtk.main_quit()
-
- except:
- pass
-
-
-if __name__ == "__main__":
-
- # Read from the uzbl config into the global config dictionary.
- readconfig(UZBL_CONFIG, config)
-
- # Build command line parser
- usage = "usage: %prog [OPTIONS] {URIS}..."
- parser = OptionParser(usage=usage)
- parser.add_option('-n', '--no-session', dest='nosession',
- action='store_true', help="ignore session saving a loading.")
- parser.add_option('-v', '--verbose', dest='verbose',
- action='store_true', help='print verbose output.')
-
- # Parse command line options
- (options, uris) = parser.parse_args()
-
- if options.nosession:
- config['save_session'] = False
-
- if options.verbose:
- config['verbose'] = True
-
- if config['json_session']:
- try:
- import simplejson as json
-
- except:
- error("Warning: json_session set but cannot import the python "\
- "module simplejson. Fix: \"set json_session = 0\" or "\
- "install the simplejson python module to remove this warning.")
- config['json_session'] = False
-
- if config['verbose']:
- import pprint
- sys.stderr.write("%s\n" % pprint.pformat(config))
-
- uzbl = UzblTabbed()
-
- # All extra arguments given to uzbl_tabbed.py are interpreted as
- # web-locations to opened in new tabs.
- lasturi = len(uris)-1
- for (index,uri) in enumerate(uris):
- uzbl.new_tab(uri, switch=(index==lasturi))
-
- uzbl.run()