diff options
33 files changed, 1437 insertions, 638 deletions
@@ -10,7 +10,7 @@ RUN_PREFIX?=$(PREFIX) USE_GTK3 = $(shell pkg-config --exists gtk+-3.0 webkitgtk-3.0 && echo 1) ifeq ($(USE_GTK3),1) - REQ_PKGS += gtk+-3.0 webkitgtk-3.0 + REQ_PKGS += gtk+-3.0 webkitgtk-3.0 javascriptcoregtk-3.0 CPPFLAGS = -DG_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED else REQ_PKGS += gtk+-2.0 webkit-1.0 @@ -98,17 +98,7 @@ test-uzbl-tabbed-sandbox: uzbl-browser clean: rm -f uzbl-core - rm -f uzbl-core.o - rm -f events.o - rm -f callbacks.o - rm -f inspector.o - rm -f cookie-jar.o - rm -f util.o - rm -f commands.o - rm -f io.o - rm -f menu.o - rm -f status-bar.o - rm -f variables.o + rm -f *.o find ./examples/ -name "*.pyc" -delete cd ./tests/; $(MAKE) clean rm -rf ./sandbox/ @@ -133,18 +123,12 @@ install-uzbl-core: all install-dirs install -m755 uzbl-core $(INSTALLDIR)/bin/uzbl-core install-event-manager: install-dirs - install -m755 bin/uzbl-event-manager $(INSTALLDIR)/bin/uzbl-event-manager - mv $(INSTALLDIR)/bin/uzbl-event-manager $(INSTALLDIR)/bin/uzbl-event-manager.bak - sed "s#^PREFIX = .*#PREFIX = '$(RUN_PREFIX)'#" < $(INSTALLDIR)/bin/uzbl-event-manager.bak > $(INSTALLDIR)/bin/uzbl-event-manager + sed "s#^PREFIX = .*#PREFIX = '$(RUN_PREFIX)'#" < bin/uzbl-event-manager > $(INSTALLDIR)/bin/uzbl-event-manager chmod 755 $(INSTALLDIR)/bin/uzbl-event-manager - rm $(INSTALLDIR)/bin/uzbl-event-manager.bak install-uzbl-browser: install-dirs install-uzbl-core install-event-manager - install -m755 bin/uzbl-browser $(INSTALLDIR)/bin/uzbl-browser - mv $(INSTALLDIR)/bin/uzbl-browser $(INSTALLDIR)/bin/uzbl-browser.bak - sed 's#^PREFIX=.*#PREFIX=$(RUN_PREFIX)#' < $(INSTALLDIR)/bin/uzbl-browser.bak > $(INSTALLDIR)/bin/uzbl-browser + sed 's#^PREFIX=.*#PREFIX=$(RUN_PREFIX)#' < bin/uzbl-browser > $(INSTALLDIR)/bin/uzbl-browser chmod 755 $(INSTALLDIR)/bin/uzbl-browser - rm $(INSTALLDIR)/bin/uzbl-browser.bak cp -r examples $(INSTALLDIR)/share/uzbl/ chmod 755 $(INSTALLDIR)/share/uzbl/examples/data/scripts/* @@ -141,6 +141,7 @@ The following commands are recognized: - argument can be `begin`, `end`, or an amount given in pixels(?) or as a percentage of the size of the view - set the amount to 100% to scroll a whole page + - argument can be appended with a `!` to scroll absolutely * `reload` - Reload the current page. * `reload_ign_cache` @@ -151,11 +152,6 @@ The following commands are recognized: - Increase the zoom level. * `zoom_out` - Decrease the zoom level. -* `toggle_zoom_type` - - Toggles the variable `zoom_type` between "full-content" and "text-only" - zoom. In "text-only" zoom, only the text of the page is zoomed, while in - "full-content" zoom, images and other page elements are zoomed along with - the text. * `uri <address>` - Attempt to load `<address>`. This is equivalent to `set uri = <address>`. * `js <body>` @@ -163,8 +159,6 @@ The following commands are recognized: - Remember that the commands must not contain line breaks. * `script <file>` - Execute the JavaScript in `<file>`. -* `toggle_status` - - Toggle the display of the status bar. * `spawn <executable> <additional args>` TODO explain path-alike expansion - Runs a command; see EXTERNAL SCRIPTS for details. - `$PATH` is searched, so giving the full path to commands is not necessary. @@ -201,6 +195,11 @@ The following commands are recognized: the status bar react immediately. - If you want to unset a string, use `set` with one space after the equals sign. +* `toggle <var> [possibilities]` + - Cycles the variable `var` through the list of options given in + `possibilities`. + - If `possibilities` are not given and the variable is an int or a float, + toggles between 0 and 1. * `dump_config` - Dumps the current config (which may have been changed at runtime) to stdout. - Uses a format which can be piped into `uzbl` again or saved as a config @@ -313,6 +312,9 @@ file). - `data`: The cookie data. Only included for "PUT" requests. * `scheme_handler`: handler to execute for each URI navigated to - the navigation request will be ignored if handler prints "USED\n" +* `request_handler`: Executed whenever any request is made. The handler can + print a URI to redirect the request (or `about:blank` to effectively cancel it). + If the handler does nothing, the request will continue unchanged. * `download_handler`: executed when a download is started. the handler script should print a path that the download should be saved to, or print nothing to cancel the download. @@ -348,12 +350,15 @@ file). * `monospace_size`: The default size of monospaced font (default 1). * `minimum_font_size`: The minimum font size used to display text (default 1). * `enable_pagecache`: Enable the webkit pagecache (it caches rendered pages for a speedup when you go back or forward in history) (default 0). -* `disable_plugins`: Disable embedded plugin objects (default 0). -* `disable_scripts`: Disable embedded scripting languages (default 0). +* `enable_plugins`: Disable embedded plugin objects (default 0). +* `enable_scripts`: Disable embedded scripting languages (default 0). * `autoload_images`: Automatically load images (default 1). * `autoshrink_images`: Shrink images to window size (default 0). * `enable_spellcheck`: Whether to enable spell checking while typing (default 0). +* `spellcheck_languages`: The languages (in locale `lang_COUNTRY` form, e.g. + `en_CA` or `pt_BR`) to be used for spell checking, separated by commas. + Defaults to the value returned by `gtk_get_default_language`. * `enable_private`: Whether to enable private browsing mode (default 0). * `print_backgrounds`: Print background images? (default 0). * `stylesheet_uri`: Use this to override the pagelayout with a custom @@ -364,8 +369,13 @@ file). * `enforce_96_dpi`: Enforce a resolution of 96 DPI (default 1). * `caret_browsing`: Whether the caret is enabled in the text portion of pages (default 0). +* `enable_cross_file_access`: Whether a page loaded from a `file://` URI can + access the contents of other `file://` URIs. (default 0). * `follow_hint_keys`: keys for keyboard-based navigation and link highlighting +* `ssl_ca_file`: File that contains CA certificates. +* `ssl_verify`: If set to 1, uzbl won't connect to "https" url unless it can + validate certificate presented by remote server against `ssl_ca_file`. #### Constants (not dumpable or writeable) @@ -502,8 +512,9 @@ access to the following environment variables: * `$UZBL_URI`: The URI of the current page. * `$UZBL_TITLE`: The current page title. -Handler scripts (`download_handler`, `cookie_handler`, `scheme_handler` and -`authentication_handler`) are called with special arguments: +Handler scripts (`download_handler`, `cookie_handler`, `scheme_handler`, +`request_handler`, and `authentication_handler`) are called with special +arguments: * download handler @@ -532,6 +543,10 @@ Handler scripts (`download_handler`, `cookie_handler`, `scheme_handler` and - `$1 URI` of the page to be navigated to +* request handler + + - `$1 URI` of the resource which is being requested + * authentication handler: - `$1`: authentication zone unique identifier diff --git a/bin/uzbl-event-manager b/bin/uzbl-event-manager index 64a1354..56253ef 100755 --- a/bin/uzbl-event-manager +++ b/bin/uzbl-event-manager @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # Event Manager for Uzbl # Copyright (c) 2009-2010, Mason Larobina <mason.larobina@gmail.com> @@ -42,7 +42,7 @@ from itertools import count from optparse import OptionParser from select import select from signal import signal, SIGTERM, SIGINT, SIGKILL -from socket import socket, AF_UNIX, SOCK_STREAM +from socket import socket, AF_UNIX, SOCK_STREAM, error as socket_error from traceback import format_exc @@ -370,7 +370,7 @@ class Uzbl(object): data = ''.join(self.child_buffer) try: bsent = self.child_socket.send(data) - except socket.error as e: + except socket_error as e: if e.errno in (errno.EAGAIN, errno.EINTR): self.child_buffer = [data] return @@ -625,13 +625,13 @@ class UzblEventDaemon(object): self.uzbls[child_socket] = Uzbl(self, child_socket) connections += 1 - for uzbl in [self.uzbls[s] for s in writes]: + for uzbl in [self.uzbls[s] for s in writes if s in self.uzbls ]: uzbl.do_send() - for uzbl in [self.uzbls[s] for s in reads]: + for uzbl in [self.uzbls[s] for s in reads if s in self.uzbls]: uzbl.read() - for uzbl in [self.uzbls[s] for s in errors]: + for uzbl in [self.uzbls[s] for s in errors if s in self.uzbls]: uzbl.logger.error('socket read error') uzbl.close() diff --git a/bin/uzbl-tabbed b/bin/uzbl-tabbed index 1a65788..b78a54a 100755 --- a/bin/uzbl-tabbed +++ b/bin/uzbl-tabbed @@ -884,7 +884,11 @@ class UzblTabbed: title = config['new_tab_title'] cmd = ['uzbl-browser', '-n', name, '-s', str(sid), - '--connect-socket', self.socket_path, '--uri', str(uri)] + '--connect-socket', self.socket_path] + + if(uri): + cmd = cmd + ['--uri', str(uri)] + gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH) uzbl = UzblInstance(self, name, uri, title, switch) @@ -1,7 +1,20 @@ FAQ --- +### I just installed uzbl but it doesn't do much. What now? + +There's no "uzbl" command, that's just the name for the whole collection of +tools. The command you're looking for is `uzbl-browser` or `uzbl-tabbed`. + +The central program `uzbl-core` doesn't do many useful things by itself, +it's meant for integration with other tools and scripts. See README. + +Once you've got `uzbl-browser` or `uzbl-tabbed` running, +the first thing to do is look at the [keybindings](http://uzbl.org/keybindings.php). +The second thing to do is explore the default configuration (`~/.config/uzbl/config`). + ### Help, Uzbl isn't responding to any of my keyboard commands! + If the left side of the status bar says `[]` instead of `[Cmd]` when you start `uzbl-browser`, then the event manager isn't starting or its plugins aren't loading. @@ -13,13 +26,6 @@ If you're trying to run uzbl without installing it, you should launch `uzbl-event-manager` yourself with the `--plugin-dir=DIR` option pointing to the location of the event manager plugins (they're in examples/data/plugins/) -### I just installed uzbl but it doesn't do much. What now? -There's no "uzbl" command, that's just the name for the whole collection of -tools. The command you're looking for is `uzbl-browser` or `uzbl-tabbed`. - -The central program `uzbl-core` doesn't do many useful things by itself, -it's meant for integration with other tools and scripts. See README. - ### Where are the widgets (forward button, back button, search bar, etc)? There are none. What we do have is a powerful statusbar and lots of keybinding possibilities. diff --git a/docs/keybindings.html b/docs/keybindings.html new file mode 100644 index 0000000..f3acc66 --- /dev/null +++ b/docs/keybindings.html @@ -0,0 +1,222 @@ +<title>Uzbl Keybindings</title> + +<style type="text/css"> + body { + max-width: 40em; + margin: 0 auto; + } + + body > h1 { + text-align: center; + } + + body > section > h1 { + padding-left: 2em; + } + + dl { + padding-left: 5em; + } + + dt { + float: left; + clear: left; + + padding: 0 0.5em; + + font-family: monospace; + font-weight: bold; + color: #444; + + background: #ccc; + border: 1px solid #999; + } + + dt.long { + display: inline-block; + float: none; + } + + dt + dt { + display: inline-block; + float: none; + margin-left: 0.2em; + } + + dt, dd { + margin-bottom: 1ex; + } +</style> + +<h1>Uzbl Keybindings</h1> + +<p>These keybindings are totally customizable. You can edit +<code>$XDG_CONFIG_HOME/uzbl/config</code> to modify them or add more.</p> + +<section> + <h1>Navigating the Web</h1> + + <dl> + <dt>o</dt> + <dd>Open a URL</dd> + + <dt>O</dt> + <dd>Edit the current URL</dd> + + <dt>S</dt> + <dd>Stop loading</dd> + + <dt>b</dt> + <dd>Go back</dd> + + <dt>m</dt> + <dd>Go forward</dd> + + <dt>U</dt> + <dd>Search the history</dd> + + <dt>r</dt> + <dd>Reload the current page</dd> + + <dt>fl</dt> + <dd>Select a link or form element using the keyboard</dd> + + <dt>Fl</dt> + <dd>Select a link to open in a new window using the keyboard</dd> + + <dt>w</dt> + <dd>Open a new window</dd> + + <dt>c</dt> + <dd>Clone the current window</dd> + </dl> +</section> + +<section> + <h1>Navigating the Page</h1> + + <dl> + <dt>j</dt> + <dd>Scroll down</dd> + + <dt>k</dt> + <dd>Scroll up</dd> + + <dt>h</dt> + <dd>Scroll left</dd> + + <dt>l</dt> + <dd>Scroll right</dd> + + <dt><Page_Up></dt> + <dt><Ctrl>f</dt> + <dd>Page up</dd> + + <dt><Page_Down></dt> + <dt><Ctrl>b</dt> + <dd>Page down</dd> + + <dt><<</dt> + <dt><Home></dt> + <dd>Scroll to top</dd> + + <dt>>></dt> + <dt><End></dt> + <dd>Scroll to bottom</dd> + + <dt>/</dt> + <dd>Search the current page</dd> + + <dt>?</dt> + <dd>Search the current page in reverse</dd> + + <dt>n</dt> + <dd>Next search result</dd> + + <dt>n</dt> + <dd>Previous search result</dd> + </dl> +</section> + +<section> + <h1>Modes</h1> + + <p>Uzbl's default configuration is modal (although a completely modeless + configuration should be possible).</p> + + <p>In "command" mode, everything you type is interpreted by uzbl as a + command.</p> + + <p>In "insert" mode, everything you type is passed to the web page, for + form input or the web page's keybindings.</p> + + <dl> + <dt><Ctrl>i</dt> + <dt>i</dt> + <dd>Switch to insert mode</dd> + + <dt><Escape></dt> + <dt><Ctrl>[</dt> + <dd>Return to command mode</dd> + <dd>Clear the current command</dd> + </dl> +</section> + +<section> + <h1>uzbl-tabbed</h1> + + <p>TODO: Write me.</p> +</section> + +<section> + <h1>Clipboard</h1> + + <p>The terminology here is a bit confusing, please look at + <a href="http://en.wikipedia.org/wiki/X_Window_selection">this article</a> + if you're not familiar with X selections.</p> + + <p>For these commands to work, <code>xclip</code> must be installed.</p> + + <dl> + <dt>yu</dt> + <dd>Copy the current URL to the primary selection</dd> + + <dt>yU</dt> + <dd>Copy the URL of the hovered link to the primary selection</dd> + + <dt>yy</dt> + <dd>Copy the page title to the primary selection</dd> + + <dt>p</dt> + <dd>Go to the URL in the primary selection</dd> + + <dt>P</dt> + <dd>Go to the URL in the clipboard selection</dd> + + <dt>'p</dt> + <dd>Open the URL in the primary selection in a new window</dd> + + <dt class="long"><Shift><Insert></dt> + <dd>Command mode: Paste the primary selection into the status bar</dd> + <dd>Insert mode: Paste the primary selection into the active form + element.</dd> + </dl> +</section> + +<section> + <h1>Advanced Commands</h1> + + <dl> + <dt>s</dt> + <dd>Set a variable</dd> + + <dt>:</dt> + <dd>Issue an uzbl command</dd> + + <dt class="long">!reload</dt> + <dd>Reload configuration file</dd> + + <dt class="long"><Ctrl><Mod1>t</dt> + <dd>Open a terminal that prints events and can issue commands to uzbl</dd> + </dl> +</section> diff --git a/examples/config/config b/examples/config/config index 385a60b..77686a0 100644 --- a/examples/config/config +++ b/examples/config/config @@ -28,8 +28,6 @@ set on_set = request ON_SET set modmap = request MODMAP # request IGNORE_KEY <glob> set ignore_key = request IGNORE_KEY -# request MODKEY_ADDITION <key1> <key2> <keyn> <result> -set modkey_addition = request MODKEY_ADDITION # request TOGGLE_MODES <mode1> <mode2> ... <moden> set toggle_modes = request TOGGLE_MODES @@ -44,6 +42,7 @@ set scripts_dir = @data_home/uzbl:@prefix/share/uzbl/examples/data:scripts # These handlers can't be moved to the new event system yet as we don't # support events that can wait for a response from a script. set scheme_handler = sync_spawn @scripts_dir/scheme.py +#set request_handler = sync_spawn @scripts_dir/request.py set authentication_handler = sync_spawn @scripts_dir/auth.py set download_handler = sync_spawn @scripts_dir/download.sh @@ -145,6 +144,16 @@ set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}) (@(+uname # Drop google analytics tracking cookies (applied after whitelists if any) #request BLACKLIST_COOKIE name '^__utm.$' +# === SSL related configuration ============================================== + +# Set it to certificates store of your distribution, or your own CAfile. +set ssl_ca_file = /etc/ssl/certs/ca-certificates.crt +set ssl_verify = 1 +# Command to toggle ssl_verify value: +@cbind !ssl = chain 'toggle ssl_verify' 'reload' +# Example SSL error handler: +@on_event LOAD_ERROR js var patt=new RegExp('SSL handshake failed'); if (patt.test('%3')) {alert ('%3');} + # === Key binding configuration ============================================== # --- Internal modmapping and ignoring --------------------------------------- @@ -240,6 +249,8 @@ set ebind = @mode_bind global,-insert @cbind ^ = scroll horizontal begin @cbind $ = scroll horizontal end @cbind <Space> = scroll vertical end +@cbind G<Go To:>_ = scroll vertical %r! +@cbind _G<Go To:>_ = scroll horizontal %r! # Navigation binds @cbind b = back @@ -251,12 +262,12 @@ set ebind = @mode_bind global,-insert # Zoom binds @cbind + = zoom_in @cbind - = zoom_out -@cbind T = toggle_zoom_type +@cbind T = toggle zoom_type @cbind 1 = set zoom_level = 1.0 @cbind 2 = set zoom_level = 2.0 # Appearance binds -@cbind t = toggle_status +@cbind t = toggle show_status # Page searching binds @cbind /* = search %s @@ -349,6 +360,8 @@ set follow_hint_keys = 0123456789 @cbind FL* = spawn @scripts_dir/follow.sh \@< uzbl.follow("\@follow_hint_keys", "%s", 'returnuri') >\@ clipboard @cbind fi = spawn @scripts_dir/go_input.sh +@cbind '* = spawn @scripts_dir/follow.sh \@< uzbl.follow.followTextContent("%s", 'click') >\@ + # Form filler binds # This script allows you to configure (per domain) values to fill in form # fields (eg login information) and to fill in these values automatically. diff --git a/examples/config/style.css b/examples/config/style.css index a368aa0..b50b87c 100644 --- a/examples/config/style.css +++ b/examples/config/style.css @@ -25,4 +25,10 @@ color: black !important; } +.uzbl-follow-text-match { + outline: 2px solid invert; + background: #333 !important; + color: white !important; +} + /* vim:set et ts=4: */ diff --git a/examples/data/scripts/follow.js b/examples/data/scripts/follow.js index 5ecdcef..88f80f2 100644 --- a/examples/data/scripts/follow.js +++ b/examples/data/scripts/follow.js @@ -15,16 +15,14 @@ uzbldivid = 'uzbl_link_hints'; uzbl.follow = function() { // Export - charset = arguments[0]; + uzbl.follow.charset = arguments[0]; + if (arguments[2] == 0 || arguments[2] == 'click') { - newwindow = false; - returnuri = false; + uzbl.follow.mode = 'click'; } else if (arguments[2] == 1 || arguments[2] == 'newwindow') { - newwindow = true; - returnuri = false; + uzbl.follow.mode = 'newwindow'; } else if (arguments[2] == 'returnuri') { - newwindow = false; - returnuri = true; + uzbl.follow.mode = 'returnuri'; } var keypress = arguments[1]; @@ -110,7 +108,18 @@ uzbl.follow.elementInViewport = function(el) { // by this script in the given document. uzbl.follow.removeHints = function(doc) { var elements = doc.getElementById(uzbldivid); - if (elements) elements.parentNode.removeChild(elements); + if (elements) + elements.parentNode.removeChild(elements); + + // this returns a live NodeList, which is super-annoying when we to try + // to remove the class. + var followTextMatches = doc.getElementsByClassName('uzbl-follow-text-match'); + var matches = []; + for(var i = 0; i < followTextMatches.length; ++i) + matches.push(followTextMatches[i]); + + for(var i = 0; i < matches.length; ++i) + matches[i].classList.remove('uzbl-follow-text-match'); } // Clears all hints in every document @@ -129,26 +138,32 @@ uzbl.follow.generateHint = function(doc, el, label, top, left) { return hint; } -// Here we choose what to do with an element if we -// want to "follow" it. On form elements we "select" -// or pass the focus, on links we try to perform a click, -// but at least set the href of the link. (needs some improvements) +// this is pointlessly duplicated in uzbl.formfiller +uzbl.follow.textInputTypes = [ + 'text', 'password', 'search', 'email', 'url', 'number', 'range', 'color', + 'date', 'month', 'week', 'time', 'datetime', 'datetime-local' +]; + +// this is pointlessly duplicated in uzbl.formfiller +uzbl.follow.inputTypeIsText = function(type) { + return uzbl.follow.textInputTypes.indexOf(type) >= 0; +} + +// Here we choose what to do with an element that's been selected. +// On text form elements we focus and select the content. On other +// elements we simulate a mouse click. uzbl.follow.clickElem = function(item) { if(!item) return; - if (item instanceof HTMLInputElement) { - var type = item.type; - if (type == 'text' || type == 'file' || type == 'password') { - item.focus(); - item.select(); - return "XXXEMIT_FORM_ACTIVEXXX"; - } - // otherwise fall through to a simulated mouseclick. + if (item instanceof HTMLInputElement && uzbl.follow.inputTypeIsText(item.type)) { + item.focus(); + item.select(); + return "XXXFORM_ACTIVEXXX"; } else if (item instanceof HTMLTextAreaElement || item instanceof HTMLSelectElement) { item.focus(); if(typeof item.select != 'undefined') item.select(); - return "XXXEMIT_FORM_ACTIVEXXX"; + return "XXXFORM_ACTIVEXXX"; } // simulate a mouseclick to activate the element @@ -159,9 +174,7 @@ uzbl.follow.clickElem = function(item) { } // Draw all hints for all elements passed. -uzbl.follow.reDrawHints = function(elems, chars) { - var elements = elems.map(function(pair) { return pair[0] }); - var labels = elems.map(function(pair) { return pair[1].substring(chars) }); +uzbl.follow.reDrawHints = function(elements, len) { // we have to calculate element positions before we modify the DOM // otherwise the elementPosition call slows way down. var positions = elements.map(uzbl.follow.elementPosition); @@ -171,13 +184,15 @@ uzbl.follow.reDrawHints = function(elems, chars) { if (!doc.body) return; doc.hintdiv = doc.createElement('div'); doc.hintdiv.id = uzbldivid; - if(newwindow) doc.hintdiv.className = "new-window"; + if(uzbl.follow.mode == 'newwindow') + doc.hintdiv.className = "new-window"; doc.body.appendChild(doc.hintdiv); }); elements.forEach(function(el, i) { - var label = labels[i]; + var label = uzbl.follow.intToLabel(i, len); var pos = positions[i]; + try { var doc = uzbl.follow.getDocument(el); var h = uzbl.follow.generateHint(doc, el, label, pos[0], pos[1]); @@ -197,19 +212,24 @@ uzbl.follow.labelLength = function(n) { n -= 1; // Our highest key will be n-1 while(n) { keylen += 1; - n = Math.floor(n / charset.length); + n = Math.floor(n / uzbl.follow.charset.length); } return keylen; } -// pass: number -// returns: label -uzbl.follow.intToLabel = function(n) { +// converts an integer 'n' to a string of length 'len' composed of +// characters selected from uzbl.follow.charset. +uzbl.follow.intToLabel = function(n, len) { var label = ''; do { - label = charset.charAt(n % charset.length) + label; - n = Math.floor(n / charset.length); + label = uzbl.follow.charset.charAt(n % uzbl.follow.charset.length) + label; + n = Math.floor(n / uzbl.follow.charset.length); } while(n); + + for (var x = label.length; x < len; x++) { + label = uzbl.follow.charset.charAt(0) + label; + } + return label; } @@ -218,67 +238,91 @@ uzbl.follow.intToLabel = function(n) { uzbl.follow.labelToInt = function(label) { var n = 0; for(var i = 0; i < label.length; ++i) { - n *= charset.length; - n += charset.indexOf(label[i]); + n *= uzbl.follow.charset.length; + n += uzbl.follow.charset.indexOf(label[i]); } return n; } -// Put it all together -uzbl.follow.followLinks = function(follow) { - var s = follow.split(''); - var linknr = this.labelToInt(follow); +uzbl.follow.findMatchingHintId = function(elems, str) { + var linknr = this.labelToInt(str); + + var len = this.labelLength(elems.length); + if (str.length == len && linknr < elems.length && linknr >= 0) { + // an element has been selected! + var el = elems[linknr]; + return [el]; + } + + return elems.filter(function(el, i) { + // return elements whose labels begin with the given str + var label = uzbl.follow.intToLabel(i, len); + return label.slice(0, str.length) == str; + }); +} + +uzbl.follow.getInterestingElements = function() { var followable = 'a, area, textarea, select, input:not([type=hidden]), button, *[onclick]'; var uri = 'a, area, frame, iframe'; //var focusable = 'a, area, textarea, select, input:not([type=hidden]), button, frame, iframe, applet, object'; //var desc = '*[title], img[alt], applet[alt], area[alt], input[alt]'; //var image = 'img, input[type=image]'; - if(newwindow || returnuri) - var res = this.query(uri); + if(uzbl.follow.mode == 'newwindow' || uzbl.follow.mode == 'returnuri') + var elems = this.query(uri); else - var res = this.query(followable); + var elems = this.query(followable); - var elems = res.filter(uzbl.follow.elementInViewport); - var len = this.labelLength(elems.length); + return elems.filter(uzbl.follow.elementInViewport); +} - if (s.length == len && linknr < elems.length && linknr >= 0) { - // an element has been selected! - var el = elems[linknr]; +uzbl.follow.elementSelected = function(el) { + // clear all of our hints + this.clearHints(); + + if (uzbl.follow.mode == 'returnuri') { + var uri = el.src || el.href; + return "XXXRETURNED_URIXXX" + uri + } else if (uzbl.follow.mode == 'newwindow') { + // we're opening a new window using the URL attached to this element + var uri = el.src || el.href; + if(uri.match(/javascript:/)) return; + return "XXXNEW_WINDOWXXX " + uri; + } else { + // we're just going to click the element + return this.clickElem(el); + } +} - // clear all of our hints - this.clearHints(); +uzbl.follow.followTextContent = function(str) { + str = str.toUpperCase(); - if (returnuri) { - var uri = el.src || el.href; - return "XXXRETURNED_URIXXX" + uri - } + var matching = []; - if (newwindow) { - // we're opening a new window using the URL attached to this element - var uri = el.src || el.href; - if(uri.match(/javascript:/)) return; - window.open(uri); - return "XXXRESET_MODEXXX" - } + var elems = uzbl.follow.getInterestingElements(); + elems.forEach(function(el) { + // do a case-insensitive match on element content + if(el.textContent.toUpperCase().match(str)) { + el.classList.add('uzbl-follow-text-match'); + matching.push(el); + } else { + el.classList.remove('uzbl-follow-text-match'); + } + }); - // we're just going to click the element - return this.clickElem(el); - } + if(matching.length == 1) + return uzbl.follow.elementSelected(matching[0]); +} - var leftover = []; - for (var j = 0; j < elems.length; j++) { - var b = true; - var label = this.intToLabel(j); - var n = label.length; - for (n; n < len; n++) - label = charset.charAt(0) + label; - for (var k = 0; k < s.length; k++) - b = b && label.charAt(k) == s[k]; - if (b) - leftover.push([elems[j], label]); - } +uzbl.follow.followLinks = function(str) { + var elems = uzbl.follow.getInterestingElements(); + var leftover = uzbl.follow.findMatchingHintId(elems, str); - this.reDrawHints(leftover, s.length); + if(leftover.length == 1) + return uzbl.follow.elementSelected(leftover[0]); + else { + var len = this.labelLength(elems.length) - str.length; + this.reDrawHints(leftover, len); + } } diff --git a/examples/data/scripts/follow.sh b/examples/data/scripts/follow.sh index 30d3775..de126eb 100755 --- a/examples/data/scripts/follow.sh +++ b/examples/data/scripts/follow.sh @@ -1,22 +1,23 @@ #!/bin/sh -# This scripts acts on the return value of followLinks in follow.js +# This scripts acts on the return value of followLinks in follow.js result=$1 shift -uriaction=$1 -shift - case "$result" in - XXXEMIT_FORM_ACTIVEXXX) + XXXFORM_ACTIVEXXX) # a form element was selected - printf 'event KEYCMD_CLEAR\n' > "$UZBL_FIFO" + echo 'event KEYCMD_CLEAR' > "$UZBL_FIFO" ;; XXXRESET_MODEXXX) # a link was selected, reset uzbl's input mode printf 'set mode=\nevent KEYCMD_CLEAR\n' > "$UZBL_FIFO" ;; + XXXNEW_WINDOWXXX*) + printf "set mode=\nevent KEYCMD_CLEAR\nevent NEW_WINDOW $@\n" > "$UZBL_FIFO" + ;; XXXRETURNED_URIXXX*) + uriaction=$1 uri=${result#XXXRETURNED_URIXXX} case "$uriaction" in diff --git a/examples/data/scripts/formfiller.js b/examples/data/scripts/formfiller.js index 4470661..06db648 100644 --- a/examples/data/scripts/formfiller.js +++ b/examples/data/scripts/formfiller.js @@ -1,14 +1,16 @@ uzbl.formfiller = { - inputTypeIsText: function(type) { - var types = [ 'text', 'password', 'search', 'email', 'url', - 'number', 'range', 'color', 'date', 'month', - 'week', 'time', 'datetime', 'datetime-local' ]; + // this is pointlessly duplicated in uzbl.follow + textInputTypes: [ + 'text', 'password', 'search', 'email', 'url', 'number', 'range', 'color', + 'date', 'month', 'week', 'time', 'datetime', 'datetime-local' + ] - for(var i = 0; i < types.length; ++i) - if(types[i] == type) return true; + , - return false; + // this is pointlessly duplicated in uzbl.follow + inputTypeIsText: function(type) { + return uzbl.formfiller.textInputTypes.indexOf(type) >= 0; } , @@ -27,6 +29,9 @@ uzbl.formfiller = { for( var k = 0; k < inputs.length; ++k ) { var input = inputs[k]; + if ( ! input.name ) { + continue + } if ( uzbl.formfiller.inputTypeIsText(input.type) ) { rv += '%' + escape(input.name) + '(' + input.type + '):' + input.value + '\n'; } else if ( input.type == 'checkbox' || input.type == 'radio' ) { @@ -37,8 +42,10 @@ uzbl.formfiller = { var textareas = allFrames[j].document.getElementsByTagName("textarea"); for( var k = 0; k < textareas.length; ++k ) { var textarea = textareas[k]; - rv += '%' + escape(textarea.name) + '(textarea):\n' + textarea.value.replace(/\n%/g,"\n\\%") + '\n%\n'; - rv += '%' + escape(textarea.name) + '(textarea):\n' + textarea.value.replace(/\n\\/g,"\n\\\\").replace(/\n%/g,"\n\\%") + '%\n'; + if ( ! textarea.name ) { + continue + } + rv += '%' + escape(textarea.name) + '(textarea):\n' + textarea.value.replace(/(^|\n)\\/g,"$1\\\\").replace(/(^|\n)%/g,"$1\\%") + '\n%\n'; } } catch (err) { } diff --git a/examples/data/scripts/formfiller.sh b/examples/data/scripts/formfiller.sh index 394bfbd..52d6ec6 100755 --- a/examples/data/scripts/formfiller.sh +++ b/examples/data/scripts/formfiller.sh @@ -63,6 +63,9 @@ ParseFields () awk '/^%/ { sub ( /%/, "" ) + gsub ( /\\/, "\\\\\\\\" ) + gsub ( /@/, "\\@" ) + gsub ( /"/, "\\\"" ) split( $0, parts, /\(|\)|\{|\}/ ) @@ -73,15 +76,24 @@ ParseFields () printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",%s);\n", parts[1], parts[2], parts[3], field ) - else if ( parts[2] ~ /^textarea$/ ) { + else if ( parts[2] == "textarea" ) { field = "" while (getline) { if ( /^%/ ) break sub ( /^\\/, "" ) + # JavaScript escape + gsub ( /\\/, "\\\\\\\\" ) gsub ( /"/, "\\\"" ) - gsub ( /\\/, "\\\\" ) - field = field $0 "\\\\n" + # To support the possibility of the last line of the textarea + # not being terminated by a newline, we add the newline here. + # The "if (field)" is so that this does not happen in the first + # iteration. + if (field) field = field "\\n" + field = field $0 } + # Uzbl escape + gsub ( /\\/, "\\\\\\\\", field ) + gsub ( /@/, "\\@", field ) printf( "js uzbl.formfiller.insert(\"%s\",\"%s\",\"%s\",0);\n", parts[1], parts[2], field ) } @@ -120,7 +132,6 @@ Load () ParseProfile $option < "$file" \ | ParseFields \ - | sed 's/@/\\@/g' \ > "$UZBL_FIFO" } @@ -136,7 +147,6 @@ Once () test -e "$tmpfile" && ParseFields < "$tmpfile" \ - | sed 's/@/\\@/g' \ > "$UZBL_FIFO" } diff --git a/examples/data/scripts/go_input.js b/examples/data/scripts/go_input.js index 557671f..fedbd90 100644 --- a/examples/data/scripts/go_input.js +++ b/examples/data/scripts/go_input.js @@ -18,7 +18,7 @@ function gi() { else { el.focus(); } - return "XXXEMIT_FORM_ACTIVEXXX"; + return "XXXFORM_ACTIVEXXX"; } } } diff --git a/examples/data/scripts/go_input.sh b/examples/data/scripts/go_input.sh index 9797788..973ae4f 100755 --- a/examples/data/scripts/go_input.sh +++ b/examples/data/scripts/go_input.sh @@ -1,7 +1,7 @@ #!/bin/sh case "$( echo "script @scripts_dir/go_input.js" | socat - "unix-connect:$UZBL_SOCKET" )" in - *XXXEMIT_FORM_ACTIVEXXX*) - echo "event FORM_ACTIVE" > "$UZBL_FIFO" + *XXXFORM_ACTIVEXXX*) + echo 'event KEYCMD_CLEAR' > "$UZBL_FIFO" ;; esac diff --git a/examples/data/scripts/load_cookies.sh b/examples/data/scripts/load_cookies.sh index c7fcc58..380301e 100755 --- a/examples/data/scripts/load_cookies.sh +++ b/examples/data/scripts/load_cookies.sh @@ -13,9 +13,11 @@ BEGIN { scheme["FALSE"] = "http"; } $0 ~ /^#HttpOnly_/ { + gsub(/@/, "\\@") printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", substr($1,length("#HttpOnly_"),length($1)), $3, $6, $7, scheme[$4], $5) } $0 !~ /^#/ { + gsub(/@/, "\\@") printf("add_cookie \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", $1, $3, $6, $7, scheme[$4], $5) } ' "$cookie_file" diff --git a/examples/data/scripts/session.sh b/examples/data/scripts/session.sh index 4e7bfd1..96db4f8 100755 --- a/examples/data/scripts/session.sh +++ b/examples/data/scripts/session.sh @@ -33,7 +33,6 @@ UZBL="uzbl-browser -c \"$UZBL_CONFIG_FILE\"" # add custom flags and whatever her scriptfile="$( readlink -f "$0" )" # this script act="$1" -shift if [ -z "$act" ]; then [ -f "$UZBL_SESSION_FILE" ] && act="launch" || act="endsession" diff --git a/extras/vim/syntax/uzbl.vim b/extras/vim/syntax/uzbl.vim index 1a4172b..2cb4006 100644 --- a/extras/vim/syntax/uzbl.vim +++ b/extras/vim/syntax/uzbl.vim @@ -26,8 +26,8 @@ elseif exists("b:current_syntax") endif syn keyword uzblKeyword back forward scroll reload reload_ign_cache stop -syn keyword uzblKeyword zoom_in zoom_out toggle_zoom_type uri script -syn keyword uzblKeyword toggle_status spawn sync_spawn sync_sh sync_spawn_exec +syn keyword uzblKeyword zoom_in zoom_out toggle uri script +syn keyword uzblKeyword spawn sync_spawn sync_sh sync_spawn_exec syn keyword uzblKeyword exit search search_reverse search_clear dehilight set syn keyword uzblKeyword dump_config dump_config_as_events chain print event syn keyword uzblKeyword request menu_add menu_link_add menu_image_add diff --git a/src/callbacks.c b/src/callbacks.c index 446e868..23b8d55 100644 --- a/src/callbacks.c +++ b/src/callbacks.c @@ -8,6 +8,7 @@ #include "events.h" #include "menu.h" #include "type.h" +#include "variables.h" void link_hover_cb (WebKitWebView *page, const gchar *title, const gchar *link, gpointer data) { @@ -113,16 +114,15 @@ destroy_cb (GtkWidget* widget, gpointer data) { gboolean configure_event_cb(GtkWidget* window, GdkEventConfigure* event) { - (void) window; - (void) event; - gchar *lastgeo = NULL; + (void) window; (void) event; - lastgeo = g_strdup(uzbl.gui.geometry); - retrieve_geometry(); + gchar *last_geo = uzbl.gui.geometry; + gchar *current_geo = get_geometry(); - if(strcmp(lastgeo, uzbl.gui.geometry)) - send_event(GEOMETRY_CHANGED, NULL, TYPE_STR, uzbl.gui.geometry, NULL); - g_free(lastgeo); + if(!last_geo || strcmp(last_geo, current_geo)) + send_event(GEOMETRY_CHANGED, NULL, TYPE_STR, current_geo, NULL); + + g_free(current_geo); return FALSE; } @@ -165,12 +165,13 @@ button_press_cb (GtkWidget* window, GdkEventButton* event) { gboolean propagate = FALSE, sendev = FALSE; + context = get_click_context(NULL); + if(event->type == GDK_BUTTON_PRESS) { if(uzbl.state.last_button) gdk_event_free((GdkEvent *)uzbl.state.last_button); uzbl.state.last_button = (GdkEventButton *)gdk_event_copy((GdkEvent *)event); - context = get_click_context(NULL); /* left click */ if(event->button == 1) { if((context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE)) @@ -357,7 +358,31 @@ request_starting_cb(WebKitWebView *web_view, WebKitWebFrame *frame, WebKitWebRes (void) response; (void) user_data; + const gchar* uri = webkit_network_request_get_uri (request); + + if (uzbl.state.verbose) + printf("Request starting -> %s\n", uri); send_event (REQUEST_STARTING, NULL, TYPE_STR, webkit_network_request_get_uri(request), NULL); + + if (uzbl.behave.request_handler) { + GString *result = g_string_new (""); + GArray *a = g_array_new (TRUE, FALSE, sizeof(gchar*)); + const CommandInfo *c = parse_command_parts(uzbl.behave.request_handler, a); + + if(c) { + g_array_append_val(a, uri); + run_parsed_command(c, a, result); + } + g_array_free(a, TRUE); + + if(result->len > 0) { + char *p = strchr(result->str, '\n' ); + if ( p != NULL ) *p = '\0'; + webkit_network_request_set_uri(request, result->str); + } + + g_string_free(result, TRUE); + } } void diff --git a/src/commands.c b/src/commands.c index 85057b3..b3546a9 100644 --- a/src/commands.c +++ b/src/commands.c @@ -34,6 +34,7 @@ CommandInfo cmdlist[] = { "search_clear", search_clear, TRUE }, { "dehilight", dehilight, 0 }, { "set", set_var, TRUE }, + { "toggle", toggle_var, 0 }, { "dump_config", act_dump_config, 0 }, { "dump_config_as_events", act_dump_config_as_events, 0 }, { "chain", chain, 0 }, @@ -110,23 +111,25 @@ view_go_forward(WebKitWebView *page, GArray *argv, GString *result) { void toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result) { - (void)argv; (void)result; - webkit_web_view_set_full_content_zoom (page, !webkit_web_view_get_full_content_zoom (page)); + (void)page; (void)argv; (void)result; + + int current_type = get_zoom_type(); + set_zoom_type(!current_type); } void toggle_status (WebKitWebView* page, GArray *argv, GString *result) { (void)page; (void)argv; (void)result; - uzbl.behave.show_status = !uzbl.behave.show_status; - - set_show_status(); + int current_status = get_show_status(); + set_show_status(!current_status); } /* * scroll vertical 20 * scroll vertical 20% * scroll vertical -40 + * scroll vertical 20! * scroll vertical begin * scroll vertical end * scroll horizontal 10 @@ -175,6 +178,112 @@ set_var(WebKitWebView *page, GArray *argv, GString *result) { g_strfreev(split); } +void +toggle_var(WebKitWebView *page, GArray *argv, GString *result) { + (void) page; (void) result; + + if(!argv_idx(argv, 0)) + return; + + const gchar *var_name = argv_idx(argv, 0); + + uzbl_cmdprop *c = get_var_c(var_name); + + if(!c) { + set_var_value(var_name, argv_idx(argv, 1)); + return; + } + + switch(c->type) { + case TYPE_STR: + { + const gchar *next; + + if(argv->len >= 3) { + gchar *current = get_var_value_string_c(c); + + guint i = 2; + const gchar *first = argv_idx(argv, 1); + const gchar *this = first; + next = argv_idx(argv, 2); + + while(next && strcmp(current, this)) { + this = next; + next = argv_idx(argv, ++i); + } + + if(!next) + next = first; + + g_free(current); + } else + next = ""; + + set_var_value_string_c(c, next); + break; + } + case TYPE_INT: + { + int current = get_var_value_int_c(c); + int next; + + if(argv->len >= 3) { + guint i = 2; + + int first = strtoul(argv_idx(argv, 1), NULL, 10); + int this = first; + + const gchar *next_s = argv_idx(argv, 2); + + while(next_s && this != current) { + this = strtoul(next_s, NULL, 10); + next_s = argv_idx(argv, ++i); + } + + if(next_s) + next = strtoul(next_s, NULL, 10); + else + next = first; + } else + next = !current; + + set_var_value_int_c(c, next); + break; + } + case TYPE_FLOAT: + { + float current = get_var_value_float_c(c); + float next; + + if(argv->len >= 3) { + guint i = 2; + + float first = strtod(argv_idx(argv, 1), NULL); + float this = first; + + const gchar *next_s = argv_idx(argv, 2); + + while(next_s && this != current) { + this = strtod(next_s, NULL); + next_s = argv_idx(argv, ++i); + } + + if(next_s) + next = strtod(next_s, NULL); + else + next = first; + } else + next = !current; + + set_var_value_float_c(c, next); + break; + } + default: + g_assert_not_reached(); + } + + send_set_var_event(var_name, c); +} void event(WebKitWebView *page, GArray *argv, GString *result) { @@ -418,10 +527,13 @@ chain(WebKitWebView *page, GArray *argv, GString *result) { void close_uzbl (WebKitWebView *page, GArray *argv, GString *result) { (void)page; (void)argv; (void)result; - // hide window a soon as possible to avoid getting stuck with a - // non-response window in the cleanup steps - if (uzbl.gui.main_window) - gtk_widget_destroy(uzbl.gui.main_window); + // hide window a soon as possible to avoid getting stuck with a + // non-response window in the cleanup steps + if (uzbl.gui.main_window) + gtk_widget_destroy(uzbl.gui.main_window); + else if (uzbl.gui.plug) + gtk_widget_destroy(GTK_WIDGET(uzbl.gui.plug)); + gtk_main_quit (); } diff --git a/src/commands.h b/src/commands.h index b8cf095..38bd5f2 100644 --- a/src/commands.h +++ b/src/commands.h @@ -58,6 +58,7 @@ void delete_cookie(WebKitWebView *page, GArray *argv, GString *result); void clear_cookies(WebKitWebView *pag, GArray *argv, GString *result); void download(WebKitWebView *pag, GArray *argv, GString *result); void set_var(WebKitWebView *page, GArray *argv, GString *result); +void toggle_var(WebKitWebView *page, GArray *argv, GString *result); void run_js (WebKitWebView * web_view, GArray *argv, GString *result); void run_external_js (WebKitWebView * web_view, GArray *argv, GString *result); void toggle_zoom_type (WebKitWebView* page, GArray *argv, GString *result); diff --git a/src/events.c b/src/events.c index 45140c0..081a942 100644 --- a/src/events.c +++ b/src/events.c @@ -3,6 +3,8 @@ ** (c) 2009 by Robert Manea */ +#include <glib.h> + #include "uzbl-core.h" #include "events.h" #include "util.h" @@ -58,6 +60,11 @@ const char *event_table[LAST_EVENT] = { "BLUR_ELEMENT" }; +/* for now this is just a alias for GString */ +struct _Event { + GString message; +}; + void event_buffer_timeout(guint sec) { struct itimerval t; @@ -141,12 +148,12 @@ send_event_stdout(GString *msg) { fflush(stdout); } -void -vsend_event(int type, const gchar *custom_event, va_list vargs) { - GString *event_message = g_string_sized_new (512); - +Event * +vformat_event(int type, const gchar *custom_event, va_list vargs) { if (type >= LAST_EVENT) - return; + return NULL; + + GString *event_message = g_string_sized_new (512); const gchar *event = custom_event ? custom_event : event_table[type]; char* str; @@ -160,41 +167,100 @@ vsend_event(int type, const gchar *custom_event, va_list vargs) { case TYPE_INT: g_string_append_printf (event_message, "%d", va_arg (vargs, int)); break; + case TYPE_STR: /* a string that needs to be escaped */ g_string_append_c (event_message, '\''); append_escaped (event_message, va_arg (vargs, char*)); g_string_append_c (event_message, '\''); break; + case TYPE_FORMATTEDSTR: /* a string has already been escaped */ g_string_append (event_message, va_arg (vargs, char*)); break; + + case TYPE_STR_ARRAY: + ; /* gcc is acting up and requires a expression before the variables */ + GArray *a = va_arg (vargs, GArray*); + const char *p; + int i = 0; + while ((p = argv_idx(a, i++))) { + if (i != 0) + g_string_append_c(event_message, ' '); + g_string_append_c (event_message, '\''); + append_escaped (event_message, p); + g_string_append_c (event_message, '\''); + } + break; + case TYPE_NAME: str = va_arg (vargs, char*); g_assert (valid_name (str)); g_string_append (event_message, str); break; + case TYPE_FLOAT: // ‘float’ is promoted to ‘double’ when passed through ‘...’ - g_string_append_printf (event_message, "%.2f", va_arg (vargs, double)); + + // Make sure the formatted double fits in the buffer + if (event_message->allocated_len - event_message->len < G_ASCII_DTOSTR_BUF_SIZE) + g_string_set_size (event_message, event_message->len + G_ASCII_DTOSTR_BUF_SIZE); + + // format in C locale + char *tmp = g_ascii_formatd ( + event_message->str + event_message->len, + event_message->allocated_len - event_message->len, + "%.2f", va_arg (vargs, double)); + event_message->len += strlen(tmp); break; } } - g_string_append_c(event_message, '\n'); + return (Event*) event_message; +} - if (uzbl.state.events_stdout) - send_event_stdout (event_message); - send_event_socket (event_message); +Event * +format_event(int type, const gchar *custom_event, ...) { + va_list vargs, vacopy; + va_start (vargs, custom_event); + va_copy (vacopy, vargs); + Event *event = vformat_event (type, custom_event, vacopy); + va_end (vacopy); + va_end (vargs); + return event; +} + +void +send_formatted_event(const Event *event) { + // A event string is not supposed to contain newlines as it will be + // interpreted as two events + GString *event_message = (GString*)event; - g_string_free (event_message, TRUE); + if (!strchr(event_message->str, '\n')) { + g_string_append_c(event_message, '\n'); + + if (uzbl.state.events_stdout) + send_event_stdout (event_message); + send_event_socket (event_message); + } +} + +void +event_free(Event *event) { + g_string_free ((GString*)event, TRUE); +} + +void +vsend_event(int type, const gchar *custom_event, va_list vargs) { + if (type >= LAST_EVENT) + return; + + Event *event = vformat_event(type, custom_event, vargs); + send_formatted_event (event); + event_free (event); } -/* - * build event string and send over the supported interfaces - * custom_event == NULL indicates an internal event -*/ void send_event(int type, const gchar *custom_event, ...) { va_list vargs, vacopy; @@ -246,6 +312,19 @@ get_modifier_mask(guint state) { return g_string_free(modifiers, FALSE); } +/* backwards compatibility. */ +#if ! GTK_CHECK_VERSION (2, 22, 0) +#define GDK_KEY_Shift_L GDK_Shift_L +#define GDK_KEY_Shift_R GDK_Shift_R +#define GDK_KEY_Control_L GDK_Control_L +#define GDK_KEY_Control_R GDK_Control_R +#define GDK_KEY_Alt_L GDK_Alt_L +#define GDK_KEY_Alt_R GDK_Alt_R +#define GDK_KEY_Super_L GDK_Super_L +#define GDK_KEY_Super_R GDK_Super_R +#define GDK_KEY_ISO_Level3_Shift GDK_ISO_Level3_Shift +#endif + guint key_to_modifier(guint keyval) { /* FIXME * Should really use XGetModifierMapping and/or Xkb to get actual mod keys diff --git a/src/events.h b/src/events.h index 8e89899..73d0712 100644 --- a/src/events.h +++ b/src/events.h @@ -31,18 +31,47 @@ enum event_type { LAST_EVENT }; +typedef struct _Event Event; +struct _Event; + void event_buffer_timeout(guint sec); void replay_buffered_events(); +/* + * build event string + */ +Event * +format_event(int type, const gchar *custom_event, ...) G_GNUC_NULL_TERMINATED; + +Event * +vformat_event(int type, const gchar *custom_event, va_list vargs); + +/* + * send a already formatted event string over the supported interfaces. + * returned event string should be freed by `event_free` + */ void -vsend_event(int type, const gchar *custom_event, va_list vargs); +send_formatted_event(const Event *event); +/* + * frees a event string + */ +void +event_free(Event *event); + +/* + * build event string and send over the supported interfaces + * this is the same as calling `format_event` and then `send_formatted_event` + */ void send_event(int type, const gchar *custom_event, ...) G_GNUC_NULL_TERMINATED; +void +vsend_event(int type, const gchar *custom_event, va_list vargs); + gchar * get_modifier_mask(guint state); @@ -26,8 +26,6 @@ build_stream_name(int type, const gchar* dir) { gboolean control_fifo(GIOChannel *gio, GIOCondition condition) { - if (uzbl.state.verbose) - printf("triggered\n"); gchar *ctl_line; GIOStatus ret; GError *err = NULL; @@ -36,7 +34,7 @@ control_fifo(GIOChannel *gio, GIOCondition condition) { g_error ("Fifo: Read end of pipe died!\n"); if(!gio) - g_error ("Fifo: GIOChannel broke\n"); + g_error ("Fifo: GIOChannel broke\n"); ret = g_io_channel_read_line(gio, &ctl_line, NULL, NULL, &err); if (ret == G_IO_STATUS_ERROR) { @@ -74,9 +72,10 @@ attach_fifo(gchar *path) { } -/*@null@*/ gchar* -init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */ - if (uzbl.comm.fifo_path) { /* get rid of the old fifo if one exists */ +gboolean +init_fifo(const gchar *dir) { + if (uzbl.comm.fifo_path) { + /* we're changing the fifo path, get rid of the old fifo if one exists */ if (unlink(uzbl.comm.fifo_path) == -1) g_warning ("Fifo: Can't unlink old fifo at %s\n", uzbl.comm.fifo_path); g_free(uzbl.comm.fifo_path); @@ -85,32 +84,20 @@ init_fifo(gchar *dir) { /* return dir or, on error, free dir and return NULL */ gchar *path = build_stream_name(FIFO, dir); - if (!file_exists(path)) { - if (mkfifo (path, 0666) == 0 && attach_fifo(path)) { - return dir; - } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno)); - } else { - /* the fifo exists. but is anybody home? */ - int fd = open(path, O_WRONLY|O_NONBLOCK); - if(fd < 0) { - /* some error occurred, presumably nobody's on the read end. - * we can attach ourselves to it. */ - if(attach_fifo(path)) - return dir; - else - g_warning("init_fifo: can't attach to %s: %s\n", path, strerror(errno)); - } else { - /* somebody's there, we can't use that fifo. */ - close(fd); - /* whatever, this instance can live without a fifo. */ - g_warning ("init_fifo: can't create %s: file exists and is occupied\n", path); - } - } + /* if something exists at that path, try to delete it. */ + if (file_exists(path) && unlink(path) == -1) + g_warning ("Fifo: Can't unlink old fifo at %s\n", path); - /* if we got this far, there was an error; cleanup */ - g_free(dir); + if (mkfifo (path, 0666) == 0) { + if(attach_fifo(path)) + return TRUE; + else + g_warning("init_fifo: can't attach to %s: %s\n", path, strerror(errno)); + } else g_warning ("init_fifo: can't create %s: %s\n", path, strerror(errno)); + + /* if we got this far, there was an error; clean up */ g_free(path); - return NULL; + return FALSE; } @@ -299,8 +286,8 @@ attach_socket(gchar *path, struct sockaddr_un *local) { } -/*@null@*/ gchar* -init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL */ +gboolean +init_socket(const gchar *dir) { if (uzbl.comm.socket_path) { /* remove an existing socket should one exist */ if (unlink(uzbl.comm.socket_path) == -1) g_warning ("init_socket: couldn't unlink socket at %s\n", uzbl.comm.socket_path); @@ -309,40 +296,24 @@ init_socket(gchar *dir) { /* return dir or, on error, free dir and return NULL * } if (*dir == ' ') { - g_free(dir); - return NULL; + return FALSE; } - struct sockaddr_un local; gchar *path = build_stream_name(SOCKET, dir); + /* if something exists at that path, try to delete it. */ + if(file_exists(path) && unlink(path) == -1) + g_warning ("Fifo: Can't unlink old fifo at %s\n", path); + + struct sockaddr_un local; local.sun_family = AF_UNIX; strcpy (local.sun_path, path); - if(!file_exists(path) && attach_socket(path, &local)) { - /* it's free for the taking. */ - return dir; - } else { - /* see if anybody's listening on the socket path we want. */ - int sock = socket (AF_UNIX, SOCK_STREAM, 0); - if(connect(sock, (struct sockaddr *) &local, sizeof(local)) < 0) { - /* some error occurred, presumably nobody's listening. - * we can attach ourselves to it. */ - unlink(path); - if(attach_socket(path, &local)) - return dir; - else - g_warning("init_socket: can't attach to existing socket %s: %s\n", path, strerror(errno)); - } else { - /* somebody's there, we can't use that socket path. */ - close(sock); - /* whatever, this instance can live without a socket. */ - g_warning ("init_socket: can't create %s: socket exists and is occupied\n", path); - } - } + if(attach_socket(path, &local)) { + return TRUE; + } else g_warning("init_socket: can't attach to %s: %s\n", path, strerror(errno)); /* if we got this far, there was an error; cleanup */ g_free(path); - g_free(dir); - return NULL; + return FALSE; } @@ -8,14 +8,12 @@ build_stream_name(int type, const gchar *dir); gboolean control_fifo(GIOChannel *gio, GIOCondition condition); -/*@null@*/ gchar* -init_fifo(gchar *dir); +gboolean init_fifo(const gchar *dir); gboolean control_stdin(GIOChannel *gio, GIOCondition condition); void create_stdin(); -/*@null@*/ gchar* -init_socket(gchar *dir); +gboolean init_socket(const gchar *dir); gboolean control_socket(GIOChannel *chan); gboolean control_client_socket(GIOChannel *chan); diff --git a/src/status-bar.c b/src/status-bar.c index 6d4541b..d80dd51 100644 --- a/src/status-bar.c +++ b/src/status-bar.c @@ -1,6 +1,6 @@ #include "status-bar.h" -G_DEFINE_TYPE (UzblStatusBar, uzbl_status_bar, GTK_TYPE_HBOX) +G_DEFINE_TYPE (UzblStatusBar, uzbl_status_bar, GTK_TYPE_BOX) static void uzbl_status_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation); diff --git a/src/status-bar.h b/src/status-bar.h index e972701..cae8905 100644 --- a/src/status-bar.h +++ b/src/status-bar.h @@ -14,14 +14,14 @@ typedef struct _UzblStatusBar UzblStatusBar; typedef struct _UzblStatusBarClass UzblStatusBarClass; struct _UzblStatusBar { - GtkHBox hbox; + GtkBox box; GtkWidget *left_label; GtkWidget *right_label; }; struct _UzblStatusBarClass { - GtkHBoxClass parent_class; + GtkBoxClass parent_class; }; GType uzbl_status_bar_get_type (void) G_GNUC_CONST; @@ -2,23 +2,43 @@ * Uzbl Types */ +#ifndef __UZBL_TYPE__ +#define __UZBL_TYPE__ + enum ptr_type { TYPE_INT = 1, TYPE_STR, TYPE_FLOAT, TYPE_NAME, - TYPE_FORMATTEDSTR // used by send_event + // used by send_event + TYPE_FORMATTEDSTR, + TYPE_STR_ARRAY }; +// I'm doing this instead of just using "uzbl_value *" because this way our +// list of variables can be: +// { .ptr = { .s = &some_char_star }, ... } +// instead of +// { .ptr = (uzbl_value *)&some_char_star, ... } +// which works here, but I suspect has portability issues. +typedef union uzbl_value_ptr_t { + int *i; + float *f; + gchar **s; +} uzbl_value_ptr; + +/* a really generic function pointer. */ +typedef void (*uzbl_fp)(void); + typedef struct { enum ptr_type type; - union { - int *i; - float *f; - gchar **s; - } ptr; + uzbl_value_ptr ptr; int dump; int writeable; - /*@null@*/ void (*func)(void); + + /* the various get_/set_ functions cast these back into something useful. */ + uzbl_fp getter; + uzbl_fp setter; } uzbl_cmdprop; +#endif @@ -151,8 +151,8 @@ argv_idx(const GArray *a, const guint idx) { GString * append_escaped (GString *dest, const gchar *src) { - g_assert(dest); - g_assert(src); + g_assert(dest); + g_assert(src); // Hint that we are going to append another string. int oldlen = dest->len; diff --git a/src/uzbl-core.c b/src/uzbl-core.c index 1e3bed3..770d832 100644 --- a/src/uzbl-core.c +++ b/src/uzbl-core.c @@ -246,7 +246,7 @@ expand(const char* s, guint recurse) { void clean_up(void) { if (uzbl.info.pid_str) { - send_event (INSTANCE_EXIT, NULL, TYPE_INT, getpid(), NULL); + send_event (INSTANCE_EXIT, NULL, TYPE_INT, uzbl.info.pid, NULL); g_free(uzbl.info.pid_str); uzbl.info.pid_str = NULL; } @@ -331,11 +331,15 @@ scroll(GtkAdjustment* bar, gchar *amount_str) { if (*end == '%') value += page_size * amount * 0.01; + else if (*end == '!') + value = amount; else value += amount; max_value = gtk_adjustment_get_upper(bar) - page_size; + if (value < 0) + value = 0; /* don't scroll past the beginning of the page */ if (value > max_value) value = max_value; /* don't scroll past the end of the page */ @@ -617,23 +621,16 @@ run_parsed_command(const CommandInfo *c, GArray *a, GString *result) { if(strcmp("set", c->key) && strcmp("event", c->key) && strcmp("request", c->key)) { - GString *param = g_string_new(""); - const gchar *p; - guint i = 0; - while ((p = argv_idx(a, i++))) { - g_string_append (param, " '"); - append_escaped (param, p); - g_string_append_c (param, '\''); - } + Event *event = format_event (COMMAND_EXECUTED, NULL, + TYPE_NAME, c->key, + TYPE_STR_ARRAY, a, + NULL); - /* might be destructive on array a */ + /* might be destructive on array a */ c->function(uzbl.gui.web_view, a, result); - send_event(COMMAND_EXECUTED, NULL, - TYPE_NAME, c->key, - TYPE_FORMATTEDSTR, param->str, - NULL); - g_string_free(param, TRUE); + send_formatted_event (event); + event_free (event); } else c->function(uzbl.gui.web_view, a, result); @@ -729,7 +726,7 @@ update_title(void) { const gchar *title_format = b->title_format_long; /* Update the status bar if shown */ - if (b->show_status) { + if (get_show_status()) { title_format = b->title_format_short; gchar *parsed = expand(b->status_format, 0); @@ -799,7 +796,6 @@ create_window() { GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 800, 600); - gtk_widget_set_name (window, "Uzbl"); gtk_window_set_title(GTK_WINDOW(window), "Uzbl"); #if GTK_CHECK_VERSION(3,0,0) @@ -912,21 +908,6 @@ void handle_authentication (SoupSession *session, SoupMessage *msg, SoupAuth *au } } -void -retrieve_geometry() { - int w, h, x, y; - GString *buf = g_string_new(""); - - gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h); - gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y); - - g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y); - - if(uzbl.gui.geometry) - g_free(uzbl.gui.geometry); - uzbl.gui.geometry = g_string_free(buf, FALSE); -} - /* Set up gtk, gobject, variable defaults and other things that tests and other * external applications need to do anyhow */ void @@ -964,9 +945,10 @@ initialize(int argc, char** argv) { if (uzbl.state.socket_id || uzbl.state.embed) uzbl.state.plug_mode = TRUE; +#if !GLIB_CHECK_VERSION(2, 31, 0) if (!g_thread_supported()) g_thread_init(NULL); - +#endif /* TODO: move the handler setup to event_buffer_timeout and disarm the * handler in empty_event_buffer? */ @@ -1011,7 +993,13 @@ initialize(int argc, char** argv) { create_scrolled_win(); /* pack the window and the status bar */ + +#if GTK_CHECK_VERSION(3,0,0) + uzbl.gui.vbox = gtk_box_new(FALSE, 0); + gtk_orientable_set_orientation(GTK_ORIENTABLE(uzbl.gui.vbox), GTK_ORIENTATION_VERTICAL); +#else uzbl.gui.vbox = gtk_vbox_new(FALSE, 0); +#endif gtk_box_pack_start(GTK_BOX(uzbl.gui.vbox), uzbl.gui.scrolled_win, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(uzbl.gui.vbox), uzbl.gui.status_bar, FALSE, TRUE, 0); @@ -1022,30 +1010,28 @@ initialize(int argc, char** argv) { /** -- MAIN -- **/ int main (int argc, char* argv[]) { + Window xwin; + initialize(argc, argv); - /* Embedded mode */ if (uzbl.state.plug_mode) { + /* Embedded mode */ uzbl.gui.plug = create_plug(); + gtk_widget_set_name (GTK_WIDGET(uzbl.gui.plug), "Uzbl"); gtk_container_add (GTK_CONTAINER (uzbl.gui.plug), uzbl.gui.vbox); - /* in xembed mode the window has no unique id and thus - * socket/fifo names aren't unique either. - * we use a custom randomizer to create a random id - */ - struct timeval tv; - gettimeofday(&tv, NULL); - srand((unsigned int)tv.tv_sec*tv.tv_usec); - uzbl.xwin = rand(); - } - - /* Windowed mode */ - else { + } else { + /* Windowed mode */ uzbl.gui.main_window = create_window(); + gtk_widget_set_name (uzbl.gui.main_window, "Uzbl"); gtk_container_add (GTK_CONTAINER (uzbl.gui.main_window), uzbl.gui.vbox); + /* We need to ensure there is a window, before we can get XID */ gtk_widget_realize (GTK_WIDGET (uzbl.gui.main_window)); + xwin = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (uzbl.gui.main_window))); - uzbl.xwin = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (uzbl.gui.main_window))); + gchar *xwin_str = g_strdup_printf("%d", (int)xwin); + g_setenv("UZBL_XID", xwin_str, TRUE); + g_free(xwin_str); gtk_widget_grab_focus (GTK_WIDGET (uzbl.gui.web_view)); } @@ -1064,17 +1050,14 @@ main (int argc, char* argv[]) { "signal::changed", (GCallback)scroll_horiz_cb, NULL, NULL); - gchar *xwin = g_strdup_printf("%d", (int)uzbl.xwin); - g_setenv("UZBL_XID", xwin, TRUE); + uzbl.info.pid = getpid(); + uzbl.info.pid_str = g_strdup_printf("%d", uzbl.info.pid); + g_setenv("UZBL_PID", uzbl.info.pid_str, TRUE); if(!uzbl.state.instance_name) - uzbl.state.instance_name = g_strdup(xwin); - - g_free(xwin); + uzbl.state.instance_name = uzbl.info.pid_str; - uzbl.info.pid_str = g_strdup_printf("%d", getpid()); - g_setenv("UZBL_PID", uzbl.info.pid_str, TRUE); - send_event(INSTANCE_START, NULL, TYPE_INT, getpid(), NULL); + send_event(INSTANCE_START, NULL, TYPE_INT, uzbl.info.pid, NULL); if (uzbl.state.plug_mode) { send_event(PLUG_CREATED, NULL, TYPE_INT, gtk_plug_get_id (uzbl.gui.plug), NULL); @@ -1084,11 +1067,10 @@ main (int argc, char* argv[]) { builtins(); /* Check uzbl is in window mode before getting/setting geometry */ - if (uzbl.gui.main_window) { - if (uzbl.gui.geometry) - set_geometry(); - else - retrieve_geometry(); + if (uzbl.gui.main_window && uzbl.gui.geometry) { + gchar *geometry = g_strdup(uzbl.gui.geometry); + set_geometry(geometry); + g_free(geometry); } gchar *uri_override = (uzbl.state.uri ? g_strdup(uzbl.state.uri) : NULL); @@ -1101,10 +1083,7 @@ main (int argc, char* argv[]) { settings_init(); /* Update status bar */ - if (!uzbl.behave.show_status) - gtk_widget_hide(uzbl.gui.status_bar); - else - update_title(); + update_title(); /* WebInspector */ set_up_inspector(); @@ -1131,13 +1110,12 @@ main (int argc, char* argv[]) { if (uzbl.state.socket_id) printf("plug_id %i\n", (int)gtk_plug_get_id(uzbl.gui.plug)); else - printf("window_id %i\n",(int) uzbl.xwin); + printf("window_id %i\n",(int) xwin); printf("pid %i\n", getpid ()); printf("name: %s\n", uzbl.state.instance_name); printf("commit: %s\n", uzbl.info.commit); } - gtk_main(); /* Cleanup and exit*/ diff --git a/src/uzbl-core.h b/src/uzbl-core.h index c84380e..29b7b64 100644 --- a/src/uzbl-core.h +++ b/src/uzbl-core.h @@ -74,7 +74,6 @@ typedef struct { WebKitWebView* web_view; gchar* main_title; gchar* icon; - gchar* window_role; /* WebInspector */ GtkWidget* inspector_window; @@ -131,6 +130,11 @@ typedef struct { gint max_conns_host; } Network; +/* ssl */ +typedef struct { + gchar *ca_file; + gchar *verify_cert; +} Ssl; /* Behaviour */ typedef struct { @@ -138,7 +142,6 @@ typedef struct { gchar* status_format; gchar* status_format_right; gchar* status_background; - gboolean show_status; gboolean status_top; /* Window title */ @@ -152,50 +155,20 @@ typedef struct { /* Handlers */ gchar* authentication_handler; gchar* scheme_handler; + gchar* request_handler; gchar* download_handler; - /* Fonts */ - gchar* default_font_family; - gchar* monospace_font_family; - gchar* sans_serif_font_family; - gchar* serif_font_family; - gchar* fantasy_font_family; - gchar* cursive_font_family; - gboolean forward_keys; guint http_debug; gchar* shell_cmd; guint view_source; - /* WebKitWebSettings exports */ - guint font_size; - guint monospace_size; - guint minimum_font_size; - gfloat zoom_level; - gboolean zoom_type; - guint enable_pagecache; - guint disable_plugins; - guint disable_scripts; - guint autoload_img; - guint autoshrink_img; - guint enable_spellcheck; - gchar* spellcheck_languages; - guint enable_private; - guint print_bg; - gchar* style_uri; - guint resizable_txt; - gchar* default_encoding; - gchar* current_encoding; - guint enforce_96dpi; - gchar *inject_html; - guint caret_browsing; - guint javascript_windows; gboolean print_version; /* command list: (key)name -> (value)Command */ GHashTable* commands; /* variables: (key)name -> (value)uzbl_cmdprop */ - GHashTable *proto_var; + GHashTable* proto_var; } Behaviour; @@ -206,6 +179,8 @@ typedef struct { int webkit_micro; gchar* arch; gchar* commit; + + pid_t pid; gchar* pid_str; } Info; @@ -215,11 +190,10 @@ typedef struct { GUI gui; State state; Network net; + Ssl ssl; Behaviour behave; Communication comm; Info info; - - Window xwin; } UzblCore; extern UzblCore uzbl; /* Main Uzbl object */ @@ -247,7 +221,6 @@ gchar* expand(const char* s, guint recurse); gboolean run_command(const gchar *command, const gchar **args, const gboolean sync, char **output_stdout); void run_command_file(const gchar *path); -void parse_command(const char *cmd, const char *param, GString *result); void parse_cmd_line(const char *ctl_line, GString *result); const CommandInfo * parse_command_parts(const gchar *line, GArray *a); diff --git a/src/variables.c b/src/variables.c index b72c4b0..8d874c2 100644 --- a/src/variables.c +++ b/src/variables.c @@ -4,31 +4,39 @@ #include "events.h" #include "io.h" #include "util.h" -#include "type.h" + +uzbl_cmdprop * +get_var_c(const gchar *name) { + return g_hash_table_lookup(uzbl.behave.proto_var, name); +} void send_set_var_event(const char *name, const uzbl_cmdprop *c) { /* check for the variable type */ switch(c->type) { case TYPE_STR: + { + gchar *v = get_var_value_string_c(c); send_event (VARIABLE_SET, NULL, TYPE_NAME, name, TYPE_NAME, "str", - TYPE_STR, *c->ptr.s ? *c->ptr.s : " ", + TYPE_STR, v, NULL); + g_free(v); break; + } case TYPE_INT: send_event (VARIABLE_SET, NULL, TYPE_NAME, name, TYPE_NAME, "int", - TYPE_INT, *c->ptr.i, + TYPE_INT, get_var_value_int_c(c), NULL); break; case TYPE_FLOAT: send_event (VARIABLE_SET, NULL, TYPE_NAME, name, TYPE_NAME, "float", - TYPE_FLOAT, *c->ptr.f, + TYPE_FLOAT, get_var_value_float_c(c), NULL); break; default: @@ -38,81 +46,162 @@ send_set_var_event(const char *name, const uzbl_cmdprop *c) { void expand_variable(GString *buf, const gchar *name) { - uzbl_cmdprop* c; - if((c = g_hash_table_lookup(uzbl.behave.proto_var, name))) { - if(c->type == TYPE_STR && *c->ptr.s != NULL) { - g_string_append(buf, (gchar *)*c->ptr.s); - } - else if(c->type == TYPE_INT) { - g_string_append_printf(buf, "%d", *c->ptr.i); - } - else if(c->type == TYPE_FLOAT) { - g_string_append_printf(buf, "%f", *c->ptr.f); - } + uzbl_cmdprop* c = get_var_c(name); + if(c) { + if(c->type == TYPE_STR) { + gchar *v = get_var_value_string_c(c); + g_string_append(buf, v); + g_free(v); + } else if(c->type == TYPE_INT) + g_string_append_printf(buf, "%d", get_var_value_int_c(c)); + else if(c->type == TYPE_FLOAT) + g_string_append_printf(buf, "%f", get_var_value_float_c(c)); } } +void +set_var_value_string_c(uzbl_cmdprop *c, const gchar *val) { + if(c->setter) + ((void (*)(const gchar *))c->setter)(val); + else { + g_free(*(c->ptr.s)); + *(c->ptr.s) = g_strdup(val); + } +} + +void +set_var_value_int_c(uzbl_cmdprop *c, int i) { + if(c->setter) + ((void (*)(int))c->setter)(i); + else + *(c->ptr.i) = i; +} + +void +set_var_value_float_c(uzbl_cmdprop *c, float f) { + if(c->setter) + ((void (*)(float))c->setter)(f); + else + *(c->ptr.f) = f; +} + gboolean set_var_value(const gchar *name, gchar *val) { - uzbl_cmdprop *c = NULL; - char *endp = NULL; - char *buf = NULL; - g_assert(val != NULL); - if( (c = g_hash_table_lookup(uzbl.behave.proto_var, name)) ) { + uzbl_cmdprop *c = get_var_c(name); + + if(c) { if(!c->writeable) return FALSE; switch(c->type) { case TYPE_STR: - buf = g_strdup(val); - g_free(*c->ptr.s); - *c->ptr.s = buf; + set_var_value_string_c(c, val); break; case TYPE_INT: - *c->ptr.i = (int)strtoul(val, &endp, 10); + { + int i = (int)strtoul(val, NULL, 10); + set_var_value_int_c(c, i); break; + } case TYPE_FLOAT: - *c->ptr.f = strtod(val, &endp); + { + float f = strtod(val, NULL); + set_var_value_float_c(c, f); break; + } default: g_assert_not_reached(); } send_set_var_event(name, c); - - /* invoke a command specific function */ - if(c->func) c->func(); } else { - /* check wether name violates our naming scheme */ + /* a custom var that has not been set. */ + /* check whether name violates our naming scheme */ if(!valid_name(name)) { if (uzbl.state.verbose) printf("Invalid variable name: %s\n", name); return FALSE; } - /* custom vars */ + /* create the cmdprop */ c = g_malloc(sizeof(uzbl_cmdprop)); - c->type = TYPE_STR; - c->dump = 0; - c->func = NULL; - c->writeable = 1; - buf = g_strdup(val); - c->ptr.s = g_malloc(sizeof(char *)); - *c->ptr.s = buf; + c->type = TYPE_STR; + c->dump = 0; + c->getter = NULL; + c->setter = NULL; + c->writeable = 1; + + c->ptr.s = g_malloc(sizeof(gchar*)); + g_hash_table_insert(uzbl.behave.proto_var, g_strdup(name), (gpointer) c); - send_event (VARIABLE_SET, NULL, - TYPE_NAME, name, - TYPE_NAME, "str", - TYPE_STR, buf, - NULL); + /* set the value. */ + *(c->ptr.s) = g_strdup(val); + + send_set_var_event(name, c); } update_title(); return TRUE; } +gchar* +get_var_value_string_c(const uzbl_cmdprop *c) { + if(!c) return NULL; + + gchar *result = NULL; + + if(c->getter) { + result = ((gchar *(*)())c->getter)(); + } else if(c->ptr.s) + result = g_strdup(*(c->ptr.s)); + + return result ? result : g_strdup(""); +} + +gchar* +get_var_value_string(const gchar *name) { + uzbl_cmdprop *c = get_var_c(name); + return get_var_value_string_c(c); +} + +int +get_var_value_int_c(const uzbl_cmdprop *c) { + if(!c) return 0; + + if(c->getter) { + return ((int (*)())c->getter)(); + } else if(c->ptr.i) + return *(c->ptr.i); + + return 0; +} + +int +get_var_value_int(const gchar *name) { + uzbl_cmdprop *c = get_var_c(name); + return get_var_value_int_c(c); +} + +float +get_var_value_float_c(const uzbl_cmdprop *c) { + if(!c) return 0; + + if(c->getter) { + return ((float (*)())c->getter)(); + } else if(c->ptr.f) + return *(c->ptr.f); + + return 0; +} + +float +get_var_value_float(const gchar *name) { + uzbl_cmdprop *c = get_var_c(name); + return get_var_value_float_c(c); +} + void dump_var_hash(gpointer k, gpointer v, gpointer ud) { (void) ud; @@ -121,12 +210,14 @@ dump_var_hash(gpointer k, gpointer v, gpointer ud) { if(!c->dump) return; - if(c->type == TYPE_STR) - printf("set %s = %s\n", (char *)k, *c->ptr.s ? *c->ptr.s : " "); - else if(c->type == TYPE_INT) - printf("set %s = %d\n", (char *)k, *c->ptr.i); + if(c->type == TYPE_STR) { + gchar *v = get_var_value_string_c(c); + printf("set %s = %s\n", (char *)k, v); + g_free(v); + } else if(c->type == TYPE_INT) + printf("set %s = %d\n", (char *)k, get_var_value_int_c(c)); else if(c->type == TYPE_FLOAT) - printf("set %s = %f\n", (char *)k, *c->ptr.f); + printf("set %s = %f\n", (char *)k, get_var_value_float_c(c)); } void @@ -161,36 +252,79 @@ view_settings() { } void -uri_change_cb (WebKitWebView *web_view, GParamSpec param_spec) { - (void) param_spec; - - g_free (uzbl.state.uri); - g_object_get (web_view, "uri", &uzbl.state.uri, NULL); - g_setenv("UZBL_URI", uzbl.state.uri, TRUE); - +set_window_property(const gchar* prop, const gchar* value) { if(GTK_IS_WIDGET(uzbl.gui.main_window)) { gdk_property_change( gtk_widget_get_window (GTK_WIDGET (uzbl.gui.main_window)), - gdk_atom_intern_static_string("UZBL_URI"), + gdk_atom_intern_static_string(prop), gdk_atom_intern_static_string("STRING"), 8, GDK_PROP_MODE_REPLACE, - (unsigned char *)uzbl.state.uri, - strlen(uzbl.state.uri)); + (const guchar*)value, + strlen(value)); } } void -cmd_load_uri() { - const gchar *uri = uzbl.state.uri; +uri_change_cb (WebKitWebView *web_view, GParamSpec param_spec) { + (void) param_spec; - gchar *newuri; - SoupURI *soup_uri; + g_free (uzbl.state.uri); + g_object_get (web_view, "uri", &uzbl.state.uri, NULL); - /* Strip leading whitespaces */ + g_setenv("UZBL_URI", uzbl.state.uri, TRUE); + set_window_property("UZBL_URI", uzbl.state.uri); +} + +gchar * +make_uri_from_user_input(const gchar *uri) { + gchar *result = NULL; + + SoupURI *soup_uri = soup_uri_new(uri); + if (soup_uri) { + /* this looks like a valid URI. */ + if(soup_uri->host == NULL && string_is_integer(soup_uri->path)) + /* the user probably typed in a host:port without a scheme. */ + result = g_strconcat("http://", uri, NULL); + else + result = g_strdup(uri); + + soup_uri_free(soup_uri); + + return result; + } + + /* it's not a valid URI, maybe it's a path on the filesystem? + * check to see if such a path exists. */ + if (file_exists(uri)) { + if (g_path_is_absolute (uri)) + return g_strconcat("file://", uri, NULL); + + /* make it into an absolute path */ + gchar *wd = g_get_current_dir (); + result = g_strconcat("file://", wd, "/", uri, NULL); + g_free(wd); + + return result; + } + + /* not a path on the filesystem, just assume it's an HTTP URL. */ + return g_strconcat("http://", uri, NULL); +} + +void +set_uri(const gchar *uri) { + /* Strip leading whitespace */ while (*uri && isspace(*uri)) uri++; + /* don't do anything when given a blank URL */ + if(uri[0] == 0) + return; + + g_free(uzbl.state.uri); + uzbl.state.uri = g_strdup(uri); + /* evaluate javascript: URIs */ if (!strncmp (uri, "javascript:", 11)) { eval_js(uzbl.gui.web_view, uri, NULL, "javascript:"); @@ -198,63 +332,34 @@ cmd_load_uri() { } /* attempt to parse the URI */ - soup_uri = soup_uri_new(uri); - - if (!soup_uri) { - /* it's not a valid URI, maybe it's a path on the filesystem. */ - const gchar *fullpath; - if (g_path_is_absolute (uri)) - fullpath = uri; - else { - gchar *wd = g_get_current_dir (); - fullpath = g_build_filename (wd, uri, NULL); - g_free(wd); - } - - struct stat stat_result; - if (! g_stat(fullpath, &stat_result)) - newuri = g_strconcat("file://", fullpath, NULL); - else - newuri = g_strconcat("http://", uri, NULL); - } else { - if(soup_uri->host == NULL && string_is_integer(soup_uri->path)) - /* the user probably typed in a host:port without a scheme */ - newuri = g_strconcat("http://", uri, NULL); - else - newuri = g_strdup(uri); - - soup_uri_free(soup_uri); - } - - if(GTK_IS_WIDGET(uzbl.gui.main_window)) { - gdk_property_change( - gtk_widget_get_window (GTK_WIDGET (uzbl.gui.main_window)), - gdk_atom_intern_static_string("UZBL_URI"), - gdk_atom_intern_static_string("STRING"), - 8, - GDK_PROP_MODE_REPLACE, - (unsigned char *)newuri, - strlen(newuri)); - } + gchar *newuri = make_uri_from_user_input(uri); + set_window_property("UZBL_URI", newuri); webkit_web_view_load_uri (uzbl.gui.web_view, newuri); + g_free (newuri); } void -cmd_max_conns() { +set_max_conns(int max_conns) { + uzbl.net.max_conns = max_conns; + g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_MAX_CONNS, uzbl.net.max_conns, NULL); } void -cmd_max_conns_host() { +set_max_conns_host(int max_conns_host) { + uzbl.net.max_conns_host = max_conns_host; + g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_MAX_CONNS_PER_HOST, uzbl.net.max_conns_host, NULL); } void -cmd_http_debug() { +set_http_debug(int debug) { + uzbl.behave.http_debug = debug; + if(uzbl.net.soup_logger) { soup_session_remove_feature (uzbl.net.soup_session, SOUP_SESSION_FEATURE(uzbl.net.soup_logger)); @@ -267,64 +372,80 @@ cmd_http_debug() { } void -cmd_font_size() { - GObject *ws = view_settings(); - if (uzbl.behave.font_size > 0) { - g_object_set (ws, "default-font-size", uzbl.behave.font_size, NULL); - } +set_ca_file(gchar *path) { + g_object_set (uzbl.net.soup_session, "ssl-ca-file", path, NULL); +} - if (uzbl.behave.monospace_size > 0) { - g_object_set (ws, "default-monospace-font-size", - uzbl.behave.monospace_size, NULL); - } else { - g_object_set (ws, "default-monospace-font-size", - uzbl.behave.font_size, NULL); - } +gchar * +get_ca_file() { + gchar *path; + g_object_get (uzbl.net.soup_session, "ssl-ca-file", &path, NULL); + return path; } void -cmd_zoom_level() { - webkit_web_view_set_zoom_level (uzbl.gui.web_view, uzbl.behave.zoom_level); +set_verify_cert(int strict) { + g_object_set (uzbl.net.soup_session, "ssl-strict", strict, NULL); +} + +int +get_verify_cert() { + int strict; + g_object_get (uzbl.net.soup_session, "ssl-strict", &strict, NULL); + return strict; } -#define EXPOSE_WEBKIT_VIEW_SETTINGS(SYM, STORAGE, PROPERTY) void cmd_##SYM() { \ - g_object_set(view_settings(), (PROPERTY), (STORAGE), NULL); \ +#define EXPOSE_WEBKIT_VIEW_SETTINGS(SYM, PROPERTY, TYPE) \ +void set_##SYM(TYPE val) { \ + g_object_set(view_settings(), (PROPERTY), val, NULL); \ +} \ +TYPE get_##SYM() { \ + TYPE val; \ + g_object_get(view_settings(), (PROPERTY), &val, NULL); \ + return val; \ } -EXPOSE_WEBKIT_VIEW_SETTINGS(default_font_family, uzbl.behave.default_font_family, "default-font-family") -EXPOSE_WEBKIT_VIEW_SETTINGS(monospace_font_family, uzbl.behave.monospace_font_family, "monospace-font-family") -EXPOSE_WEBKIT_VIEW_SETTINGS(sans_serif_font_family, uzbl.behave.sans_serif_font_family, "sans_serif-font-family") -EXPOSE_WEBKIT_VIEW_SETTINGS(serif_font_family, uzbl.behave.serif_font_family, "serif-font-family") -EXPOSE_WEBKIT_VIEW_SETTINGS(cursive_font_family, uzbl.behave.cursive_font_family, "cursive-font-family") -EXPOSE_WEBKIT_VIEW_SETTINGS(fantasy_font_family, uzbl.behave.fantasy_font_family, "fantasy-font-family") +EXPOSE_WEBKIT_VIEW_SETTINGS(default_font_family, "default-font-family", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(monospace_font_family, "monospace-font-family", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(sans_serif_font_family, "sans_serif-font-family", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(serif_font_family, "serif-font-family", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(cursive_font_family, "cursive-font-family", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(fantasy_font_family, "fantasy-font-family", gchar *) -EXPOSE_WEBKIT_VIEW_SETTINGS(minimum_font_size, uzbl.behave.minimum_font_size, "minimum_font_size") +EXPOSE_WEBKIT_VIEW_SETTINGS(minimum_font_size, "minimum-font-size", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(font_size, "default-font-size", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(monospace_size, "default-monospace-font-size", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(disable_plugins, !uzbl.behave.disable_plugins, "enable-plugins") -EXPOSE_WEBKIT_VIEW_SETTINGS(disable_scripts, !uzbl.behave.disable_scripts, "enable-scripts") +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_plugins, "enable-plugins", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_scripts, "enable-scripts", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(javascript_windows, uzbl.behave.javascript_windows, "javascript-can-open-windows-automatically") +EXPOSE_WEBKIT_VIEW_SETTINGS(javascript_windows, "javascript-can-open-windows-automatically", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(autoload_img, uzbl.behave.autoload_img, "auto-load-images") -EXPOSE_WEBKIT_VIEW_SETTINGS(autoshrink_img, uzbl.behave.autoshrink_img, "auto-shrink-images") +EXPOSE_WEBKIT_VIEW_SETTINGS(autoload_images, "auto-load-images", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(autoshrink_images, "auto-shrink-images", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(enable_pagecache, uzbl.behave.enable_pagecache, "enable-page-cache") -EXPOSE_WEBKIT_VIEW_SETTINGS(enable_private, uzbl.behave.enable_private, "enable-private-browsing") +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_pagecache, "enable-page-cache", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_private, "enable-private-browsing", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(enable_spellcheck, uzbl.behave.enable_spellcheck, "enable-spell-checking") -EXPOSE_WEBKIT_VIEW_SETTINGS(spellcheck_languages, uzbl.behave.spellcheck_languages, "spell-checking-languages") -EXPOSE_WEBKIT_VIEW_SETTINGS(resizable_txt, uzbl.behave.resizable_txt, "resizable-text-areas") +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_spellcheck, "enable-spell-checking", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(spellcheck_languages, "spell-checking-languages", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(resizable_text_areas, "resizable-text-areas", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(style_uri, uzbl.behave.style_uri, "user-stylesheet-uri") -EXPOSE_WEBKIT_VIEW_SETTINGS(print_bg, uzbl.behave.print_bg, "print-backgrounds") -EXPOSE_WEBKIT_VIEW_SETTINGS(enforce_96dpi, uzbl.behave.enforce_96dpi, "enforce-96-dpi") +EXPOSE_WEBKIT_VIEW_SETTINGS(stylesheet_uri, "user-stylesheet-uri", gchar *) +EXPOSE_WEBKIT_VIEW_SETTINGS(print_bg, "print-backgrounds", int) +EXPOSE_WEBKIT_VIEW_SETTINGS(enforce_96_dpi, "enforce-96-dpi", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(caret_browsing, uzbl.behave.caret_browsing, "enable-caret-browsing") +EXPOSE_WEBKIT_VIEW_SETTINGS(caret_browsing, "enable-caret-browsing", int) -EXPOSE_WEBKIT_VIEW_SETTINGS(default_encoding, uzbl.behave.default_encoding, "default-encoding") +EXPOSE_WEBKIT_VIEW_SETTINGS(enable_cross_file_access, "enable-file-access-from-file-uris", int) + +EXPOSE_WEBKIT_VIEW_SETTINGS(default_encoding, "default-encoding", gchar *) void -set_proxy_url() { +set_proxy_url(const gchar *proxy_url) { + g_free(uzbl.net.proxy_url); + uzbl.net.proxy_url = g_strdup(proxy_url); + const gchar *url = uzbl.net.proxy_url; SoupSession *session = uzbl.net.soup_session; SoupURI *soup_uri = NULL; @@ -338,14 +459,16 @@ set_proxy_url() { soup_uri_free(soup_uri); } - void -set_authentication_handler() { +set_authentication_handler(const gchar *handler) { /* Check if WEBKIT_TYPE_SOUP_AUTH_DIALOG feature is set */ GSList *flist = soup_session_get_features (uzbl.net.soup_session, (GType) WEBKIT_TYPE_SOUP_AUTH_DIALOG); guint feature_is_set = g_slist_length(flist); g_slist_free(flist); + g_free(uzbl.behave.authentication_handler); + uzbl.behave.authentication_handler = g_strdup(handler); + if (uzbl.behave.authentication_handler == NULL || *uzbl.behave.authentication_handler == 0) { if (!feature_is_set) soup_session_add_feature_by_type @@ -355,16 +478,18 @@ set_authentication_handler() { soup_session_remove_feature_by_type (uzbl.net.soup_session, (GType) WEBKIT_TYPE_SOUP_AUTH_DIALOG); } - return; } void -set_status_background() { +set_status_background(const gchar *background) { /* 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) */ GtkWidget* widget = uzbl.gui.main_window ? uzbl.gui.main_window : GTK_WIDGET (uzbl.gui.plug); + g_free(uzbl.behave.status_background); + uzbl.behave.status_background = g_strdup(background); + #if GTK_CHECK_VERSION(2,91,0) GdkRGBA color; gdk_rgba_parse (&color, uzbl.behave.status_background); @@ -377,61 +502,96 @@ set_status_background() { } void -set_icon() { - if(file_exists(uzbl.gui.icon)) { - if (uzbl.gui.main_window) - gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL); +set_icon(const gchar *icon) { + if(file_exists(icon) && uzbl.gui.main_window) { + g_free(uzbl.gui.icon); + uzbl.gui.icon = g_strdup(icon); + + gtk_window_set_icon_from_file (GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.icon, NULL); } else { - g_printerr ("Icon \"%s\" not found. ignoring.\n", uzbl.gui.icon); + g_printerr ("Icon \"%s\" not found. ignoring.\n", icon); } } void -set_window_role() { - if (uzbl.gui.main_window) - gtk_window_set_role(GTK_WINDOW (uzbl.gui.main_window), uzbl.gui.window_role); +set_window_role(const gchar *role) { + if (!uzbl.gui.main_window) + return; + + gtk_window_set_role(GTK_WINDOW (uzbl.gui.main_window), role); } -void -set_geometry() { - int ret=0, x=0, y=0; - unsigned int w=0, h=0; - if(uzbl.gui.geometry) { - if(uzbl.gui.geometry[0] == 'm') { /* m/maximize/maximized */ - gtk_window_maximize((GtkWindow *)(uzbl.gui.main_window)); - } else { - /* we used to use gtk_window_parse_geometry() but that didn't work how it was supposed to */ - ret = XParseGeometry(uzbl.gui.geometry, &x, &y, &w, &h); - if(ret & XValue) - gtk_window_move((GtkWindow *)uzbl.gui.main_window, x, y); - if(ret & WidthValue) - gtk_window_resize((GtkWindow *)uzbl.gui.main_window, w, h); - } +gchar * +get_window_role() { + if (!uzbl.gui.main_window) + return NULL; + + const gchar* role = gtk_window_get_role(GTK_WINDOW (uzbl.gui.main_window)); + return g_strdup(role); +} + +gchar * +get_geometry() { + int w, h, x, y; + GString *buf = g_string_new(""); + + if(uzbl.gui.main_window) { + gtk_window_get_size(GTK_WINDOW(uzbl.gui.main_window), &w, &h); + gtk_window_get_position(GTK_WINDOW(uzbl.gui.main_window), &x, &y); + + g_string_printf(buf, "%dx%d+%d+%d", w, h, x, y); } - /* update geometry var with the actual geometry - this is necessary as some WMs don't seem to honour - the above setting and we don't want to end up with - wrong geometry information - */ - retrieve_geometry(); + return g_string_free(buf, FALSE); } void -set_show_status() { - if (!uzbl.behave.show_status) - gtk_widget_hide(uzbl.gui.status_bar); - else - gtk_widget_show(uzbl.gui.status_bar); +set_geometry(const gchar *geometry) { + if(!geometry) + return; + + if(geometry[0] == 'm') { /* m/maximize/maximized */ + gtk_window_maximize((GtkWindow *)(uzbl.gui.main_window)); + } else { + int x=0, y=0; + unsigned int w=0, h=0; + /* we used to use gtk_window_parse_geometry() but that didn't work + * how it was supposed to. */ + int ret = XParseGeometry(uzbl.gui.geometry, &x, &y, &w, &h); + + if(ret & XValue) + gtk_window_move((GtkWindow *)uzbl.gui.main_window, x, y); + + if(ret & WidthValue) + gtk_window_resize((GtkWindow *)uzbl.gui.main_window, w, h); + } + + /* get the actual geometry (which might be different from what was + * specified) and store it (since the GEOMETRY_CHANGED event needs to + * know what it changed from) */ + g_free(uzbl.gui.geometry); + uzbl.gui.geometry = get_geometry(); +} + +void +set_show_status(int show_status) { + gtk_widget_set_visible(uzbl.gui.status_bar, show_status); update_title(); } +int +get_show_status() { + return gtk_widget_get_visible(uzbl.gui.status_bar); +} + void -set_status_top() { +set_status_top(int status_top) { if (!uzbl.gui.scrolled_win && !uzbl.gui.status_bar) return; + uzbl.behave.status_top = status_top; + g_object_ref(uzbl.gui.scrolled_win); g_object_ref(uzbl.gui.status_bar); gtk_container_remove(GTK_CONTAINER(uzbl.gui.vbox), uzbl.gui.scrolled_win); @@ -453,49 +613,67 @@ set_status_top() { } void -set_current_encoding() { - gchar *encoding = uzbl.behave.current_encoding; +set_current_encoding(const gchar *encoding) { if(strlen(encoding) == 0) encoding = NULL; webkit_web_view_set_custom_encoding(uzbl.gui.web_view, encoding); } +gchar * +get_current_encoding() { + const gchar *encoding = webkit_web_view_get_custom_encoding(uzbl.gui.web_view); + return g_strdup(encoding); +} + void -cmd_fifo_dir() { - uzbl.behave.fifo_dir = init_fifo(uzbl.behave.fifo_dir); +set_fifo_dir(const gchar *fifo_dir) { + g_free(uzbl.behave.fifo_dir); + + if(init_fifo(fifo_dir)) + uzbl.behave.fifo_dir = g_strdup(fifo_dir); + else + uzbl.behave.fifo_dir = NULL; } void -cmd_socket_dir() { - uzbl.behave.socket_dir = init_socket(uzbl.behave.socket_dir); +set_socket_dir(const gchar *socket_dir) { + g_free(uzbl.behave.socket_dir); + + if(init_socket(socket_dir)) + uzbl.behave.socket_dir = g_strdup(socket_dir); + else + uzbl.behave.socket_dir = NULL; } void -cmd_inject_html() { - if(uzbl.behave.inject_html) { - webkit_web_view_load_html_string (uzbl.gui.web_view, - uzbl.behave.inject_html, NULL); - } +set_inject_html(const gchar *html) { + webkit_web_view_load_html_string (uzbl.gui.web_view, html, NULL); } void -cmd_useragent() { - if (*uzbl.net.useragent == ' ') { - g_free (uzbl.net.useragent); +set_useragent(const gchar *useragent) { + g_free(uzbl.net.useragent); + + if (*useragent == ' ') { uzbl.net.useragent = NULL; } else { + uzbl.net.useragent = g_strdup(useragent); + g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_USER_AGENT, uzbl.net.useragent, NULL); } } void -set_accept_languages() { - if (*uzbl.net.accept_languages == ' ') { - g_free (uzbl.net.accept_languages); +set_accept_languages(const gchar *accept_languages) { + g_free(uzbl.net.accept_languages); + + if (*accept_languages == ' ') { uzbl.net.accept_languages = NULL; } else { + uzbl.net.accept_languages = g_strdup(accept_languages); + g_object_set(G_OBJECT(uzbl.net.soup_session), SOUP_SESSION_ACCEPT_LANGUAGE, uzbl.net.accept_languages, NULL); } @@ -503,26 +681,48 @@ set_accept_languages() { /* requires webkit >=1.1.14 */ void -cmd_view_source() { +set_view_source(int view_source) { + uzbl.behave.view_source = view_source; + webkit_web_view_set_view_source_mode(uzbl.gui.web_view, (gboolean) uzbl.behave.view_source); } void -cmd_set_zoom_type () { - if(uzbl.behave.zoom_type) - webkit_web_view_set_full_content_zoom (uzbl.gui.web_view, TRUE); - else - webkit_web_view_set_full_content_zoom (uzbl.gui.web_view, FALSE); +set_zoom_type (int type) { + webkit_web_view_set_full_content_zoom (uzbl.gui.web_view, type); +} + +int +get_zoom_type () { + return webkit_web_view_get_full_content_zoom (uzbl.gui.web_view); +} + +void +set_zoom_level(float zoom_level) { + webkit_web_view_set_zoom_level (uzbl.gui.web_view, zoom_level); +} + +float +get_zoom_level() { + return webkit_web_view_get_zoom_level (uzbl.gui.web_view); } /* abbreviations to help keep the table's width humane */ -#define PTR_V_STR(var, d, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = d, .writeable = 1, .func = fun } -#define PTR_V_INT(var, d, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = d, .writeable = 1, .func = fun } -#define PTR_V_FLOAT(var, d, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = d, .writeable = 1, .func = fun } -#define PTR_C_STR(var, fun) { .ptr.s = &(var), .type = TYPE_STR, .dump = 0, .writeable = 0, .func = fun } -#define PTR_C_INT(var, fun) { .ptr.i = (int*)&(var), .type = TYPE_INT, .dump = 0, .writeable = 0, .func = fun } -#define PTR_C_FLOAT(var, fun) { .ptr.f = &(var), .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .func = fun } + +/* variables */ +#define PTR_V_STR(var, d, set) { .ptr = { .s = &(var) }, .type = TYPE_STR, .dump = d, .writeable = 1, .getter = NULL, .setter = (uzbl_fp)set } +#define PTR_V_INT(var, d, set) { .ptr = { .i = (int*)&(var) }, .type = TYPE_INT, .dump = d, .writeable = 1, .getter = NULL, .setter = (uzbl_fp)set } +#define PTR_V_FLOAT(var, d, set) { .ptr = { .f = &(var) }, .type = TYPE_FLOAT, .dump = d, .writeable = 1, .getter = NULL, .setter = (uzbl_fp)set } + +#define PTR_V_STR_GETSET(var) { .type = TYPE_STR, .dump = 1, .writeable = 1, .getter = (uzbl_fp) get_##var, .setter = (uzbl_fp)set_##var } +#define PTR_V_INT_GETSET(var) { .type = TYPE_INT, .dump = 1, .writeable = 1, .getter = (uzbl_fp) get_##var, .setter = (uzbl_fp)set_##var } +#define PTR_V_FLOAT_GETSET(var) { .type = TYPE_FLOAT, .dump = 1, .writeable = 1, .getter = (uzbl_fp) get_##var, .setter = (uzbl_fp)set_##var } + +/* constants */ +#define PTR_C_STR(var) { .ptr = { .s = &(var) }, .type = TYPE_STR, .dump = 0, .writeable = 0, .getter = NULL, .setter = NULL } +#define PTR_C_INT(var) { .ptr = { .i = (int*)&(var) }, .type = TYPE_INT, .dump = 0, .writeable = 0, .getter = NULL, .setter = NULL } +#define PTR_C_FLOAT(var) { .ptr = { .f = &(var) }, .type = TYPE_FLOAT, .dump = 0, .writeable = 0, .getter = NULL, .setter = NULL } const struct var_name_to_ptr_t { const char *name; @@ -530,78 +730,97 @@ const struct var_name_to_ptr_t { } var_name_to_ptr[] = { /* variable name pointer to variable in code dump callback function */ /* ---------------------------------------------------------------------------------------------- */ - { "uri", PTR_V_STR(uzbl.state.uri, 1, cmd_load_uri)}, + { "uri", PTR_V_STR(uzbl.state.uri, 1, set_uri)}, + { "verbose", PTR_V_INT(uzbl.state.verbose, 1, NULL)}, { "print_events", PTR_V_INT(uzbl.state.events_stdout, 1, NULL)}, - { "inject_html", PTR_V_STR(uzbl.behave.inject_html, 0, cmd_inject_html)}, - { "geometry", PTR_V_STR(uzbl.gui.geometry, 1, set_geometry)}, - { "show_status", PTR_V_INT(uzbl.behave.show_status, 1, set_show_status)}, + + { "show_status", PTR_V_INT_GETSET(show_status)}, { "status_top", PTR_V_INT(uzbl.behave.status_top, 1, set_status_top)}, { "status_format", PTR_V_STR(uzbl.behave.status_format, 1, NULL)}, { "status_format_right", PTR_V_STR(uzbl.behave.status_format_right, 1, NULL)}, { "status_background", PTR_V_STR(uzbl.behave.status_background, 1, set_status_background)}, { "title_format_long", PTR_V_STR(uzbl.behave.title_format_long, 1, NULL)}, { "title_format_short", PTR_V_STR(uzbl.behave.title_format_short, 1, NULL)}, + + { "geometry", PTR_V_STR_GETSET(geometry)}, { "icon", PTR_V_STR(uzbl.gui.icon, 1, set_icon)}, - { "window_role", PTR_V_STR(uzbl.gui.window_role, 1, set_window_role)}, + { "window_role", PTR_V_STR_GETSET(window_role)}, + { "forward_keys", PTR_V_INT(uzbl.behave.forward_keys, 1, NULL)}, + { "authentication_handler", PTR_V_STR(uzbl.behave.authentication_handler, 1, set_authentication_handler)}, { "scheme_handler", PTR_V_STR(uzbl.behave.scheme_handler, 1, NULL)}, + { "request_handler", PTR_V_STR(uzbl.behave.request_handler, 1, NULL)}, { "download_handler", PTR_V_STR(uzbl.behave.download_handler, 1, NULL)}, - { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, cmd_fifo_dir)}, - { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, cmd_socket_dir)}, - { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, cmd_http_debug)}, + + { "fifo_dir", PTR_V_STR(uzbl.behave.fifo_dir, 1, set_fifo_dir)}, + { "socket_dir", PTR_V_STR(uzbl.behave.socket_dir, 1, set_socket_dir)}, + { "shell_cmd", PTR_V_STR(uzbl.behave.shell_cmd, 1, NULL)}, + + { "http_debug", PTR_V_INT(uzbl.behave.http_debug, 1, set_http_debug)}, { "proxy_url", PTR_V_STR(uzbl.net.proxy_url, 1, set_proxy_url)}, - { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, cmd_max_conns)}, - { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, cmd_max_conns_host)}, - { "useragent", PTR_V_STR(uzbl.net.useragent, 1, cmd_useragent)}, + { "max_conns", PTR_V_INT(uzbl.net.max_conns, 1, set_max_conns)}, + { "max_conns_host", PTR_V_INT(uzbl.net.max_conns_host, 1, set_max_conns_host)}, + { "useragent", PTR_V_STR(uzbl.net.useragent, 1, set_useragent)}, { "accept_languages", PTR_V_STR(uzbl.net.accept_languages, 1, set_accept_languages)}, - { "javascript_windows", PTR_V_INT(uzbl.behave.javascript_windows, 1, cmd_javascript_windows)}, - /* requires webkit >=1.1.14 */ - { "view_source", PTR_V_INT(uzbl.behave.view_source, 0, cmd_view_source)}, + + { "view_source", PTR_V_INT(uzbl.behave.view_source, 0, set_view_source)}, + + { "ssl_ca_file", PTR_V_STR_GETSET(ca_file)}, + { "ssl_verify", PTR_V_INT_GETSET(verify_cert)}, /* exported WebKitWebSettings properties */ - { "zoom_level", PTR_V_FLOAT(uzbl.behave.zoom_level, 1, cmd_zoom_level)}, - { "zoom_type", PTR_V_INT(uzbl.behave.zoom_type, 1, cmd_set_zoom_type)}, - { "font_size", PTR_V_INT(uzbl.behave.font_size, 1, cmd_font_size)}, - { "default_font_family", PTR_V_STR(uzbl.behave.default_font_family, 1, cmd_default_font_family)}, - { "monospace_font_family", PTR_V_STR(uzbl.behave.monospace_font_family, 1, cmd_monospace_font_family)}, - { "cursive_font_family", PTR_V_STR(uzbl.behave.cursive_font_family, 1, cmd_cursive_font_family)}, - { "sans_serif_font_family", PTR_V_STR(uzbl.behave.sans_serif_font_family, 1, cmd_sans_serif_font_family)}, - { "serif_font_family", PTR_V_STR(uzbl.behave.serif_font_family, 1, cmd_serif_font_family)}, - { "fantasy_font_family", PTR_V_STR(uzbl.behave.fantasy_font_family, 1, cmd_fantasy_font_family)}, - { "monospace_size", PTR_V_INT(uzbl.behave.monospace_size, 1, cmd_font_size)}, - { "minimum_font_size", PTR_V_INT(uzbl.behave.minimum_font_size, 1, cmd_minimum_font_size)}, - { "enable_pagecache", PTR_V_INT(uzbl.behave.enable_pagecache, 1, cmd_enable_pagecache)}, - { "disable_plugins", PTR_V_INT(uzbl.behave.disable_plugins, 1, cmd_disable_plugins)}, - { "disable_scripts", PTR_V_INT(uzbl.behave.disable_scripts, 1, cmd_disable_scripts)}, - { "autoload_images", PTR_V_INT(uzbl.behave.autoload_img, 1, cmd_autoload_img)}, - { "autoshrink_images", PTR_V_INT(uzbl.behave.autoshrink_img, 1, cmd_autoshrink_img)}, - { "enable_spellcheck", PTR_V_INT(uzbl.behave.enable_spellcheck, 1, cmd_enable_spellcheck)}, - { "spellcheck_languages", PTR_V_STR(uzbl.behave.spellcheck_languages, 1, cmd_spellcheck_languages)}, - { "enable_private", PTR_V_INT(uzbl.behave.enable_private, 1, cmd_enable_private)}, - { "print_backgrounds", PTR_V_INT(uzbl.behave.print_bg, 1, cmd_print_bg)}, - { "stylesheet_uri", PTR_V_STR(uzbl.behave.style_uri, 1, cmd_style_uri)}, - { "resizable_text_areas", PTR_V_INT(uzbl.behave.resizable_txt, 1, cmd_resizable_txt)}, - { "default_encoding", PTR_V_STR(uzbl.behave.default_encoding, 1, cmd_default_encoding)}, - { "current_encoding", PTR_V_STR(uzbl.behave.current_encoding, 1, set_current_encoding)}, - { "enforce_96_dpi", PTR_V_INT(uzbl.behave.enforce_96dpi, 1, cmd_enforce_96dpi)}, - { "caret_browsing", PTR_V_INT(uzbl.behave.caret_browsing, 1, cmd_caret_browsing)}, + { "javascript_windows", PTR_V_INT_GETSET(javascript_windows)}, + { "zoom_level", PTR_V_FLOAT_GETSET(zoom_level)}, + { "zoom_type", PTR_V_INT_GETSET(zoom_type)}, + + { "default_font_family", PTR_V_STR_GETSET(default_font_family)}, + { "monospace_font_family", PTR_V_STR_GETSET(monospace_font_family)}, + { "cursive_font_family", PTR_V_STR_GETSET(cursive_font_family)}, + { "sans_serif_font_family", PTR_V_STR_GETSET(sans_serif_font_family)}, + { "serif_font_family", PTR_V_STR_GETSET(serif_font_family)}, + { "fantasy_font_family", PTR_V_STR_GETSET(fantasy_font_family)}, + + { "monospace_size", PTR_V_INT_GETSET(monospace_size)}, + { "font_size", PTR_V_INT_GETSET(font_size)}, + { "minimum_font_size", PTR_V_INT_GETSET(minimum_font_size)}, + + { "enable_pagecache", PTR_V_INT_GETSET(enable_pagecache)}, + { "enable_plugins", PTR_V_INT_GETSET(enable_plugins)}, + { "enable_scripts", PTR_V_INT_GETSET(enable_scripts)}, + { "autoload_images", PTR_V_INT_GETSET(autoload_images)}, + { "autoshrink_images", PTR_V_INT_GETSET(autoshrink_images)}, + { "enable_spellcheck", PTR_V_INT_GETSET(enable_spellcheck)}, + { "spellcheck_languages", PTR_V_STR_GETSET(spellcheck_languages)}, + { "enable_private", PTR_V_INT_GETSET(enable_private)}, + { "print_backgrounds", PTR_V_INT_GETSET(print_bg)}, + { "stylesheet_uri", PTR_V_STR_GETSET(stylesheet_uri)}, + { "resizable_text_areas", PTR_V_INT_GETSET(resizable_text_areas)}, + { "default_encoding", PTR_V_STR_GETSET(default_encoding)}, + { "current_encoding", PTR_V_STR_GETSET(current_encoding)}, + { "enforce_96_dpi", PTR_V_INT_GETSET(enforce_96_dpi)}, + { "caret_browsing", PTR_V_INT_GETSET(caret_browsing)}, + { "enable_cross_file_access", PTR_V_INT_GETSET(enable_cross_file_access)}, + + { "inject_html", { .type = TYPE_STR, .dump = 0, .writeable = 1, .getter = NULL, .setter = (uzbl_fp) set_inject_html }}, /* constants (not dumpable or writeable) */ - { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major, NULL)}, - { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor, NULL)}, - { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro, NULL)}, - { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch, NULL)}, - { "COMMIT", PTR_C_STR(uzbl.info.commit, NULL)}, - { "TITLE", PTR_C_STR(uzbl.gui.main_title, NULL)}, - { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url, NULL)}, - { "NAME", PTR_C_STR(uzbl.state.instance_name, NULL)}, - { "PID", PTR_C_STR(uzbl.info.pid_str, NULL)}, - { "_", PTR_C_STR(uzbl.state.last_result, NULL)}, - - { NULL, {.ptr.s = NULL, .type = TYPE_INT, .dump = 0, .writeable = 0, .func = NULL}} + { "WEBKIT_MAJOR", PTR_C_INT(uzbl.info.webkit_major)}, + { "WEBKIT_MINOR", PTR_C_INT(uzbl.info.webkit_minor)}, + { "WEBKIT_MICRO", PTR_C_INT(uzbl.info.webkit_micro)}, + { "ARCH_UZBL", PTR_C_STR(uzbl.info.arch)}, + { "COMMIT", PTR_C_STR(uzbl.info.commit)}, + { "TITLE", PTR_C_STR(uzbl.gui.main_title)}, + { "SELECTED_URI", PTR_C_STR(uzbl.state.selected_url)}, + { "NAME", PTR_C_STR(uzbl.state.instance_name)}, + { "PID", PTR_C_STR(uzbl.info.pid_str)}, + { "_", PTR_C_STR(uzbl.state.last_result)}, + + /* and we terminate the whole thing with the closest thing we have to NULL. + * it's important that dump = 0. */ + { NULL, {.ptr = { .i = NULL }, .type = TYPE_INT, .dump = 0, .writeable = 0}} }; /* construct a hash from the var_name_to_ptr array for quick access */ diff --git a/src/variables.h b/src/variables.h index 6aa0ab0..dade652 100644 --- a/src/variables.h +++ b/src/variables.h @@ -8,14 +8,41 @@ #include <glib.h> #include <webkit/webkit.h> +#include "type.h" + +uzbl_cmdprop *get_var_c(const gchar *name); + gboolean set_var_value(const gchar *name, gchar *val); void expand_variable(GString *buf, const gchar *name); void variables_hash(); + +gchar *get_var_value_string_c(const uzbl_cmdprop *c); +gchar *get_var_value_string(const char *name); +int get_var_value_int_c(const uzbl_cmdprop *c); +int get_var_value_int(const char *name); +float get_var_value_float_c(const uzbl_cmdprop *c); +float get_var_value_float(const char *name); + +void set_var_value_string_c(uzbl_cmdprop *c, const gchar *val); +void set_var_value_int_c(uzbl_cmdprop *c, int f); +void set_var_value_float_c(uzbl_cmdprop *c, float f); + +void send_set_var_event(const char *name, const uzbl_cmdprop *c); + void dump_config(); void dump_config_as_events(); void uri_change_cb (WebKitWebView *web_view, GParamSpec param_spec); -void set_show_status(); -void set_geometry(); + +void set_show_status(int); + +void set_zoom_type(int); +int get_zoom_type(); + +gchar *get_geometry(); +void set_geometry(const gchar *); + +int get_show_status(); +void set_show_status(int); #endif diff --git a/tests/test-command.c b/tests/test-command.c index 0f0f3c1..2d91b64 100644 --- a/tests/test-command.c +++ b/tests/test-command.c @@ -25,6 +25,7 @@ #include <src/uzbl-core.h> #include <src/config.h> #include <src/type.h> +#include <src/variables.h> extern UzblCore uzbl; @@ -163,17 +164,14 @@ test_set_variable (struct EventFixture *ef, const void *data) { /* set a float */ /* we have to be careful about locales here */ - GString *cmd, *ev; + GString *cmd; cmd = g_string_new("set zoom_level = "); g_string_append_printf(cmd, "%f", 0.25); parse_cmd_line(g_string_free(cmd, FALSE), NULL); - ev = g_string_new("EVENT [" INSTANCE_NAME "] VARIABLE_SET zoom_level float "); - g_string_append_printf(ev, "%.2f\n", 0.25); - read_event(ef); - g_assert_cmpstr(g_string_free(ev, FALSE), ==, ef->event_buffer); + ASSERT_EVENT(ef, "VARIABLE_SET zoom_level float 0.25"); - g_assert_cmpfloat(0.25, ==, uzbl.behave.zoom_level); + g_assert_cmpfloat(0.25, ==, get_var_value_float("zoom_level")); /* set a constant int (nothing should happen) */ int old_major = uzbl.info.webkit_major; @@ -191,13 +189,13 @@ test_set_variable (struct EventFixture *ef, const void *data) { parse_cmd_line("set nonexistant_variable = Some Value", NULL); ASSERT_EVENT(ef, "VARIABLE_SET nonexistant_variable str 'Some Value'"); uzbl_cmdprop *c = g_hash_table_lookup(uzbl.behave.proto_var, "nonexistant_variable"); - g_assert_cmpstr("Some Value", ==, *c->ptr.s); + g_assert_cmpstr("Some Value", ==, *(c->ptr.s)); /* set a custom variable with expansion */ parse_cmd_line("set an_expanded_variable = Test @(echo expansion)@", NULL); ASSERT_EVENT(ef, "VARIABLE_SET an_expanded_variable str 'Test expansion'"); c = g_hash_table_lookup(uzbl.behave.proto_var, "an_expanded_variable"); - g_assert_cmpstr("Test expansion", ==, *c->ptr.s); + g_assert_cmpstr("Test expansion", ==, *(c->ptr.s)); } void @@ -251,15 +249,16 @@ test_scroll (void) { void test_toggle_status (void) { - g_assert(!uzbl.behave.show_status); + /* status bar is not shown (for whatever reason) */ + g_assert(!get_show_status()); /* status bar can be toggled on */ - parse_cmd_line("toggle_status", NULL); - g_assert(uzbl.behave.show_status); + parse_cmd_line("toggle show_status", NULL); + g_assert(get_show_status()); /* status bar can be toggled back off */ - parse_cmd_line("toggle_status", NULL); - g_assert(!uzbl.behave.show_status); + parse_cmd_line("toggle show_status", NULL); + g_assert(!get_show_status()); } void @@ -309,6 +308,57 @@ test_no_such_command (void) { /* if we didn't crash then we're ok! */ } +// TODO: test toggling emits an event +void +test_toggle_int (void) { + g_assert_cmpint(0, ==, uzbl.behave.forward_keys); + + parse_cmd_line("toggle forward_keys", NULL); + g_assert_cmpint(1, ==, uzbl.behave.forward_keys); + + parse_cmd_line("toggle forward_keys", NULL); + g_assert_cmpint(0, ==, uzbl.behave.forward_keys); + + // if cycle values are specified it should use those + parse_cmd_line("toggle forward_keys 1 2", NULL); + g_assert_cmpint(1, ==, uzbl.behave.forward_keys); + + parse_cmd_line("toggle forward_keys 1 2", NULL); + g_assert_cmpint(2, ==, uzbl.behave.forward_keys); + + // and wrap to the first value when it reaches the end. + parse_cmd_line("toggle forward_keys 1 2", NULL); + g_assert_cmpint(1, ==, uzbl.behave.forward_keys); +} + +void +test_toggle_string (void) { + parse_cmd_line("set useragent = something interesting", NULL); + g_assert_cmpstr("something interesting", ==, uzbl.net.useragent); + + // when something was set, it gets reset + parse_cmd_line("toggle useragent", NULL); + g_assert_cmpstr("", ==, uzbl.net.useragent); + + // if cycle values are specified it should use those + parse_cmd_line("set useragent = something interesting", NULL); + parse_cmd_line("toggle useragent 'x' 'y'", NULL); + g_assert_cmpstr("x", ==, uzbl.net.useragent); + + parse_cmd_line("toggle useragent 'x' 'y'", NULL); + g_assert_cmpstr("y", ==, uzbl.net.useragent); + + // and wrap to the first value when it reaches the end. + parse_cmd_line("toggle useragent 'x' 'y'", NULL); + g_assert_cmpstr("x", ==, uzbl.net.useragent); + + // user-defined variables can be toggled too. + parse_cmd_line("toggle new_variable 'x' 'y'", NULL); + gchar *value = get_var_value_string("new_variable"); + g_assert_cmpstr("x", ==, value); + g_free(value); +} + int main (int argc, char *argv[]) { /* set up tests */ @@ -330,6 +380,10 @@ main (int argc, char *argv[]) { g_test_add_func("/test-command/no-such-command", test_no_such_command); + g_test_add_func("/test-command/toggle-int", test_toggle_int); + // we should probably test toggle float, but meh. + g_test_add_func("/test-command/toggle-string", test_toggle_string); + /* set up uzbl */ initialize(argc, argv); |