aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--AUTHORS11
-rw-r--r--Makefile4
-rw-r--r--README28
-rw-r--r--docs/COMMUNITY16
-rw-r--r--docs/CONTRIBUTING92
-rw-r--r--docs/FAQ42
-rw-r--r--docs/INSTALL2
-rw-r--r--docs/TODO11
-rw-r--r--examples/config/uzbl/config8
-rwxr-xr-xexamples/data/uzbl/scripts/cookies.py94
-rwxr-xr-xexamples/data/uzbl/scripts/cookies.sh14
-rwxr-xr-xexamples/data/uzbl/scripts/formfiller.sh7
-rwxr-xr-xexamples/data/uzbl/scripts/uzbl_tabbed.py1087
-rwxr-xr-xexamples/data/uzbl/scripts/yank.sh10
-rw-r--r--uzbl.c175
-rw-r--r--uzbl.h43
16 files changed, 1380 insertions, 264 deletions
diff --git a/AUTHORS b/AUTHORS
index 2731d6c..e9b97a9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -21,16 +21,21 @@ Contributors:
Uli Schlachter (psychon) - basic mime_policy_cb & Makefile patch
(uranther) - zoom level
(bobpaul) - session script patches
- Tom Adams (holizz) - few patches, cookies.py, gtkplugk/socket & uzbl_tabbed.py
+ Tom Adams (holizz) - few patches, cookies.py, gtkplug/socket & proof of concept uzbl_tabbed.py
neutralinsomniac - load_progress = 0 fix
- Maximilian Gaß (mxey) - small patches
+ Maximilian Gaß (mxey) - several small patches
Abel Camarillo (00z) - make it compile on OpenBSD
(israellevin) - toggle_zoom_type
(kmeaw) - fix for multibyte utf8 characters segfault
(evocallaghan) - tiny patches
Aaron Griffin (phrakture) - Makefile patches to build on OSX
- Mason Larobina - os.environ.keys() & os.path.join fix in cookies.py
+ Mason Larobina - os.environ.keys() & os.path.join fix in cookies.py, work on uzbl_tabbed.py
(dequis) - Uzbl.run, birectional socket, javascript commands
+ Brendan Taylor (bct) - various bugfixes
+ Chris van Dijk (quigybo) - work on uzbl_tabbed.py
+ Moritz Lenz - small doc fix
+ Sergey Shepelev (temoto) - doc patch
+ Tassilo Horn (tsdh) - $VISUAL patch
Originaly based on http://trac.webkit.org/browser/trunk/WebKitTools/GtkLauncher/main.c
Which is copyrighted:
diff --git a/Makefile b/Makefile
index 053eca2..857494c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,7 @@
+# first entries are for gnu make, 2nd for BSD make. see http://lists.uzbl.org/pipermail/uzbl-dev-uzbl.org/2009-July/000177.html
+
CFLAGS:=-std=c99 $(shell pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -ggdb -Wall -W -DARCH="\"$(shell uname -m)\"" -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT="\"$(shell git log | head -n1 | sed "s/.* //")\"" $(CPPFLAGS)
-CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS)
+CFLAGS!=echo -std=c99 `pkg-config --cflags gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -ggdb -Wall -W -DARCH='"\""'`uname -m`'"\""' -lgthread-2.0 -DG_ERRORCHECK_MUTEXES -DCOMMIT='"\""'`git log | head -n1 | sed "s/.* //"`'"\""' $(CPPFLAGS)
LDFLAGS:=$(shell pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0) -pthread $(LDFLAGS)
LDFLAGS!=echo `pkg-config --libs gtk+-2.0 webkit-1.0 libsoup-2.4 gthread-2.0` -pthread $(LDFLAGS)
diff --git a/README b/README
index a4f6d9f..d5db188 100644
--- a/README
+++ b/README
@@ -9,8 +9,8 @@
* invoke uzbl --help
* to get you started: `XDG_DATA_HOME=/usr/share/uzbl/examples/data XDG_CONFIG_HOME=/usr/share/uzbl/examples/config uzbl --uri www.archlinux.org`
* study the sample config, have a look at all the bindings, and note how you can call the scripts to load new url from history and the bookmarks file
-* note that there is no url bar. all url editing is supposed to happen _outside_ of uzbl.
- For now, you can use the `load_from_*` dmenu based scripts to pick a url or type a new one or write commands into the fifo (see /usr/share/uzbl/docs/CHECKLIST)
+* You can change the url with commands (if you have setup the appropriate keybinds) but to be most effective it's better to do url editing/changing _outside_ of uzbl.
+ Eg, you can use the `load_from_*` dmenu based scripts to pick/edit a url or type a new one.
* If you have questions, you are likely to find answers in the FAQ or in the other documentation.
@@ -26,8 +26,8 @@ Time to change that!
Here are the general ideas:
* each instance of uzbl renders 1 page (eg it's a small wrapper around webkit), no tabbing, tab previews, or speed dial things.
- For "multiple instances management" use your window managers, or scripts.
- This way you can get something much more useful than tabbing (see rationale in docs)
+ For "multiple instances management" use your window managers, scripts or wrappers.
+ This way you can get something much more useful than tabbing by default
* very simple, plaintext , changeable at runtime configuration
* various interfaces for (programmatic) interaction with uzbl (see below)
* customizable keyboard shortcuts in vim or emacs style (whatever user wants)
@@ -61,7 +61,7 @@ There are several interfaces to interact with uzbl:
* uzbl --config <filename>: <filename> will be read line by line, and the commands in it will be executed. useful to configure uzbl at startup.
If you have a file in `$XDG_CONFIG_HOME/uzbl/config` (this expands to ~/.config/uzbl/config on most systems) it will be automatically recognized
-* stdin: you can also write commands into stdin
+* stdin: to write commands into stdin, use `--config -` (or `-c -`)
* interactive: you can enter commands (and bind them to shortcuts, even at runtime)
By default, the behaviour is modal (vi style):
command mode: every keystroke is interpreted to run commands
@@ -290,23 +290,29 @@ The script specific arguments are this:
* cookie handler
$8 GET/PUT
- $9 request address host (if current page url is www.foo.com/somepage, this could be something else than foo, eg advertising from another host)
- $10 request address path
- $11 cookie (only with PUT requests)
+ $9 request address scheme (e.g. http or https)
+ $10 request address host (if current page url is www.foo.com/somepage, this could be something else than foo, eg advertising from another host)
+ $11 request address path
+ $12 cookie (only with PUT requests)
Custom, userdefined scripts (`spawn foo bar`) get first the arguments as specified in the config and then the above 7 are added at the end.
### COMMAND LINE ARGUMENTS
-
- -u, --uri=URI Uri to load (equivalent to 'set uri = URI')
+ uzbl [ uri ]
+ -u, --uri=URI alternative way to load uri on start. (equivalent to 'set uri = URI')
-v, --verbose Whether to print all messages or just errors.
-n, --name=NAME Name of the current instance (defaults to Xorg window id)
- -c, --config=FILE Config file (this is pretty much equivalent to uzbl < FILE )
+ -c, --config=FILE Config file (or `-` to use stdin)
+ -s, --socket=SOCKET Socket ID
+ -V, --version Print the version and exit
--display=DISPLAY X display to use
--help Help
+
+
+
### BUGS
known bugs:
diff --git a/docs/COMMUNITY b/docs/COMMUNITY
index f3159ad..7f86c7b 100644
--- a/docs/COMMUNITY
+++ b/docs/COMMUNITY
@@ -3,7 +3,7 @@ COMMUNITY
### Mailing list
-* Address: uzbl-dev@lists.uzbl.org
+* Address: [uzbl-dev@lists.uzbl.org](mailto:uzbl-dev@lists.uzbl.org)
* [Page](http://lists.uzbl.org/listinfo.cgi/uzbl-dev-uzbl.org)
* [Archives](http://lists.uzbl.org/pipermail/uzbl-dev-uzbl.org/)
@@ -13,21 +13,21 @@ COMMUNITY
### Website
-* http://www.uzbl.org
+* [www.uzbl.org](http://www.uzbl.org/)
### Bugtracker
-* http://www.uzbl.org/bugs/
+* [www.uzbl.org/bugs/](http://www.uzbl.org/bugs/)
### Code repositories
Remember: dieter/master = official stable branch.
If you want to develop, develop against dieter/experimental
-* http://github.com/Dieterbe/uzbl/
-* http://github.com/anydot/uzbl/
-* http://github.com/Barrucadu/uzbl/
-* http://github.com/dusanx/uzbl/
-* http://github.com/robm/uzbl/
+* [github.com/Dieterbe/uzbl](http://github.com/Dieterbe/uzbl/)
+* [github.com/anydot/uzbl](http://github.com/anydot/uzbl/)
+* [github.com/Barrucadu/uzbl](http://github.com/Barrucadu/uzbl/)
+* [github.com/dusanx/uzbl](http://github.com/dusanx/uzbl/)
+* [github.com/robm/uzbl](http://github.com/robm/uzbl/)
There are more contributors who have forks. See:
diff --git a/docs/CONTRIBUTING b/docs/CONTRIBUTING
index 74d1b36..f5d7ebc 100644
--- a/docs/CONTRIBUTING
+++ b/docs/CONTRIBUTING
@@ -1,44 +1,96 @@
### Users
-Right now, the best way to contribute to Uzbl is to use it, hang around in
-our IRC channel, and tell us when things break. If you're feeling more
-adventerous, you can use one of the development branches and give bug
+Just use Uzbl, hang around in our IRC channel, try out different things and report bugs.
+If you're feeling more adventurous, you can use one of the development branches and give bug
reports and suggestions straight to the developer in charge of that, so the
-same problems don't occur when they get merged into the master branch. Have
-a look at the CHECKLIST file to see all the stuff that is supposed to work.
+same problems don't occur when they get merged into the master branch.
Play around with the configs and scripts and see if you can improve things.
+The wiki can be a good source of inspiration.
+You may also find mistakes in our documentation.
### Developers
-If you don't feel like just sending bug reports, by all means dive into the
-code and clone the code to start hacking. (github makes this really easy
-with their "fork" concept). But it's usually a good thing to tell us first
+If you don't feel like just sending bug reports, you can dive into the
+code and start hacking.
+Even if you're not good at C, thanks to uzbl's 'ecosystem' of scripts there
+is a lot that can be done.
+However, it's usually a good idea to tell us first
what you want to do, to avoid unneeded or duplicate work.
-Note that cloning and letting us pull (preferably via github) is way more
-workable then submitting plain patches. Git is good in merging in branches
-even if the base code has changed in the meanwhile. This does not work with
-patches.
+Read on for more info...
-If you're new to Git/github, have no fear:
+### Clone/patch/merge workflow
-* [Github guides (highly recommended)](http://github.com/guides/home)
-* [Guides: Fork a project and submit your modifications](http://github.com/guides/fork-a-project-and-submit-your-modifications)
+1. clone the code with git.
+ Either you host your git repo yourself, or use something userfriendly
+ like [Github](http://www.github.com)
+ If you want to use github, you basically just need to register and click
+ the fork button on [Dieterbe/uzbl](http://github.com/Dieterbe/uzbl)
+ If you're new to Git/github, have no fear:
-Our convention is to develop in the *experimental* branch, and keep only stable, tested stuff in *master*.
-So ideally, all contributors develop in their experimental, that gets merged into the mainline experimental, and after QA it gets merged into the main master.
+ * [Github guides (highly recommended)](http://github.com/guides/home)
+ * [Guides: Fork a project and submit your modifications](http://github.com/guides/fork-a-project-and-submit-your-modifications)
+2. Do your work, test it and push to your repo. It may be interesting to ask
+ others for input on your work. Make sure you do not develop against the
+ master branch! It is meant for stable, tested code.
+ All contributors develop in their experimental or other specific branches,
+ which get merged into the mainline experimental, and after QA it gets merged into the main master.
-### VALGRIND PROFILING
+3. If you think your code should be in the main uzbl code and meets all
+ requirements (see below), then ask Dieter to merge it.
+
+ * send a mail to the mailing list with subject "`Pull request: <title>`"
+ with a short description of your stuff and link to your git clone and
+ which branch you're talking about.
+ We prefer you send us links to git clones, and not just patches. They
+ seem to be easier to merge, especially if the 'base code' has changed in the
+ meanwhile.
+ * If you really don't want to subscribe to the ML and only if it's a
+ small change, you can email Dieter personally.
+ * Please avoid sending private messages on github or asking Dieter to merge on IRC.
+ * If your patch is big, if it will help a lot if you can mention
+ that your code has been tested and is supported by other people.
+
+This is a relatively easy, solid and transparent way to handle all requests in order.
+
+### Patch/branch requirements before merging:
+
+* patches/merges must be about one thing. If you want to work on multiple things, create new branches.
+ I allow exceptions for trivial typo fixes and such, but that's it.
+ This also implies that you also need to update your tree reguraly. Don't fall behind too much. (ie merge from Dieter)
+* any change in functionality that you want merged in must also be documented.
+ There is a readme and some files in the 'docs' directory who should correspond to the code base at all times.
+ Update them, not only for end users but also for your fellow hackers.
+* We recommend you finish your stuff first and then let Dieter know you want your stuff to be merged in, but
+ we know for bigger changes this is not always feasible. Just try to keep the merges about bigger "clean changesets".
+* Your code should not introduce any compile warnings or errors. And also,
+ no regressions but that's harder to check.
+That said, you can always ask us to check on your stuff or ask for advice.
+
+
+### Bugreporting
+
+Bug reports are also welcome, especially the ones that come with a patch ;-)
+Before making a new ticket, check whether the bug is reported already.
+If you want to report a bug and you don't know where the problem in the code
+is, please supply:
+
+* version (commit hash) (see `uzbl --version`)
+* operating system
+* versions of libsoup, webkit, gtk.
+* output of uzbl --verbose (with http_debug set, if relevant)
+
+### Valgrind profiling
$ add this to Makefile header: CFLAGS=-g
$ recompile
$ valgrind --tool=callgrind ./uzbl ....
$ kcachegrind callgrind.out.foo
-### MEMORY LEAK CHECKING
+### Memory leak checking
valgrind --tool=memcheck --leak-check=full ./uzbl
-### DEBUGGING / BACKTRACES
+### Debugging / backtraces
* compile with -ggdb (enabled by default on experimental tree)
* run: `gdb ./uzbl`
diff --git a/docs/FAQ b/docs/FAQ
index b377607..b2a5c9c 100644
--- a/docs/FAQ
+++ b/docs/FAQ
@@ -4,8 +4,8 @@ FAQ
### I just installed uzbl but it doesn't do much. What now?
Uzbl includes very limited default settings (statusbar settings, but no keybinds, history/download handlers etc.)
Look at /usr/share/uzbl/docs/config.h to see the default settings.
-Neither does uzbl create a default config file on startup like some other programs do because we want to give you the freedom to place your config where you want, and to use a config or not.
Have a look in /usr/share/uzbl/examples/configs to see what you can do.
+
If you save a config as $XDG\_CONFIG\_HOME/uzbl/config it will be loaded automatically.
Running with the `--verbose` flag on a command line can also be interesting.
To get you started, try this:
@@ -14,6 +14,26 @@ It will temporarily override your $XDG\_CONFIG\_HOME and $XDG\_DATA\_HOME
variables so you can try the sample stuff directly in /usr/share/uzbl/examples.
If you like what you can do, you can copy the sample stuff into your ~ and edit to your liking.
+### Why don't you just use a reasonable config by default?
+We actually did some attempts to make uzbl "usable by default" but in the
+end we had to conclude it cannot be done because of the following reasons:
+
+ * We don't want to store anything "automagically" in the users home.
+ Some people prefer different file/directory layouts, most of just want to
+ control the files in $HOME themselves.
+ * We considered the option of having a global '/etc/uzbl' which user
+ specific ones could override but that would overcomplicate things.
+ * We adhere to the [FHS](http://www.pathname.com/fhs/) (`man hier`), so
+ `uzbl -c /usr/share/...` is not an option.
+
+So even though we would like to make uzbl more usable by default, we think
+there is no sensible way to do it. Maybe downstream packagers could provide
+a note that users could copy the examples from /usr/share/uzbl/examples into
+their homes, because really, that's all that is needed if you want it to
+just "work" without controlling how yourself. (unless you have no
+xdg variables set, in which case you should be smart enough to edit the sample
+config yourself).
+
### Where is the location bar? How do I change the URL ?
Uzbl has no location bar. All changes to the uri (editing of current uri, typing new uri, loading of uri from bookmarks/history/...) happens *outside* of uzbl.
Have a look at the sample scripts in /usr/share/uzbl/examples. Most of our examples use dmenu which is a nifty little tool to pick an item from a list of items (very
@@ -33,13 +53,15 @@ have lots of keybinding possibilities.
We stick to "one page per uzbl instance" because that's a simple, clean and flexible method. We believe "multiple instances management" is something that must
be handled outside of uzbl by a separate/different program. Here are some solutions:
- * many window managers can (and should) handle this by default. Xmonads tabbed layout, Wmii's stacked layout and so on.
- * you can write a custom script. The only thing you need to do is focus/maximize the instance you want,
+ * Many window managers can (and should) handle this by default. Xmonads tabbed layout, Wmii's stacked layout and so on.
+ * Uzbl supports acting as a GtkPlug to plug into GtkSockets (Xembed) so you can embed uzbl instances in other Gtk applications.
+ This allows several implementatinos, a popular one is [uzbl_tabbed.py](http://www.uzbl.org/wiki/uzbl_tabbed)
+ * If you want highest customizablity, you need the 3rd option:
+ You can also write a custom script. The only thing you need to do is focus/maximize the instance you want,
keep the others out of sight and use tools like dmenu and wmctrl to switch instances.
This allows you to use application-specific properties (such as uzbl tag, name etc).
- For more information about this approach, see docs/multiple-instances-management
- (we'll maybe include some sample scripts in the future)
- * We might implement a GtkPlug (Xembed child) in the future. So you could abuse that.
+ For more information about this approach, see docs/multiple-instances-management.
+ (If you want to work on such script, let us know and we might include it along with the other sample scripts)
### What? No support for bookmarks/history/downloads/cookies/... ? Your project sucks!
We do not support *management* of those things, because we believe a browser should only do browsing. We are firm believers in the unix philosophy.
@@ -94,6 +116,14 @@ They both have advantages and disadvantages:
So, when writing scripts, using fifo's is usually the fastest method (because you do not need to fork another process), so fifo is preferred unless you need a response.
+### What the hell is this 'XDG' stuff??
+You'll notice our example scripts and configs use variables such as `$XDG_CONFIG_HOME` and `$XDG_DATA_HOME`.
+Most of us really like the [xdg basedir spec](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html)
+so our example material folows it. Basically it helps you keeping a clean `$HOME` and it separates config, data and cache.
+If these variables are not defined on your system, it could be that you need to install an xdg package.
+If you don't like this, no one is stopping you from changing the scripts and configs to point to a single `$HOME/.uzbl` directory or whatever you want.
+
+
### Does the world really need another browser?
We did try a lot of browsers, and we do not suffer [NIH](http://en.wikipedia.org/wiki/Not_Invented_Here).
We believe that the approach taken by way too many browsers is wrong. We do not want browsers that try to do everything,
diff --git a/docs/INSTALL b/docs/INSTALL
index 43e139c..be6e85b 100644
--- a/docs/INSTALL
+++ b/docs/INSTALL
@@ -13,7 +13,7 @@ From source
You can pull the code from git or get a tagged tarball.
$ git clone git://github.com/Dieterbe/uzbl.git
- [ $ git checkout master/experimental ] # optional. see below
+ [ $ git checkout origin/experimental ] # optional. see below
$ cd uzbl
$ make
$ sudo make install
diff --git a/docs/TODO b/docs/TODO
index 561757c..00166eb 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -8,12 +8,11 @@ More or less in order of importance/urgency
* shortcuts to focus other instances (see docs/multiple-instances-management)
* recognize -h with GOption?
-* implement a vimperator-like link following scheme.
* implement getting feedback from socket
* scrolling: make page up and page down configurable.
* show % of location in statusbar/title if page doesn't fit entirely on view.
* conditionals in format strings: eg if(SELECTED_URI) { "-> SELECTED_URI" } or another smart way to achieve the same.
-* make default window size configurable, and optional
+* make default window size configurable, and optional if this is not too much work
* on uzbl.org commits overview: add date+time and repository
* how to handle different content types? (text-plain, image/png, application/pdf,... maybe a map of content-type to uzbl/command
xdg already has a spec for this i think
@@ -35,24 +34,25 @@ More or less in order of importance/urgency
* settings iterating "state generator" so we can "open in new window" again.
* handler for (broken) ssl certs.
* handlers for mailto: and maybe other thingies?
-* configure script
+* make sample scripts less depending on bash, but more posix sh.
* proxy_url is not a good var name. it's not a url.
* regex style page searching? so you can do 'or' and 'and' things. flags like case sensitive etc.
* check for real command name, not just the first letter.
* let users attach handlers to the most common events/signals in uzbl.
great use case: automatically calling formfiller for certain sites
-* write little script to open new urls with the urxvt url thing +document.
* document:
stylesheet overridding
formfiller
+ full duplex socket
+ ^X and such binds
link following
+ scrolling in %
webkit inspector usage
scroll commands can take %s, eg scroll 100% for pages
chaining of actions, print (and other actions that aren't documented yet)
overriding variables (such as -u)
variable expansion (@var, @{var}, where do they get expanded? can users have their own vars?, should we merge this with the replacement we do for useragent/window title etc?)
how %s works for the js command
-* consider switching to CMAKE. (someone already ported the makefile etc)
SOMEDAY:
@@ -61,4 +61,3 @@ figure out how webkit intercepts key input
make "disable insert mode" (esc key) configurable
keywords don't work for external commands. is this a problem?
* pass a bit less arguments by default, use the socket to query for them instead, or export the stuff through environment variables, or export them as xorg window properties
-* write a config "generator" that iterates over the Uzbl uzbl and generates the commands needed to become in that state. \ No newline at end of file
diff --git a/examples/config/uzbl/config b/examples/config/uzbl/config
index e7c03dc..3c2b7bf 100644
--- a/examples/config/uzbl/config
+++ b/examples/config/uzbl/config
@@ -11,6 +11,10 @@ set history_handler = spawn $XDG_DATA_HOME/uzbl/scripts/history.sh
set download_handler = spawn $XDG_DATA_HOME/uzbl/scripts/download.sh
set cookie_handler = spawn $XDG_DATA_HOME/uzbl/scripts/cookies.py
+# Control how new windows should open
+#set new_window = sh 'uzbl -u $8' # equivalent to the default behaviour
+#set new_window = sh 'echo uri "$8" > $4' # open in same window
+
# You can bind whatever things (spawn <foo>, script <bar>,..) to some events TODO: make events system more generic
set load_start_handler = set status_message = <span foreground="khaki">wait</span>
set load_commit_handler = set status_message = <span foreground="green">recv</span>
@@ -32,13 +36,13 @@ set font_size = 11
#set enable_spellcheck = 1
## Private browsing
-#set enbale_private = 0
+#set enable_private = 0
## The URI of a stylesheet that is applied to every page
#set stylesheet_uri = http://www.user.com/mystylelesheet.css
## enable/disable JavaScript
-#set disbale_scripts = 1
+#set disable_scripts = 1
## Whether text areas are resizable
#set resizeable_text_areas = 1
diff --git a/examples/data/uzbl/scripts/cookies.py b/examples/data/uzbl/scripts/cookies.py
index 4f80359..10f90fa 100755
--- a/examples/data/uzbl/scripts/cookies.py
+++ b/examples/data/uzbl/scripts/cookies.py
@@ -1,87 +1,37 @@
#!/usr/bin/env python
-import cookielib, sys, os, urllib2
-
-class FakeRequest:
- def __init__(self, argv):
- self.argv = argv
- self.cookies = None
- if len(self.argv) == 12:
- self.cookies = self.argv[11]
- def get_full_url(self):
- #TODO: this is a hack, fix in uzbl.c!
- u = self.get_host()+self.argv[10]
- if self.argv[6].startswith('https'):
- u = 'https://'+u
- else:
- u = 'http://'+u
- return u
- def get_host(self):
- return self.argv[9]
- def get_type(self):
- return self.get_full_url().split(':')[0]
- def is_unverifiable(self):
- return False
- def get_origin_req_host(self):
- return self.argv[9]
- def has_header(self, header):
- if header == 'Cookie':
- return self.cookies!=None
- def get_header(self, header_name, default=None):
- if header_name == 'Cookie' and self.cookies:
- return self.cookies
- else:
- return default
- def header_items(self):
- if self.cookies:
- return [('Cookie',self.cookies)]
- else:
- return []
- def add_unredirected_header(self, key, header):
- if key == 'Cookie':
- self.cookies = header
-
-class FakeHeaders:
- def __init__(self, argv):
- self.argv = argv
- def getallmatchingheaders(self, header):
- if header == 'Set-Cookie' and len(self.argv) == 12:
- return ['Set-Cookie: '+self.argv[11]]
- else:
- return []
- def getheaders(self, header):
- if header == 'Set-Cookie' and len(self.argv) == 12:
- return [self.argv[11]]
- else:
- return []
-class FakeResponse:
- def __init__(self, argv):
- self.argv = argv
- def info(self):
- return FakeHeaders(self.argv)
+import StringIO, cookielib, os, sys, urllib2
if __name__ == '__main__':
+ action = sys.argv[8]
+ uri = urllib2.urlparse.ParseResult(
+ scheme=sys.argv[9],
+ netloc=sys.argv[10],
+ path=sys.argv[11],
+ params='',
+ query='',
+ fragment='').geturl()
+ set_cookie = sys.argv[12] if len(sys.argv)>12 else None
+
if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
- jar = cookielib.MozillaCookieJar(\
- os.path.join(os.environ['XDG_DATA_HOME'],'/uzbl/cookies.txt'))
+ f = os.path.join(os.environ['XDG_DATA_HOME'],'uzbl/cookies.txt')
else:
- jar = cookielib.MozillaCookieJar(\
- os.path.join(os.environ['HOME'],'.local/share/uzbl/cookies.txt'))
+ f = os.path.join(os.environ['HOME'],'.local/share/uzbl/cookies.txt')
+ jar = cookielib.MozillaCookieJar(f)
+
try:
- jar.load()
+ jar.load(ignore_discard=True)
except:
pass
- req = FakeRequest(sys.argv)
-
- action = sys.argv[8]
+ req = urllib2.Request(uri)
if action == 'GET':
jar.add_cookie_header(req)
- if req.cookies:
- print req.cookies
+ if req.has_header('Cookie'):
+ print req.get_header('Cookie')
elif action == 'PUT':
- res = FakeResponse(sys.argv)
+ hdr = urllib2.httplib.HTTPMessage(StringIO.StringIO('Set-Cookie: %s' % set_cookie))
+ res = urllib2.addinfourl(StringIO.StringIO(), hdr, req.get_full_url())
jar.extract_cookies(res,req)
- jar.save(ignore_discard=True) # save session cookies too
- #jar.save() # save everything but session cookies
+ jar.save(ignore_discard=True)
diff --git a/examples/data/uzbl/scripts/cookies.sh b/examples/data/uzbl/scripts/cookies.sh
index 03ddef8..6951b84 100755
--- a/examples/data/uzbl/scripts/cookies.sh
+++ b/examples/data/uzbl/scripts/cookies.sh
@@ -29,7 +29,7 @@ set -n;
cookie_config=${XDG_CONFIG_HOME:-${HOME}/.config}/uzbl/cookies
[ "x$cookie_config" = x ] && exit 1
[ -d "${XDG_DATA_HOME:-${HOME}/.local/share}/uzbl/" ] &&\
-cookie_data=${XDG_DATA_HOME:-${HOME}/.local/share}/uzbl/cookies.txt || exit 1
+cookie_data=${XDG_DATA_HOME:-${HOME}/.local/share}/uzbl/cookies.txt || exit 1
notifier=
#notifier=notify-send
@@ -50,6 +50,7 @@ which zenity &>/dev/null || exit 2
# uri=${uri/http:\/\/} # strip 'http://' part
# host=${uri/\/*/}
action=$8 # GET/PUT
+shift
host=$9
shift
path=$9
@@ -147,14 +148,7 @@ then
[ "x$action" = xGET ] && fetch_cookie && echo "$cookie"
elif ! match DENY $host
then
- [ "x$action" = xPUT ] && \
- cookie=`zenity --entry --title 'Uzbl Cookie handler' \
- --text "Accept this cookie from $host ?" --entry-text="$cookie"` \
- && store_cookie $host
-
- [ "x$action" = xGET ] && fetch_cookie \
- && cookie=`zenity --entry --title 'Uzbl Cookie handler' \
- --text "Submit this cookie to $host ?" --entry-text="$cookie"` \
- && echo $cookie
+ [ "x$action" = xPUT ] && cookie=`zenity --entry --title 'Uzbl Cookie handler' --text "Accept this cookie from $host ?" --entry-text="$cookie"` && store_cookie $host
+ [ "x$action" = xGET ] && fetch_cookie && cookie=`zenity --entry --title 'Uzbl Cookie handler' --text "Submit this cookie to $host ?" --entry-text="$cookie"` && echo $cookie
fi
exit 0
diff --git a/examples/data/uzbl/scripts/formfiller.sh b/examples/data/uzbl/scripts/formfiller.sh
index bbb9d1a..10afaba 100755
--- a/examples/data/uzbl/scripts/formfiller.sh
+++ b/examples/data/uzbl/scripts/formfiller.sh
@@ -16,8 +16,11 @@ keydir=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/forms
[ -d "`dirname $keydir`" ] || exit 1
[ -d "$keydir" ] || mkdir "$keydir"
-#editor=gvim
-editor='urxvt -e vim'
+editor=${VISUAL}
+if [[ -z ${editor} ]]; then
+ #editor='gvim'
+ editor='urxvt -e vim'
+fi
config=$1; shift
pid=$1; shift
diff --git a/examples/data/uzbl/scripts/uzbl_tabbed.py b/examples/data/uzbl/scripts/uzbl_tabbed.py
index 4c3a934..6ed902d 100755
--- a/examples/data/uzbl/scripts/uzbl_tabbed.py
+++ b/examples/data/uzbl/scripts/uzbl_tabbed.py
@@ -1,41 +1,1074 @@
#!/usr/bin/python
-import string, pygtk, gtk, sys, subprocess
+# 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.
+
+
+# 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)
+# switch_to_new_tabs = 1
+#
+# Tab title options:
+# tab_titles = 1
+# new_tab_title = Loading
+# max_title_len = 50
+# show_ellipsis = 1
+#
+# Core options:
+# save_session = 1
+# fifo_dir = /tmp
+# socket_dir = /tmp
+# icon_path = $HOME/.local/share/uzbl/uzbl.png
+# session_file = $HOME/.local/share/uzbl/session
+#
+# Window options:
+# status_background = #303030
+# window_size = 800,800
+#
+# 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>
+#
+# 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!
+# - on demand store the session to file (need binding & command for that)
+
+
+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
+
pygtk.require('2.0')
-def new_tab(nothing):
- socket = gtk.Socket()
- socket.show()
- notebook.append_page(socket, gtk.Label('title goes here'))
- sid = socket.get_id()
- subprocess.call(['sh', '-c', 'uzbl -s %s &'%sid])
+def error(msg):
+ sys.stderr.write("%s\n"%msg)
+
+
+# ============================================================================
+# ::: Default configuration section ::::::::::::::::::::::::::::::::::::::::::
+# ============================================================================
+
+# Location of your uzbl data directory.
+if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']:
+ data_dir = os.path.join(os.environ['XDG_DATA_HOME'], 'uzbl/')
+else:
+ data_dir = os.path.join(os.environ['HOME'], '.local/share/uzbl/')
+if not os.path.exists(data_dir):
+ error("Warning: uzbl data_dir does not exist: %r" % data_dir)
+
+# Location of your uzbl configuration file.
+if 'XDG_CONFIG_HOME' in os.environ.keys() and os.environ['XDG_CONFIG_HOME']:
+ uzbl_config = os.path.join(os.environ['XDG_CONFIG_HOME'], 'uzbl/config')
+else:
+ uzbl_config = os.path.join(os.environ['HOME'],'.config/uzbl/config')
+if not os.path.exists(uzbl_config):
+ error("Warning: Cannot locate your uzbl_config file %r" % uzbl_config)
+
+# 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)
+ 'switch_to_new_tabs': True, # Upon opening a new tab switch to it
+
+ # 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
+
+ # Core options
+ 'save_session': True, # Save session in file when quit
+ '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'),
+ 'session_file': os.path.join(data_dir, 'session'),
+
+ # Window options
+ 'status_background': "#303030", # Default background for all panels
+ 'window_size': "800,800", # width,height in pixels
+
+ # 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
+
+ # 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 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()
+
+ for (key, value) in findsets(rawconfig):
+ key, value = key.strip(), value.strip()
+ if key not in config.keys(): continue
+ if isint(value): value = int(value)
+ config[key] = value
+
+ # Ensure that config keys that relate to paths are expanded.
+ expand = ['fifo_dir', 'socket_dir', 'session_file', 'icon_path']
+ for key in expand:
+ config[key] = os.path.expandvars(config[key])
+
+
+def rmkdir(path):
+ '''Recursively make directories.
+ I.e. `mkdir -p /some/nonexistant/path/`'''
+
+ path, sep = os.path.realpath(path), os.path.sep
+ dirs = path.split(sep)
+ for i in range(2,len(dirs)+1):
+ dir = os.path.join(sep,sep.join(dirs[:i]))
+ if not os.path.exists(dir):
+ os.mkdir(dir)
+
+
+def counter():
+ '''To infinity and beyond!'''
+
+ i = 0
+ while True:
+ i += 1
+ yield i
+
+
+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, switch):
+
+ self.parent = parent
+ self.tab = tab
+ self.fifo_socket = fifo_socket
+ self.socket_file = socket_file
+ self.pid = pid
+ self.title = config['new_tab_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():
+ gobject.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.'''
+
+ self._fifos = {}
+ self._timers = {}
+ self._buffer = ""
+
+ # Once a second is updated with the latest tabs' uris so that when the
+ # window is killed the session is saved.
+ self._tabsuris = []
+ # And index of current page in self._tabsuris
+ self._curpage = 0
+
+ # 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.quit)
+
+ # 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
+
+ # Create the uzbl_tabbed fifo
+ fifo_filename = 'uzbltabbed_%d' % os.getpid()
+ self.fifo_socket = os.path.join(config['fifo_dir'], fifo_filename)
+ self._create_fifo_socket(self.fifo_socket)
+ self._setup_fifo_watcher(self.fifo_socket)
+
+
+ def _create_fifo_socket(self, fifo_socket):
+ '''Create interprocess communication fifo socket.'''
+
+ if os.path.exists(fifo_socket):
+ if not os.access(fifo_socket, os.F_OK | os.R_OK | os.W_OK):
+ os.mkfifo(fifo_socket)
+
+ else:
+ basedir = os.path.dirname(self.fifo_socket)
+ if not os.path.exists(basedir):
+ rmkdir(basedir)
+ os.mkfifo(self.fifo_socket)
+
+ print "Listening on %s" % self.fifo_socket
+
+
+ def _setup_fifo_watcher(self, fifo_socket):
+ '''Open fifo socket fd and setup gobject IO_IN & IO_HUP watchers.
+ Also log the creation of a fd and store the the internal
+ self._watchers dictionary along with the filename of the fd.'''
+
+ if fifo_socket in self._fifos.keys():
+ fd, watchers = self._fifos[fifo_socket]
+ os.close(fd)
+ for watcherid in watchers.keys():
+ gobject.source_remove(watchers[watcherid])
+ del watchers[watcherid]
+
+ del self._fifos[fifo_socket]
+
+ # Re-open fifo and add listeners.
+ fd = os.open(fifo_socket, os.O_RDONLY | os.O_NONBLOCK)
+ watchers = {}
+ self._fifos[fifo_socket] = (fd, watchers)
+ watcher = lambda key, id: watchers.__setitem__(key, id)
+
+ # Watch for incoming data.
+ gid = gobject.io_add_watch(fd, gobject.IO_IN, self.main_fifo_read)
+ watcher('main-fifo-read', gid)
+
+ # Watch for fifo hangups.
+ gid = gobject.io_add_watch(fd, gobject.IO_HUP, self.main_fifo_hangup)
+ watcher('main-fifo-hangup', gid)
+
+
+ def run(self):
+ '''UzblTabbed main function that calls the gtk loop.'''
+
+ # Update tablist timer
+ #timer = "update-tablist"
+ #timerid = gobject.timeout_add(500, self.update_tablist,timer)
+ #self._timers[timer] = timerid
+
+ # Probe clients every second for window titles and location
+ timer = "probe-clients"
+ timerid = gobject.timeout_add(1000, self.probe_clients, timer)
+ self._timers[timer] = timerid
+
+ gtk.main()
+
+
+ def probe_clients(self, timer_call):
+ '''Probe all uzbl clients for up-to-date window titles and uri's.'''
+
+ sockd = {}
+ uriinventory = []
+ tabskeys = self.tabs.keys()
+ notebooklist = list(self.notebook)
+
+ for tab in notebooklist:
+ if tab not in tabskeys: continue
+ uzbl = self.tabs[tab]
+ uriinventory.append(uzbl.uri)
+ uzbl.probe()
+ if uzbl._socket:
+ sockd[uzbl._socket] = uzbl
+
+ self._tabsuris = uriinventory
+ self._curpage = self.notebook.get_current_page()
+
+ 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 main_fifo_hangup(self, fd, cb_condition):
+ '''Handle main fifo socket hangups.'''
+
+ # Close fd, re-open fifo_socket and watch.
+ self._setup_fifo_watcher(self.fifo_socket)
+
+ # And to kill any gobject event handlers calling this 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 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}
+
+ 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]))
+ else:
+ error("parse_command: unknown command %r" % ' '.join(cmd))
+
+
+ def get_tab_by_pid(self, pid):
+ '''Return uzbl instance by pid.'''
+
+ for tab in self.tabs.keys():
+ if self.tabs[tab].pid == pid:
+ return self.tabs[tab]
+
+ return False
+
+
+ def new_tab(self, uri='', 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()
+
+ 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']
+
+
+ # Create meta-instance and spawn child
+ if len(uri.strip()):
+ uri = '--uri %s' % uri
+
+ uzbl = self.UzblInstance(self, tab, fifo_socket, socket_file, pid,\
+ uri, switch)
+ 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 = gobject.timeout_add(100, uzbl.flush, "flush-initial-config")
+ uzbl.timers['flush-initial-config'] = timerid
+
+ self.update_tablist()
+
+
+ def config_uzbl(self, uzbl):
+ '''Send bind commands for tab new/close/next/prev to a uzbl
+ instance.'''
+
+ binds = []
+ bind_format = 'bind %s = sh "echo \\\"%s\\\" > \\\"%s\\\""'
+ bind = lambda key, action: binds.append(bind_format % (key, action, \
+ self.fifo_socket))
+
+ # Keys are defined in the config section
+ # 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')
+
+ # uzbl.send via socket or uzbl.write via fifo, I'll try send.
+ uzbl.send("\n".join(binds))
+
+
+ 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 in uzbl.timers.keys():
+ error("tab_closed: removing timer %r" % timer)
+ gobject.source_remove(uzbl.timers[timer])
+
+ if uzbl._socket:
+ uzbl._socket.close()
+ uzbl._socket = None
+
+ uzbl._fifoout = []
+ uzbl._socketout = []
+ uzbl._kill = True
+ del self.tabs[tab]
+
+ if self.notebook.get_n_pages() == 0:
+ 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']
+ 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)
+
+ tabtitle = uzbl.title[:max_title_len]
+ if show_ellipsis and len(tabtitle) != len(uzbl.title):
+ tabtitle = "%s\xe2\x80\xa6" % tabtitle[:-1] # Show Ellipsis
+
+ 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 tab_titles:
+ pango += tab_format % (tabc, index, textc, tabtitle)
+ else:
+ pango += tab_format % (tabc, textc, index)
+
+ if show_tablist:
+ self.tablist.set_markup(pango)
+
+ return True
+
+
+ def quit(self, *args):
+ '''Cleanup the application and quit. Called by delete-event signal.'''
+
+ for fifo_socket in self._fifos.keys():
+ fd, watchers = self._fifos[fifo_socket]
+ os.close(fd)
+ for watcherid in watchers.keys():
+ gobject.source_remove(watchers[watcherid])
+ del watchers[watcherid]
+
+ del self._fifos[fifo_socket]
+
+ for timerid in self._timers.keys():
+ gobject.source_remove(self._timers[timerid])
+ del self._timers[timerid]
+
+ if os.path.exists(self.fifo_socket):
+ os.unlink(self.fifo_socket)
+ print "Unlinked %s" % self.fifo_socket
+
+ if config['save_session']:
+ session_file = config['session_file']
+ if len(self._tabsuris):
+ if not os.path.isfile(session_file):
+ dirname = os.path.dirname(session_file)
+ if not os.path.isdir(dirname):
+ # Recursive mkdir not rmdir.
+ rmkdir(dirname)
+
+ sessionstr = '\n'.join(self._tabsuris)
+ h = open(session_file, 'w')
+ h.write('current = %s\n%s' % (self._curpage, sessionstr))
+ h.close()
+
+ else:
+ # Notebook has no pages so delete session file if it exists.
+ if os.path.isfile(session_file):
+ os.remove(session_file)
+ gtk.main_quit()
-window = gtk.Window()
-window.show()
-vbox = gtk.VBox()
-vbox.show()
-window.add(vbox)
+if __name__ == "__main__":
+
+ # Read from the uzbl config into the global config dictionary.
+ readconfig(uzbl_config, config)
+
+ uzbl = UzblTabbed()
+
+ if os.path.isfile(os.path.expandvars(config['session_file'])):
+ h = open(os.path.expandvars(config['session_file']),'r')
+ lines = [line.strip() for line in h.readlines()]
+ h.close()
+ current = 0
+ urls = []
+ for line in lines:
+ if line.startswith("current"):
+ current = int(line.split()[-1])
-button = gtk.Button(stock=gtk.STOCK_ADD)
-button.connect('clicked', new_tab)
-button.show()
-vbox.add(button)
+ else:
+ urls.append(line.strip())
-notebook = gtk.Notebook()
-vbox.add(notebook)
-notebook.show()
+ for (index, url) in enumerate(urls):
+ if current == index:
+ uzbl.new_tab(line, True)
+
+ else:
+ uzbl.new_tab(line, False)
-window.connect("destroy", lambda w: gtk.main_quit())
+ if not len(urls):
+ self.new_tab()
-#def plugged_event(widget):
-# print "I (", widget, ") have just had a plug inserted!"
+ else:
+ uzbl.new_tab()
-#socket.connect("plug-added", plugged_event)
-#socket.connect("plug-removed", plugged_event)
+ uzbl.run()
-if len(sys.argv) == 2:
- socket.add_id(long(sys.argv[1]))
-gtk.main() \ No newline at end of file
diff --git a/examples/data/uzbl/scripts/yank.sh b/examples/data/uzbl/scripts/yank.sh
index ddd0a4b..f953ec7 100755
--- a/examples/data/uzbl/scripts/yank.sh
+++ b/examples/data/uzbl/scripts/yank.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# use this script to pipe any variable to xclip, so you have it in your clipboard
# in your uzbl config, make the first argument the number of the (later) argument you want to use (see README for list of args)
# make the 2nd argument one of : primary, secondary, clipboard.
@@ -6,10 +6,8 @@
# bind yurl = spawn ./examples/scripts/yank.sh 6 primary
# bind ytitle = spawn ./examples/scripts/yank.sh 7 clipboard
-clip=xclip
-
which xclip &>/dev/null || exit 1
-[ "x$9" = xprimary -o "x$9" = xsecondary -o "x$9" = xclipboard ] || exit 2
+[ "$9" == primary -o "$9" == secondary -o "$9" == clipboard ] || exit 2
-echo "echo -n '${8}' | $clip -selection $9"
-echo -n "'${8}' | $clip -selection $9"
+echo echo -n "${!8}" '|' xclip -selection $9
+echo -n "${!8}" | xclip -selection $9
diff --git a/uzbl.c b/uzbl.c
index c0cd77f..c31b41c 100644
--- a/uzbl.c
+++ b/uzbl.c
@@ -62,19 +62,21 @@ static Uzbl uzbl;
/* commandline arguments (set initial values for the state variables) */
-static const
+static const
GOptionEntry entries[] =
{
{ "uri", 'u', 0, G_OPTION_ARG_STRING, &uzbl.state.uri,
"Uri to load at startup (equivalent to 'set uri = URI')", "URI" },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &uzbl.state.verbose,
"Whether to print all messages or just errors.", NULL },
- { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
+ { "name", 'n', 0, G_OPTION_ARG_STRING, &uzbl.state.instance_name,
"Name of the current instance (defaults to Xorg window id)", "NAME" },
- { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
+ { "config", 'c', 0, G_OPTION_ARG_STRING, &uzbl.state.config_file,
"Config file (this is pretty much equivalent to uzbl < FILE )", "FILE" },
- { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
+ { "socket", 's', 0, G_OPTION_ARG_INT, &uzbl.state.socket_id,
"Socket ID", "SOCKET" },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &uzbl.behave.print_version,
+ "Print the version and exit", NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
@@ -127,6 +129,7 @@ const struct {
{ "history_handler", PTR(uzbl.behave.history_handler, STR, 1, NULL)},
{ "download_handler", PTR(uzbl.behave.download_handler, STR, 1, NULL)},
{ "cookie_handler", PTR(uzbl.behave.cookie_handler, STR, 1, cmd_cookie_handler)},
+ { "new_window", PTR(uzbl.behave.new_window, STR, 1, cmd_new_window)},
{ "fifo_dir", PTR(uzbl.behave.fifo_dir, STR, 1, cmd_fifo_dir)},
{ "socket_dir", PTR(uzbl.behave.socket_dir, STR, 1, cmd_socket_dir)},
{ "http_debug", PTR(uzbl.behave.http_debug, INT, 1, cmd_http_debug)},
@@ -208,7 +211,7 @@ get_exp_type(gchar *s) {
return EXP_ERR;
}
-/*
+/*
* recurse == 1: don't expand '@(command)@'
* recurse == 2: don't expand '@<java script>@'
*/
@@ -264,7 +267,7 @@ expand(char *s, guint recurse) {
ret[vend-s] = '\0';
}
break;
- case EXP_JS:
+ case EXP_JS:
s++;
strcpy(str_end, ">@");
str_end[2] = '\0';
@@ -276,7 +279,7 @@ expand(char *s, guint recurse) {
break;
}
- if(etype == EXP_SIMPLE_VAR ||
+ if(etype == EXP_SIMPLE_VAR ||
etype == EXP_BRACED_VAR) {
if( (c = g_hash_table_lookup(uzbl.comm.proto_var, ret)) ) {
if(c->type == TYPE_STR)
@@ -292,7 +295,7 @@ expand(char *s, guint recurse) {
else
s = vend+1;
}
- else if(recurse != 1 &&
+ else if(recurse != 1 &&
etype == EXP_EXPR) {
mycmd = expand(ret, 1);
g_spawn_command_line_sync(mycmd, &cmd_stdout, NULL, NULL, &err);
@@ -308,7 +311,7 @@ expand(char *s, guint recurse) {
}
s = vend+2;
}
- else if(recurse != 2 &&
+ else if(recurse != 2 &&
etype == EXP_JS) {
mycmd = expand(ret, 2);
eval_js(uzbl.gui.web_view, mycmd, js_ret);
@@ -366,9 +369,9 @@ read_file_by_line (gchar *path) {
gsize len;
GArray *lines = g_array_new(TRUE, FALSE, sizeof(gchar*));
int i = 0;
-
+
chan = g_io_channel_new_file(path, "r", NULL);
-
+
if (chan) {
while (g_io_channel_read_line(chan, &readbuf, &len, NULL, NULL) == G_IO_STATUS_NORMAL) {
const gchar* val = g_strdup (readbuf);
@@ -376,12 +379,12 @@ read_file_by_line (gchar *path) {
g_free (readbuf);
i ++;
}
-
+
g_io_channel_unref (chan);
} else {
fprintf(stderr, "File '%s' not be read.\n", path);
}
-
+
return lines;
}
@@ -390,7 +393,7 @@ gchar* parseenv (char* string) {
extern char** environ;
gchar* tmpstr = NULL;
int i = 0;
-
+
while (environ[i] != NULL) {
gchar** env = g_strsplit (environ[i], "=", 2);
@@ -438,7 +441,7 @@ clean_up(void) {
g_hash_table_destroy(uzbl.behave.commands);
}
-/* used for html_mode_timeout
+/* used for html_mode_timeout
* be sure to extend this function to use
* more timers if needed in other places
*/
@@ -488,8 +491,8 @@ new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequ
const gchar* uri = webkit_network_request_get_uri (request);
if (uzbl.state.verbose)
printf("New window requested -> %s \n", uri);
- new_window_load_uri(uri);
- return (FALSE);
+ webkit_web_policy_decision_use(policy_decision);
+ return TRUE;
}
static gboolean
@@ -636,13 +639,13 @@ link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpoin
}
static void
-title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
+title_change_cb (WebKitWebView* web_view, GParamSpec param_spec) {
(void) web_view;
- (void) web_frame;
- (void) data;
+ (void) param_spec;
+ const gchar *title = webkit_web_view_get_title(web_view);
if (uzbl.gui.main_title)
g_free (uzbl.gui.main_title);
- uzbl.gui.main_title = g_strdup (title);
+ uzbl.gui.main_title = title ? g_strdup (title) : g_strdup ("(no title)");
update_title();
}
@@ -803,9 +806,11 @@ static void
set_var(WebKitWebView *page, GArray *argv, GString *result) {
(void) page; (void) result;
gchar **split = g_strsplit(argv_idx(argv, 0), "=", 2);
- gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
- set_var_value(g_strstrip(split[0]), value);
- g_free(value);
+ if (split[0] != NULL) {
+ gchar *value = parseenv(g_strdup(split[1] ? g_strchug(split[1]) : " "));
+ set_var_value(g_strstrip(split[0]), value);
+ g_free(value);
+ }
g_strfreev(split);
}
@@ -880,7 +885,7 @@ js_run_command (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject,
(void) function;
(void) thisObject;
(void) exception;
-
+
JSStringRef js_result_string;
GString *result = g_string_new("");
@@ -918,7 +923,7 @@ js_init() {
}
-static void
+static void
eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
WebKitWebFrame *frame;
JSGlobalContextRef context;
@@ -929,20 +934,20 @@ eval_js(WebKitWebView * web_view, gchar *script, GString *result) {
JSValueRef js_result;
JSStringRef js_result_string;
size_t js_result_size;
-
+
js_init();
frame = webkit_web_view_get_main_frame(WEBKIT_WEB_VIEW(web_view));
context = webkit_web_frame_get_global_context(frame);
globalobject = JSContextGetGlobalObject(context);
-
+
/* uzbl javascript namespace */
var_name = JSStringCreateWithUTF8CString("Uzbl");
JSObjectSetProperty(context, globalobject, var_name,
- JSObjectMake(context, uzbl.js.classref, NULL),
+ JSObjectMake(context, uzbl.js.classref, NULL),
kJSClassAttributeNone, NULL);
-
- /* evaluate the script and get return value*/
+
+ /* evaluate the script and get return value*/
js_script = JSStringCreateWithUTF8CString(script);
js_result = JSEvaluateScript(context, js_script, globalobject, NULL, 0, NULL);
if (js_result && !JSValueIsUndefined(context, js_result)) {
@@ -991,7 +996,7 @@ run_external_js (WebKitWebView * web_view, GArray *argv, GString *result) {
i ++;
g_free (line);
}
-
+
if (uzbl.state.verbose)
printf ("External JavaScript file %s loaded\n", argv_idx(argv, 0));
@@ -1015,7 +1020,7 @@ search_text (WebKitWebView *page, GArray *argv, const gboolean forward) {
uzbl.state.searchtx = g_strdup(argv_idx(argv, 0));
}
}
-
+
if (uzbl.state.searchtx) {
if (uzbl.state.verbose)
printf ("Searching: %s\n", uzbl.state.searchtx);
@@ -1045,6 +1050,12 @@ dehilight (WebKitWebView *page, GArray *argv, GString *result) {
static void
new_window_load_uri (const gchar * uri) {
+ if (uzbl.behave.new_window) {
+ GString *s = g_string_new ("");
+ g_string_printf(s, "'%s'", uri);
+ run_handler(uzbl.behave.new_window, s->str);
+ return;
+ }
GString* to_execute = g_string_new ("");
g_string_append_printf (to_execute, "%s --uri '%s'", uzbl.state.executable_path, uri);
int i;
@@ -1067,7 +1078,7 @@ chain (WebKitWebView *page, GArray *argv, GString *result) {
(void) page; (void) result;
gchar *a = NULL;
gchar **parts = NULL;
- guint i = 0;
+ guint i = 0;
while ((a = argv_idx(argv, i++))) {
parts = g_strsplit (a, " ", 2);
parse_command(parts[0], parts[1], result);
@@ -1350,7 +1361,7 @@ run_command (const gchar *command, const guint npre, const gchar **args,
const gboolean sync, char **output_stdout) {
//command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
GError *err = NULL;
-
+
GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
gchar *pid = itos(getpid());
gchar *xwin = itos(uzbl.xwin);
@@ -1368,11 +1379,11 @@ run_command (const gchar *command, const guint npre, const gchar **args,
for (i = npre; i < g_strv_length((gchar**)args); i++)
sharg_append(a, args[i]);
-
+
gboolean result;
if (sync) {
if (*output_stdout) *output_stdout = strfree(*output_stdout);
-
+
result = g_spawn_sync(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, output_stdout, NULL, NULL, &err);
} else result = g_spawn_async(NULL, (gchar **)a->data, NULL, G_SPAWN_SEARCH_PATH,
@@ -1407,7 +1418,7 @@ split_quoted(const gchar* src, const gboolean unquote) {
/* split on unquoted space, return array of strings;
remove a layer of quotes and backslashes if unquote */
if (!src) return NULL;
-
+
gboolean dq = FALSE;
gboolean sq = FALSE;
GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*));
@@ -1450,7 +1461,7 @@ spawn(WebKitWebView *web_view, GArray *argv, GString *result) {
static void
spawn_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
(void)web_view; (void)result;
-
+
if (argv_idx(argv, 0))
run_command(argv_idx(argv, 0), 0, ((const gchar **) (argv->data + sizeof(gchar*))),
TRUE, &uzbl.comm.sync_stdout);
@@ -1463,7 +1474,7 @@ spawn_sh(WebKitWebView *web_view, GArray *argv, GString *result) {
g_printerr ("spawn_sh: shell_cmd is not set!\n");
return;
}
-
+
guint i;
gchar *spacer = g_strdup("");
g_array_insert_val(argv, 1, spacer);
@@ -1484,7 +1495,7 @@ spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
g_printerr ("spawn_sh_sync: shell_cmd is not set!\n");
return;
}
-
+
guint i;
gchar *spacer = g_strdup("");
g_array_insert_val(argv, 1, spacer);
@@ -1492,7 +1503,7 @@ spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
for (i = 1; i < g_strv_length(cmd); i++)
g_array_prepend_val(argv, cmd[i]);
-
+
if (cmd) run_command(cmd[0], g_strv_length(cmd) + 1, (const gchar **) argv->data,
TRUE, &uzbl.comm.sync_stdout);
g_free (spacer);
@@ -1560,7 +1571,6 @@ set_icon() {
} else {
g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon);
}
- g_free (uzbl.gui.icon);
}
static void
@@ -1571,7 +1581,7 @@ cmd_load_uri() {
g_array_free (a, TRUE);
}
-static void
+static void
cmd_always_insert_mode() {
uzbl.behave.insert_mode =
uzbl.behave.always_insert_mode ? TRUE : FALSE;
@@ -1613,7 +1623,7 @@ cmd_font_size() {
if (uzbl.behave.font_size > 0) {
g_object_set (G_OBJECT(ws), "default-font-size", uzbl.behave.font_size, NULL);
}
-
+
if (uzbl.behave.monospace_size > 0) {
g_object_set (G_OBJECT(ws), "default-monospace-font-size",
uzbl.behave.monospace_size, NULL);
@@ -1630,7 +1640,7 @@ cmd_zoom_level() {
static void
cmd_disable_plugins() {
- g_object_set (G_OBJECT(view_settings()), "enable-plugins",
+ g_object_set (G_OBJECT(view_settings()), "enable-plugins",
!uzbl.behave.disable_plugins, NULL);
}
@@ -1677,31 +1687,31 @@ cmd_print_bg() {
uzbl.behave.print_bg, NULL);
}
-static void
+static void
cmd_style_uri() {
g_object_set (G_OBJECT(view_settings()), "user-stylesheet-uri",
uzbl.behave.style_uri, NULL);
}
-static void
+static void
cmd_resizable_txt() {
g_object_set (G_OBJECT(view_settings()), "resizable-text-areas",
uzbl.behave.resizable_txt, NULL);
}
-static void
+static void
cmd_default_encoding() {
g_object_set (G_OBJECT(view_settings()), "default-encoding",
uzbl.behave.default_encoding, NULL);
}
-static void
+static void
cmd_enforce_96dpi() {
g_object_set (G_OBJECT(view_settings()), "enforce-96-dpi",
uzbl.behave.enforce_96dpi, NULL);
}
-static void
+static void
cmd_caret_browsing() {
g_object_set (G_OBJECT(view_settings()), "enable-caret-browsing",
uzbl.behave.caret_browsing, NULL);
@@ -1721,6 +1731,19 @@ cmd_cookie_handler() {
}
static void
+cmd_new_window() {
+ gchar **split = g_strsplit(uzbl.behave.new_window, " ", 2);
+ /* pitfall: doesn't handle chain actions; must the sync_ action manually */
+ if ((g_strcmp0(split[0], "sh") == 0) ||
+ (g_strcmp0(split[0], "spawn") == 0)) {
+ g_free (uzbl.behave.new_window);
+ uzbl.behave.new_window =
+ g_strdup_printf("%s %s", split[0], split[1]);
+ }
+ g_strfreev (split);
+}
+
+static void
cmd_fifo_dir() {
uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir);
}
@@ -1746,7 +1769,7 @@ cmd_modkey() {
buf = g_utf8_strup(uzbl.behave.modkey, -1);
uzbl.behave.modmask = 0;
- if(uzbl.behave.modkey)
+ if(uzbl.behave.modkey)
g_free(uzbl.behave.modkey);
uzbl.behave.modkey = buf;
@@ -2004,7 +2027,7 @@ control_socket(GIOChannel *chan) {
clientsock = accept (g_io_channel_unix_get_fd(chan),
(struct sockaddr *) &remote, &t);
-
+
if ((clientchan = g_io_channel_unix_new(clientsock))) {
g_io_add_watch(clientchan, G_IO_IN|G_IO_HUP,
(GIOFunc) control_client_socket, clientchan);
@@ -2118,9 +2141,11 @@ update_title (void) {
if (b->status_background) {
GdkColor color;
gdk_color_parse (b->status_background, &color);
- //labels and hboxes do not draw their own background. applying this on the window is ok as we the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
+ //labels and hboxes do not draw their own background. applying this on the vbox/main_window is ok as the statusbar is the only affected widget. (if not, we could also use GtkEventBox)
if (uzbl.gui.main_window)
gtk_widget_modify_bg (uzbl.gui.main_window, GTK_STATE_NORMAL, &color);
+ else if (uzbl.gui.plug)
+ gtk_widget_modify_bg (GTK_WIDGET(uzbl.gui.plug), GTK_STATE_NORMAL, &color);
}
} else {
if (b->title_format_long) {
@@ -2221,7 +2246,7 @@ run_keycmd(const gboolean key_ret) {
else exec_paramcmd(act, i); /* otherwise execute the incremental */
break;
}
-
+
g_string_truncate(short_keys, short_keys->len - 1);
}
g_string_free (short_keys, TRUE);
@@ -2256,7 +2281,7 @@ create_browser () {
g->web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (g->web_view));
- g_signal_connect (G_OBJECT (g->web_view), "title-changed", G_CALLBACK (title_change_cb), g->web_view);
+ g_signal_connect (G_OBJECT (g->web_view), "notify::title", G_CALLBACK (title_change_cb), NULL);
g_signal_connect (G_OBJECT (g->web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), g->web_view);
g_signal_connect (G_OBJECT (g->web_view), "load-committed", G_CALLBACK (load_commit_cb), g->web_view);
g_signal_connect (G_OBJECT (g->web_view), "load-started", G_CALLBACK (load_start_cb), g->web_view);
@@ -2374,7 +2399,7 @@ run_handler (const gchar *act, const gchar *args) {
if (g_strcmp0(parts[0], "chain") == 0) {
GString *newargs = g_string_new("");
gchar **chainparts = split_quoted(parts[1], FALSE);
-
+
/* for every argument in the chain, inject the handler args
and make sure the new parts are wrapped in quotes */
gchar **cp = chainparts;
@@ -2382,7 +2407,7 @@ run_handler (const gchar *act, const gchar *args) {
gchar *quotless = NULL;
gchar **spliced_quotless = NULL; // sigh -_-;
gchar **inpart = NULL;
-
+
while (*cp) {
if ((**cp == '\'') || (**cp == '\"')) { /* strip old quotes */
quot = **cp;
@@ -2392,7 +2417,7 @@ run_handler (const gchar *act, const gchar *args) {
spliced_quotless = g_strsplit(quotless, " ", 2);
inpart = inject_handler_args(spliced_quotless[0], spliced_quotless[1], args);
g_strfreev(spliced_quotless);
-
+
g_string_append_printf(newargs, " %c%s %s%c", quot, inpart[0], inpart[1], quot);
g_free(quotless);
g_strfreev(inpart);
@@ -2402,7 +2427,7 @@ run_handler (const gchar *act, const gchar *args) {
parse_command(parts[0], &(newargs->str[1]), NULL);
g_string_free(newargs, TRUE);
g_strfreev(chainparts);
-
+
} else {
gchar **inparts = inject_handler_args(parts[0], parts[1], args);
parse_command(inparts[0], inparts[1], NULL);
@@ -2474,7 +2499,7 @@ find_xdg_file (int xdg_type, char* filename) {
temporary_file = g_strconcat (temporary_string, filename, NULL);
}
}
-
+
//g_free (temporary_string); - segfaults.
if (file_exists (temporary_file)) {
@@ -2491,8 +2516,13 @@ settings_init () {
for (i = 0; default_config[i].command != NULL; i++) {
parse_cmd_line(default_config[i].command, NULL);
}
+
+ if (g_strcmp0(s->config_file, "-") == 0) {
+ s->config_file = NULL;
+ create_stdin();
+ }
- if (!s->config_file) {
+ else if (!s->config_file) {
s->config_file = find_xdg_file (0, "/uzbl/config");
}
@@ -2524,7 +2554,7 @@ static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer use
soup_message_add_header_handler(msg, "got-headers", "Set-Cookie", G_CALLBACK(save_cookies), NULL);
GString *s = g_string_new ("");
SoupURI * soup_uri = soup_message_get_uri(msg);
- g_string_printf(s, "GET '%s' '%s'", soup_uri->host, soup_uri->path);
+ g_string_printf(s, "GET '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path);
run_handler(uzbl.behave.cookie_handler, s->str);
if(uzbl.comm.sync_stdout && strcmp (uzbl.comm.sync_stdout, "") != 0) {
@@ -2534,7 +2564,7 @@ static void handle_cookies (SoupSession *session, SoupMessage *msg, gpointer use
}
if (uzbl.comm.sync_stdout)
uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
-
+
g_string_free(s, TRUE);
}
@@ -2547,7 +2577,7 @@ save_cookies (SoupMessage *msg, gpointer user_data){
cookie = soup_cookie_to_set_cookie_header(ck->data);
SoupURI * soup_uri = soup_message_get_uri(msg);
GString *s = g_string_new ("");
- g_string_printf(s, "PUT '%s' '%s' '%s'", soup_uri->host, soup_uri->path, cookie);
+ g_string_printf(s, "PUT '%s' '%s' '%s' '%s'", soup_uri->scheme, soup_uri->host, soup_uri->path, cookie);
run_handler(uzbl.behave.cookie_handler, s->str);
g_free (cookie);
g_string_free(s, TRUE);
@@ -2686,13 +2716,20 @@ main (int argc, char* argv[]) {
uzbl.state.selected_url = NULL;
uzbl.state.searchtx = NULL;
- GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
+ GOptionContext* context = g_option_context_new ("[ uri ] - load a uri by default");
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
g_option_context_parse (context, &argc, &argv, NULL);
g_option_context_free(context);
-
+
+ if (uzbl.behave.print_version) {
+ printf("Commit: %s\n", COMMIT);
+ exit(0);
+ }
+
gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL);
+ if (argc > 1 && !uzbl.state.uri)
+ uri_override = g_strdup(argv[1]);
gboolean verbose_override = uzbl.state.verbose;
/* initialize hash table */
@@ -2778,11 +2815,9 @@ main (int argc, char* argv[]) {
/* WebInspector */
set_up_inspector();
- create_stdin();
-
if (verbose_override > uzbl.state.verbose)
uzbl.state.verbose = verbose_override;
-
+
if (uri_override) {
set_var_value("uri", uri_override);
g_free(uri_override);
diff --git a/uzbl.h b/uzbl.h
index 37b76fa..d67a84e 100644
--- a/uzbl.h
+++ b/uzbl.h
@@ -1,4 +1,4 @@
-/* -*- c-basic-offset: 4; -*-
+/* -*- c-basic-offset: 4; -*-
* See LICENSE for license details
*
@@ -137,6 +137,7 @@ typedef struct {
gchar* socket_dir;
gchar* download_handler;
gchar* cookie_handler;
+ gchar* new_window;
gboolean always_insert_mode;
gboolean show_status;
gboolean insert_mode;
@@ -153,24 +154,25 @@ typedef struct {
gfloat zoom_level;
guint disable_plugins;
guint disable_scripts;
- guint autoload_img;
- guint autoshrink_img;
+ guint autoload_img;
+ guint autoshrink_img;
guint enable_spellcheck;
- guint enable_private;
- guint print_bg;
- gchar* style_uri;
- guint resizable_txt;
- gchar* default_encoding;
- guint enforce_96dpi;
+ guint enable_private;
+ guint print_bg;
+ gchar* style_uri;
+ guint resizable_txt;
+ gchar* default_encoding;
+ guint enforce_96dpi;
gchar *inject_html;
- guint caret_browsing;
- guint mode;
+ guint caret_browsing;
+ guint mode;
gchar* base_url;
gchar* html_endmarker;
gchar* insert_indicator;
gchar* cmd_indicator;
GString* html_buffer;
- guint html_timeout;
+ guint html_timeout;
+ gboolean print_version;
/* command list: name -> Command */
GHashTable* commands;
@@ -214,7 +216,7 @@ typedef struct {
gchar* default_value;
} XDG_Var;
-XDG_Var XDG[] =
+XDG_Var XDG[] =
{
{ "XDG_CONFIG_HOME", "~/.config" },
{ "XDG_DATA_HOME", "~/.local/share" },
@@ -276,7 +278,7 @@ static void
link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data);
static void
-title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data);
+title_change_cb (WebKitWebView* web_view, GParamSpec param_spec);
static void
progress_change_cb (WebKitWebView* page, gint progress, gpointer data);
@@ -493,6 +495,9 @@ static void
cmd_cookie_handler();
static void
+cmd_new_window();
+
+static void
move_statusbar();
static void
@@ -551,22 +556,22 @@ cmd_enable_private();
static void
cmd_print_bg();
-static void
+static void
cmd_style_uri();
-static void
+static void
cmd_resizable_txt();
-static void
+static void
cmd_default_encoding();
-static void
+static void
cmd_enforce_96dpi();
static void
cmd_inject_html();
-static void
+static void
cmd_caret_browsing();
/* vi: set et ts=4: */