diff --git a/awesome/rc.lua b/awesome/rc.lua index 673478d..4eb3550 100644 --- a/awesome/rc.lua +++ b/awesome/rc.lua @@ -108,7 +108,7 @@ vicious.register(uptimewidget, vicious.widgets.uptime, "uptime ${eth0 down_kb} up ${eth0 up_kb}', 3) +vicious.register(netwidget, vicious.widgets.net, "down ${wifi0 down_kb} up ${wifi0 up_kb}", 3) -- Date datewidget = widget({ type = "textbox" }) diff --git a/awesome/themes/dustin/theme.lua b/awesome/themes/dustin/theme.lua index 472226a..786ca78 100644 --- a/awesome/themes/dustin/theme.lua +++ b/awesome/themes/dustin/theme.lua @@ -4,13 +4,16 @@ theme = {} -theme.font = "termsyn 8" +theme.font = "terminus 8" -theme.bg_normal = "#222222" -theme.bg_focus = "#535d6c" +--theme.bg_normal = "#222222" +theme.bg_normal = "#000000" +--theme.bg_focus = "#535d6c" +theme.bg_focus = "#005566" theme.bg_urgent = "#ff0000" theme.bg_minimize = "#444444" +--theme.fg_normal = "#aaaaaa" theme.fg_normal = "#aaaaaa" theme.fg_focus = "#ffffff" theme.fg_urgent = "#ffffff" @@ -18,7 +21,8 @@ theme.fg_minimize = "#ffffff" theme.border_width = "1" theme.border_normal = "#000000" -theme.border_focus = "#535d6c" +--theme.border_focus = "#535d6c" +theme.border_focus = "#004466" theme.border_marked = "#91231c" -- There are other variable sets diff --git a/luakit/binds.lua b/luakit/binds.lua new file mode 100644 index 0000000..4fc718c --- /dev/null +++ b/luakit/binds.lua @@ -0,0 +1,588 @@ +----------------- +-- Keybindings -- +----------------- + +-- Binding aliases +local key, buf, but = lousy.bind.key, lousy.bind.buf, lousy.bind.but +local cmd, any = lousy.bind.cmd, lousy.bind.any + +-- Util aliases +local match, join = string.match, lousy.util.table.join +local strip, split = lousy.util.string.strip, lousy.util.string.split + +-- Globals or defaults that are used in binds +local scroll_step = globals.scroll_step or 20 +local zoom_step = globals.zoom_step or 0.1 + +-- Add binds to a mode +function add_binds(mode, binds, before) + assert(binds and type(binds) == "table", "invalid binds table type: " .. type(binds)) + mode = type(mode) ~= "table" and {mode} or mode + for _, m in ipairs(mode) do + local mdata = get_mode(m) + if mdata and before then + mdata.binds = join(binds, mdata.binds or {}) + elseif mdata then + mdata.binds = mdata.binds or {} + for _, b in ipairs(binds) do table.insert(mdata.binds, b) end + else + new_mode(m, { binds = binds }) + end + end +end + +-- Add commands to command mode +function add_cmds(cmds, before) + add_binds("command", cmds, before) +end + +-- Adds the default menu widget bindings to a mode +menu_binds = { + -- Navigate items + key({}, "j", function (w) w.menu:move_down() end), + key({}, "k", function (w) w.menu:move_up() end), + key({}, "Down", function (w) w.menu:move_down() end), + key({}, "Up", function (w) w.menu:move_up() end), + key({}, "Tab", function (w) w.menu:move_down() end), + key({"Shift"}, "Tab", function (w) w.menu:move_up() end), +} + +-- Add binds to special mode "all" which adds its binds to all modes. +add_binds("all", { + key({}, "Escape", "Return to `normal` mode.", + function (w) w:set_mode() end), + + key({"Control"}, "[", "Return to `normal` mode.", + function (w) w:set_mode() end), + + -- Mouse bindings + but({}, 8, "Go back.", + function (w) w:back() end), + + but({}, 9, "Go forward.", + function (w) w:forward() end), + + -- Open link in new tab or navigate to selection + but({}, 2, [[Open link under mouse cursor in new tab or navigate to the + contents of `luakit.selection.primary`.]], + function (w, m) + -- Ignore button 2 clicks in form fields + if not m.context.editable then + -- Open hovered uri in new tab + local uri = w.view.hovered_uri + if uri then + w:new_tab(uri, false) + else -- Open selection in current tab + uri = luakit.selection.primary + if uri then w:navigate(w:search_open(uri)) end + end + end + end), + + -- Open link in new tab when Ctrl-clicked. + but({"Control"}, 1, "Open link under mouse cursor in new tab.", + function (w, m) + local uri = w.view.hovered_uri + if uri then + w:new_tab(uri, false) + end + end), + + -- Zoom binds + but({"Control"}, 4, "Increase text zoom level.", + function (w, m) w:zoom_in() end), + + but({"Control"}, 5, "Reduce text zoom level.", + function (w, m) w:zoom_out() end), + + -- Horizontal mouse scroll binds + but({"Shift"}, 4, "Scroll left.", + function (w, m) w:scroll{ xrel = -scroll_step } end), + + but({"Shift"}, 5, "Scroll right.", + function (w, m) w:scroll{ xrel = scroll_step } end), +}) + +add_binds("normal", { + -- Autoparse the `[count]` before a binding and re-call the hit function + -- with the count removed and added to the opts table. + any([[Meta-binding to detect the `^[count]` syntax. The `[count]` is parsed + and stripped from the internal buffer string and the value assigned to + `state.count`. Then `lousy.bind.hit()` is re-called with the modified + buffer string & original modifier state. + + #### Example binding + + lousy.bind.key({}, "%", function (w, state) + w:scroll{ ypct = state.count } + end, { count = 0 }) + + This binding demonstrates several concepts. Firstly that you are able to + specify per-binding default values of `count`. In this case if the user + types `"%"` the document will be scrolled vertically to `0%` (the top). + + If the user types `"100%"` then the document will be scrolled to `100%` + (the bottom). All without the need to use `lousy.bind.buf` bindings + everywhere and or using a `^(%d*)` pattern prefix on every binding which + would like to make use of the `[count]` syntax.]], + function (w, m) + local count, buf + if m.buffer then + count = string.match(m.buffer, "^(%d+)") + end + if count then + buf = string.sub(m.buffer, #count + 1, (m.updated_buf and -2) or -1) + local opts = join(m, {count = tonumber(count)}) + opts.buffer = (#buf > 0 and buf) or nil + if lousy.bind.hit(w, m.binds, m.mods, m.key, opts) then + return true + end + end + return false + end), + + key({}, "i", "Enter `insert` mode.", + function (w) w:set_mode("insert") end), + + key({}, ":", "Enter `command` mode.", + function (w) w:set_mode("command") end), + + -- Scrolling + key({}, "j", "Scroll document down.", + function (w) w:scroll{ yrel = scroll_step } end), + + key({}, "k", "Scroll document up.", + function (w) w:scroll{ yrel = -scroll_step } end), + + key({}, "h", "Scroll document left.", + function (w) w:scroll{ xrel = -scroll_step } end), + + key({}, "l", "Scroll document right.", + function (w) w:scroll{ xrel = scroll_step } end), + + key({}, "Down", "Scroll document down.", + function (w) w:scroll{ yrel = scroll_step } end), + + key({}, "Up", "Scroll document up.", + function (w) w:scroll{ yrel = -scroll_step } end), + + key({}, "Left", "Scroll document left.", + function (w) w:scroll{ xrel = -scroll_step } end), + + key({}, "Right", "Scroll document right.", + function (w) w:scroll{ xrel = scroll_step } end), + + key({}, "^", "Scroll to the absolute left of the document.", + function (w) w:scroll{ x = 0 } end), + + key({}, "$", "Scroll to the absolute right of the document.", + function (w) w:scroll{ x = -1 } end), + + key({}, "0", "Scroll to the absolute left of the document.", + function (w, m) + if not m.count then w:scroll{ y = 0 } else return false end + end), + + key({"Control"}, "e", "Scroll document down.", + function (w) w:scroll{ yrel = scroll_step } end), + + key({"Control"}, "y", "Scroll document up.", + function (w) w:scroll{ yrel = -scroll_step } end), + + key({"Control"}, "d", "Scroll half page down.", + function (w) w:scroll{ ypagerel = 0.5 } end), + + key({"Control"}, "u", "Scroll half page up.", + function (w) w:scroll{ ypagerel = -0.5 } end), + + key({"Control"}, "f", "Scroll page down.", + function (w) w:scroll{ ypagerel = 1.0 } end), + + key({"Control"}, "b", "Scroll page up.", + function (w) w:scroll{ ypagerel = -1.0 } end), + + key({}, "space", "Scroll page down.", + function (w) w:scroll{ ypagerel = 1.0 } end), + + key({"Shift"}, "space", "Scroll page up.", + function (w) w:scroll{ ypagerel = -1.0 } end), + + key({}, "BackSpace", "Scroll page up.", + function (w) w:scroll{ ypagerel = -1.0 } end), + + key({}, "Page_Down", "Scroll page down.", + function (w) w:scroll{ ypagerel = 1.0 } end), + + key({}, "Page_Up", "Scroll page up.", + function (w) w:scroll{ ypagerel = -1.0 } end), + + key({}, "Home", "Go to the end of the document.", + function (w) w:scroll{ y = 0 } end), + + key({}, "End", "Go to the top of the document.", + function (w) w:scroll{ y = -1 } end), + + -- Specific scroll + buf("^gg$", "Go to the top of the document.", + function (w, b, m) w:scroll{ ypct = m.count } end, {count=0}), + + buf("^G$", "Go to the bottom of the document.", + function (w, b, m) w:scroll{ ypct = m.count } end, {count=100}), + + buf("^%%$", "Go to `[count]` percent of the document.", + function (w, b, m) w:scroll{ ypct = m.count } end), + + -- Zooming + key({}, "+", "Enlarge text zoom of the current page.", + function (w, m) w:zoom_in(zoom_step * m.count) end, {count=1}), + + key({}, "-", "Reduce text zom of the current page.", + function (w, m) w:zoom_out(zoom_step * m.count) end, {count=1}), + + key({}, "=", "Reset zoom level.", + function (w, m) w:zoom_set() end), + + buf("^z[iI]$", [[Enlarge text zoom of current page with `zi` or `zI` to + reduce full zoom.]], + function (w, b, m) + w:zoom_in(zoom_step * m.count, b == "zI") + end, {count=1}), + + buf("^z[oO]$", [[Reduce text zoom of current page with `zo` or `zO` to + reduce full zoom.]], + function (w, b, m) + w:zoom_out(zoom_step * m.count, b == "zO") + end, {count=1}), + + -- Zoom reset or specific zoom ([count]zZ for full content zoom) + buf("^z[zZ]$", [[Set current page zoom to `[count]` percent with + `[count]zz`, use `[count]zZ` to set full zoom percent.]], + function (w, b, m) + w:zoom_set(m.count/100, b == "zZ") + end, {count=100}), + + -- Fullscreen + key({}, "F11", "Toggle fullscreen mode.", + function (w) w.win.fullscreen = not w.win.fullscreen end), + + -- Clipboard + key({}, "p", [[Open a URL based on the current primary selection contents + in the current tab.]], + function (w) + local uri = luakit.selection.primary + if not uri then w:notify("No primary selection...") return end + w:navigate(w:search_open(uri)) + end), + + key({}, "P", [[Open a URL based on the current primary selection contents + in `[count=1]` new tab(s).]], + function (w, m) + local uri = luakit.selection.primary + if not uri then w:notify("No primary selection...") return end + for i = 1, m.count do w:new_tab(w:search_open(uri)) end + end, {count = 1}), + + -- Yanking + key({}, "y", "Yank current URI to primary selection.", + function (w) + local uri = string.gsub(w.view.uri or "", " ", "%%20") + luakit.selection.primary = uri + w:notify("Yanked uri: " .. uri) + end), + + -- Commands + key({"Control"}, "a", "Increment last number in URL.", + function (w) w:navigate(w:inc_uri(1)) end), + + key({"Control"}, "x", "Decrement last number in URL.", + function (w) w:navigate(w:inc_uri(-1)) end), + + key({}, "o", "Open one or more URLs.", + function (w) w:enter_cmd(":open ") end), + + key({}, "t", "Open one or more URLs in a new tab.", + function (w) w:enter_cmd(":tabopen ") end), + + key({}, "w", "Open one or more URLs in a new window.", + function (w) w:enter_cmd(":winopen ") end), + + key({}, "O", "Open one or more URLs based on current location.", + function (w) w:enter_cmd(":open " .. (w.view.uri or "")) end), + + key({}, "T", + "Open one or more URLs based on current location in a new tab.", + function (w) w:enter_cmd(":tabopen " .. (w.view.uri or "")) end), + + key({}, "W", + "Open one or more URLs based on current locaton in a new window.", + function (w) w:enter_cmd(":winopen " .. (w.view.uri or "")) end), + + -- History + key({}, "H", "Go back in the browser history `[count=1]` items.", + function (w, m) w:back(m.count) end), + + key({}, "L", "Go forward in the browser history `[count=1]` times.", + function (w, m) w:forward(m.count) end), + + key({}, "XF86Back", "Go back in the browser history.", + function (w, m) w:back(m.count) end), + + key({}, "XF86Forward", "Go forward in the browser history.", + function (w, m) w:forward(m.count) end), + + key({"Control"}, "o", "Go back in the browser history.", + function (w, m) w:back(m.count) end), + + key({"Control"}, "i", "Go forward in the browser history.", + function (w, m) w:forward(m.count) end), + + -- Tab + key({"Control"}, "Page_Up", "Go to previous tab.", + function (w) w:prev_tab() end), + + key({"Control"}, "Page_Down", "Go to next tab.", + function (w) w:next_tab() end), + + key({"Control"}, "Tab", "Go to next tab.", + function (w) w:next_tab() end), + + key({"Shift","Control"}, "Tab", "Go to previous tab.", + function (w) w:prev_tab() end), + + buf("^gT$", "Go to previous tab.", + function (w) w:prev_tab() end), + + buf("^gt$", "Go to next tab (or `[count]` nth tab).", + function (w, b, m) + if not w:goto_tab(m.count) then w:next_tab() end + end, {count=0}), + + buf("^g0$", "Go to first tab.", + function (w) w:goto_tab(1) end), + + buf("^g$$", "Go to last tab.", + function (w) w:goto_tab(-1) end), + + key({"Control"}, "t", "Open a new tab.", + function (w) w:new_tab(globals.homepage) end), + + key({"Control"}, "w", "Close current tab.", + function (w) w:close_tab() end), + + key({}, "d", "Close current tab (or `[count]` tabs).", + function (w, m) for i=1,m.count do w:close_tab() end end, {count=1}), + + key({}, "<", "Reorder tab left `[count=1]` positions.", + function (w, m) + w.tabs:reorder(w.view, w.tabs:current() - m.count) + end, {count=1}), + + key({}, ">", "Reorder tab right `[count=1]` positions.", + function (w, m) + w.tabs:reorder(w.view, + (w.tabs:current() + m.count) % w.tabs:count()) + end, {count=1}), + + buf("^gH$", "Open homepage in new tab.", + function (w) w:new_tab(globals.homepage) end), + + buf("^gh$", "Open homepage.", + function (w) w:navigate(globals.homepage) end), + + buf("^gy$", "Duplicate current tab.", + function (w) w:new_tab(w.view.history or "") end), + + key({}, "r", "Reload current tab.", + function (w) w:reload() end), + + key({}, "R", "Reload current tab (skipping cache).", + function (w) w:reload(true) end), + + key({"Control"}, "c", "Stop loading the current tab.", + function (w) w.view:stop() end), + + key({"Control", "Shift"}, "R", "Restart luakit (reloading configs).", + function (w) w:restart() end), + + -- Window + buf("^ZZ$", "Quit and save the session.", + function (w) w:save_session() w:close_win() end), + + buf("^ZQ$", "Quit and don't save the session.", + function (w) w:close_win() end), + + buf("^D$", "Quit and don't save the session.", + function (w) w:close_win() end), + + -- Enter passthrough mode + key({"Control"}, "z", + "Enter `passthrough` mode, ignores all luakit keybindings.", + function (w) w:set_mode("passthrough") end), +}) + +add_binds("insert", { + key({"Control"}, "z", + "Enter `passthrough` mode, ignores all luakit keybindings.", + function (w) w:set_mode("passthrough") end), +}) + +readline_bindings = { + key({"Shift"}, "Insert", + "Insert contents of primary selection at cursor position.", + function (w) w:insert_cmd(luakit.selection.primary) end), + + key({"Control"}, "w", "Delete previous word.", + function (w) w:del_word() end), + + key({"Control"}, "u", "Delete until beginning of current line.", + function (w) w:del_line() end), + + key({"Control"}, "h", "Delete character to the left.", + function (w) w:del_backward_char() end), + + key({"Control"}, "d", "Delete character to the right.", + function (w) w:del_forward_char() end), + + key({"Control"}, "a", "Move cursor to beginning of current line.", + function (w) w:beg_line() end), + + key({"Control"}, "e", "Move cursor to end of current line.", + function (w) w:end_line() end), + + key({"Control"}, "f", "Move cursor forward one character.", + function (w) w:forward_char() end), + + key({"Control"}, "b", "Move cursor backward one character.", + function (w) w:backward_char() end), + + key({"Mod1"}, "f", "Move cursor forward one word.", + function (w) w:forward_word() end), + + key({"Mod1"}, "b", "Move cursor backward one word.", + function (w) w:backward_word() end), +} + +add_binds({"command", "search"}, readline_bindings) + +-- Switching tabs with Mod1+{1,2,3,...} +mod1binds = {} +for i=1,10 do + table.insert(mod1binds, + key({"Mod1"}, tostring(i % 10), "Jump to tab at index "..i..".", + function (w) w.tabs:switch(i) end)) +end +add_binds("normal", mod1binds) + +-- Command bindings which are matched in the "command" mode from text +-- entered into the input bar. +add_cmds({ + buf("^%S+!", + [[Detect bang syntax in `:command!` and recursively calls + `lousy.bind.match_cmd(..)` removing the bang from the command string + and setting `bang = true` in the bind opts table.]], + function (w, cmd, opts) + local cmd, args = string.match(cmd, "^(%S+)!+(.*)") + if cmd then + opts = join(opts, { bang = true }) + return lousy.bind.match_cmd(w, opts.binds, cmd .. args, opts) + end + end), + + cmd("c[lose]", "Close current tab.", + function (w) w:close_tab() end), + + cmd("print", "Print current page.", + function (w) w.view:eval_js("print()") end), + + cmd("stop", "Stop loading.", + function (w) w.view:stop() end), + + cmd("reload", "Reload page", + function (w) w:reload() end), + + cmd("restart", "Restart browser (reload config files).", + function (w) w:restart() end), + + cmd("write", "Save current session.", + function (w) w:save_session() end), + + cmd("noh[lsearch]", "Clear search highlighting.", + function (w) w:clear_search() end), + + cmd("back", "Go back in the browser history `[count=1]` items.", + function (w, a) w:back(tonumber(a) or 1) end), + + cmd("f[orward]", "Go forward in the browser history `[count=1]` items.", + function (w, a) w:forward(tonumber(a) or 1) end), + + cmd("inc[rease]", "Increment last number in URL.", + function (w, a) w:navigate(w:inc_uri(tonumber(a) or 1)) end), + + cmd("o[pen]", "Open one or more URLs.", + function (w, a) w:navigate(w:search_open(a)) end), + + cmd("t[abopen]", "Open one or more URLs in a new tab.", + function (w, a) w:new_tab(w:search_open(a)) end), + + cmd("w[inopen]", "Open one or more URLs in a new window.", + function (w, a) window.new{w:search_open(a)} end), + + cmd({"javascript", "js"}, "Evaluate JavaScript snippet.", + function (w, a) w.view:eval_js(a) end), + + -- Tab manipulation commands + cmd("tab", "Execute command and open result in new tab.", + function (w, a) w:new_tab() w:run_cmd(":" .. a) end), + + cmd("tabd[o]", "Execute command in each tab.", + function (w, a) w:each_tab(function (v) w:run_cmd(":" .. a) end) end), + + cmd("tabdu[plicate]", "Duplicate current tab.", + function (w) w:new_tab(w.view.history) end), + + cmd("tabfir[st]", "Switch to first tab.", + function (w) w:goto_tab(1) end), + + cmd("tabl[ast]", "Switch to last tab.", + function (w) w:goto_tab(-1) end), + + cmd("tabn[ext]", "Switch to the next tab.", + function (w) w:next_tab() end), + + cmd("tabp[revious]", "Switch to the previous tab.", + function (w) w:prev_tab() end), + + cmd("q[uit]", "Close the current window.", + function (w, a, o) w:close_win(o.bang) end), + + cmd({"viewsource", "vs"}, "View the source code of the current document.", + function (w, a, o) w:toggle_source(not o.bang and true or nil) end), + + cmd({"wqall", "wq"}, "Save the session and quit.", + function (w, a, o) w:save_session() w:close_win(o.bang) end), + + cmd("lua", "Evaluate Lua snippet.", function (w, a) + if a then + local ret = assert( + loadstring("return function(w) return "..a.." end"))()(w) + if ret then print(ret) end + else + w:set_mode("lua") + end + end), + + cmd("dump", "Dump current tabs html to file.", + function (w, a) + local fname = string.gsub(w.win.title, '[^%w%.%-]', '_')..'.html' -- sanitize filename + local file = a or luakit.save_file("Save file", w.win, xdg.download_dir or '.', fname) + if file then + local fd = assert(io.open(file, "w"), "failed to open: " .. file) + local html = assert(w.view:eval_js("document.documentElement.outerHTML"), "Unable to get HTML") + assert(fd:write(html), "unable to save html") + io.close(fd) + w:notify("Dumped HTML to: " .. file) + end + end), +}) + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/luakit/globals.lua b/luakit/globals.lua new file mode 100644 index 0000000..b8b4a25 --- /dev/null +++ b/luakit/globals.lua @@ -0,0 +1,85 @@ +-- Global variables for luakit +globals = { + homepage = "http://luakit.org/", + -- homepage = "http://github.com/mason-larobina/luakit", + scroll_step = 40, + zoom_step = 0.1, + max_cmd_history = 100, + max_srch_history = 100, + -- http_proxy = "http://example.com:3128", + default_window_size = "800x600", + + -- Disables loading of hostnames from /etc/hosts (for large host files) + -- load_etc_hosts = false, + -- Disables checking if a filepath exists in search_open function + -- check_filepath = false, +} + +-- Make useragent +local _, arch = luakit.spawn_sync("uname -sm") +-- Only use the luakit version if in date format (reduces identifiability) +local lkv = string.match(luakit.version, "^(%d+.%d+.%d+)") +globals.useragent = string.format("Mozilla/5.0 (%s) AppleWebKit/%s+ (KHTML, like Gecko) WebKitGTK+/%s luakit%s", + string.sub(arch, 1, -2), luakit.webkit_user_agent_version, + luakit.webkit_version, (lkv and ("/" .. lkv)) or "") + +-- Search common locations for a ca file which is used for ssl connection validation. +local ca_files = { + -- $XDG_DATA_HOME/luakit/ca-certificates.crt + luakit.data_dir .. "/ca-certificates.crt", + "/etc/certs/ca-certificates.crt", + "/etc/ssl/certs/ca-certificates.crt", +} +-- Use the first ca-file found +for _, ca_file in ipairs(ca_files) do + if os.exists(ca_file) then + soup.ssl_ca_file = ca_file + break + end +end + +-- Change to stop navigation sites with invalid or expired ssl certificates +soup.ssl_strict = false + +-- Set cookie acceptance policy +cookie_policy = { always = 0, never = 1, no_third_party = 2 } +soup.accept_policy = cookie_policy.always + +-- List of search engines. Each item must contain a single %s which is +-- replaced by URI encoded search terms. All other occurances of the percent +-- character (%) may need to be escaped by placing another % before or after +-- it to avoid collisions with lua's string.format characters. +-- See: http://www.lua.org/manual/5.1/manual.html#pdf-string.format +search_engines = { + duckduckgo = "https://duckduckgo.com/?q=%s", + github = "https://github.com/search?q=%s", + google = "https://google.com/search?q=%s", + imdb = "http://www.imdb.com/find?s=all&q=%s", + wikipedia = "https://en.wikipedia.org/wiki/Special:Search?search=%s", +} + +-- Set google as fallback search engine +search_engines.default = search_engines.google +-- Use this instead to disable auto-searching +--search_engines.default = "%s" + +-- Per-domain webview properties +-- See http://webkitgtk.org/reference/webkitgtk/stable/WebKitWebSettings.html +domain_props = { --[[ + ["all"] = { + enable_scripts = false, + enable_plugins = false, + enable_private_browsing = false, + user_stylesheet_uri = "", + }, + ["youtube.com"] = { + enable_scripts = true, + enable_plugins = true, + }, + ["bbs.archlinux.org"] = { + user_stylesheet_uri = "file://" .. luakit.data_dir .. "/styles/dark.css", + enable_private_browsing = true, + }, ]] +} + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/luakit/modes.lua b/luakit/modes.lua new file mode 100644 index 0000000..bd15c67 --- /dev/null +++ b/luakit/modes.lua @@ -0,0 +1,163 @@ +------------------------------- +-- luakit mode configuration -- +------------------------------- + +-- Table of modes and their callback hooks +local modes = {} +local lousy = require "lousy" +local join = lousy.util.table.join +local order = 0 + +-- Add new mode table (optionally merges with original mode) +function new_mode(name, desc, mode, replace) + assert(string.match(name, "^[%w-_]+$"), "invalid mode name: " .. name) + -- Detect optional description + if type(desc) == "table" then + desc, mode, replace = nil, desc, mode + end + local traceback = debug.traceback("Creation traceback:", 2) + order = order + 1 + modes[name] = join({ order = order, traceback = traceback }, + (not replace and modes[name]) or {}, mode or {}, + { name = name, desc = desc }) +end + +-- Get mode table +function get_mode(name) return modes[name] end + +function get_modes() return lousy.util.table.clone(modes) end + +-- Attach window & input bar signals for mode hooks +window.init_funcs.modes_setup = function (w) + -- Calls the `enter` and `leave` mode hooks. + w:add_signal("mode-changed", function (_, name, ...) + local leave = (w.mode or {}).leave + + -- Get new modes functions/hooks/data + local mode = assert(modes[name], "invalid mode: " .. name) + + -- Call last modes leave hook. + if leave then leave(w) end + + -- Create w.mode object + w.mode = mode + + -- Update window binds + w:update_binds(name) + + -- Call new modes enter hook. + if mode.enter then mode.enter(w, ...) end + + w:emit_signal("mode-entered", mode) + end) + + local input = w.ibar.input + + -- Calls the changed hook on input widget changed. + input:add_signal("changed", function () + local changed = w.mode.changed + if changed then changed(w, input.text) end + end) + + input:add_signal("property::position", function () + local move_cursor = w.mode.move_cursor + if move_cursor then move_cursor(w, input.position) end + end) + + -- Calls the `activate` hook on input widget activate. + input:add_signal("activate", function () + local mode = w.mode + if mode and mode.activate then + local text, hist = input.text, mode.history + if mode.activate(w, text) == false then return end + -- Check if last history item is identical + if hist and hist.items and hist.items[hist.len or -1] ~= text then + table.insert(hist.items, text) + end + end + end) +end + +-- Add mode related window methods +window.methods.set_mode = lousy.mode.set +local mget = lousy.mode.get +window.methods.is_mode = function (w, name) return name == mget(w) end + +-- Setup normal mode +new_mode("normal", [[When luakit first starts you will find yourself in this + mode.]], { + enter = function (w) + w:set_prompt() + w:set_input() + end, +}) + +new_mode("all", [[Special meta-mode in which the bindings for this mode are + present in all modes.]]) + +-- Setup insert mode +new_mode("insert", [[When clicking on form fields luakit will enter the insert + mode which allows you to enter text in form fields without accidentally + triggering normal mode bindings.]], { + enter = function (w) + w:set_prompt("-- INSERT --") + w:set_input() + w.view:focus() + end, + -- Send key events to webview + passthrough = true, +}) + +new_mode("passthrough", [[Luakit will pass every key event to the WebView + until the user presses Escape.]], { + enter = function (w) + w:set_prompt("-- PASS THROUGH --") + w:set_input() + end, + -- Send key events to webview + passthrough = true, + -- Don't exit mode when clicking outside of form fields + reset_on_focus = false, + -- Don't exit mode on navigation + reset_on_navigation = false, +}) + +-- Setup command mode +new_mode("command", [[Enter commands.]], { + enter = function (w) + w:set_prompt() + w:set_input(":") + end, + changed = function (w, text) + -- Auto-exit command mode if user backspaces ":" in the input bar. + if not string.match(text, "^:") then w:set_mode() end + end, + activate = function (w, text) + w:set_mode() + local cmd = string.sub(text, 2) + if not string.find(cmd, "%S") then return end + + local success, match = xpcall( + function () return w:match_cmd(cmd) end, + function (err) w:error(debug.traceback(err, 3)) end) + + if success and not match then + w:error(string.format("Not a browser command: %q", cmd)) + end + end, + history = {maxlen = 50}, +}) + +new_mode("lua", [[Execute arbitrary Lua commands within the luakit + environment.]], { + enter = function (w) + w:set_prompt(">") + w:set_input("") + end, + activate = function (w, text) + w:set_input("") + local ret = assert(loadstring("return function(w) return "..text.." end"))()(w) + if ret then print(ret) end + end, + history = {maxlen = 50}, +}) diff --git a/luakit/rc.lua b/luakit/rc.lua new file mode 100644 index 0000000..c03a6bd --- /dev/null +++ b/luakit/rc.lua @@ -0,0 +1,181 @@ +----------------------------------------------------------------------- +-- luakit configuration file, more information at http://luakit.org/ -- +----------------------------------------------------------------------- + +require "lfs" + +if unique then + unique.new("org.luakit") + -- Check for a running luakit instance + if unique.is_running() then + if uris[1] then + for _, uri in ipairs(uris) do + if lfs.attributes(uri) then uri = os.abspath(uri) end + unique.send_message("tabopen " .. uri) + end + else + unique.send_message("winopen") + end + luakit.quit() + end +end + +-- Load library of useful functions for luakit +require "lousy" + +-- Small util functions to print output (info prints only when luakit.verbose is true) +function warn(...) io.stderr:write(string.format(...) .. "\n") end +function info(...) if luakit.verbose then io.stdout:write(string.format(...) .. "\n") end end + +-- Load users global config +-- ("$XDG_CONFIG_HOME/luakit/globals.lua" or "/etc/xdg/luakit/globals.lua") +require "globals" + +-- Load users theme +-- ("$XDG_CONFIG_HOME/luakit/theme.lua" or "/etc/xdg/luakit/theme.lua") +lousy.theme.init(lousy.util.find_config("theme.lua")) +theme = assert(lousy.theme.get(), "failed to load theme") + +-- Load users window class +-- ("$XDG_CONFIG_HOME/luakit/window.lua" or "/etc/xdg/luakit/window.lua") +require "window" + +-- Load users webview class +-- ("$XDG_CONFIG_HOME/luakit/webview.lua" or "/etc/xdg/luakit/webview.lua") +require "webview" + +-- Load users mode configuration +-- ("$XDG_CONFIG_HOME/luakit/modes.lua" or "/etc/xdg/luakit/modes.lua") +require "modes" + +-- Load users keybindings +-- ("$XDG_CONFIG_HOME/luakit/binds.lua" or "/etc/xdg/luakit/binds.lua") +require "binds" + +---------------------------------- +-- Optional user script loading -- +---------------------------------- + +require "webinspector" + +-- Add sqlite3 cookiejar +require "cookies" + +-- Cookie blocking by domain (extends cookies module) +-- Add domains to the whitelist at "$XDG_CONFIG_HOME/luakit/cookie.whitelist" +-- and blacklist at "$XDG_CONFIG_HOME/luakit/cookie.blacklist". +-- Each domain must be on it's own line and you may use "*" as a +-- wildcard character (I.e. "*google.com") +--require "cookie_blocking" + +-- Block all cookies by default (unless whitelisted) +--cookies.default_allow = false + +-- Add uzbl-like form filling +require "formfiller" + +-- Add proxy support & manager +require "proxy" + +-- Add quickmarks support & manager +require "quickmarks" + +-- Add session saving/loading support +require "session" + +-- Add command to list closed tabs & bind to open closed tabs +require "undoclose" + +-- Add command to list tab history items +require "tabhistory" + +-- Add greasemonkey-like javascript userscript support +require "userscripts" + +-- Add bookmarks support +require "bookmarks" +require "bookmarks_chrome" + +-- Add download support +require "downloads" +require "downloads_chrome" + +-- Example using xdg-open for opening downloads / showing download folders +--downloads.add_signal("open-file", function (file, mime) +-- luakit.spawn(string.format("xdg-open %q", file)) +-- return true +--end) + +-- Add vimperator-like link hinting & following +require "follow" + +-- Use a custom charater set for hint labels +--local s = follow.label_styles +--follow.label_maker = s.sort(s.reverse(s.charset("asdfqwerzxcv"))) + +-- Match only hint labels +--follow.pattern_maker = follow.pattern_styles.match_label + +-- Add command history +require "cmdhist" + +-- Add search mode & binds +require "search" + +-- Add ordering of new tabs +require "taborder" + +-- Save web history +require "history" +require "history_chrome" + +require "introspector" + +-- Add command completion +require "completion" + +-- NoScript plugin, toggle scripts and or plugins on a per-domain basis. +-- `,ts` to toggle scripts, `,tp` to toggle plugins, `,tr` to reset. +-- Remove all "enable_scripts" & "enable_plugins" lines from your +-- domain_props table (in config/globals.lua) as this module will conflict. +--require "noscript" + +require "follow_selected" +require "go_input" +require "go_next_prev" +require "go_up" + +----------------------------- +-- End user script loading -- +----------------------------- + +-- Restore last saved session +local w = (session and session.restore()) +if w then + for i, uri in ipairs(uris) do + w:new_tab(uri, i == 1) + end +else + -- Or open new window + window.new(uris) +end + +------------------------------------------- +-- Open URIs from other luakit instances -- +------------------------------------------- + +if unique then + unique.add_signal("message", function (msg, screen) + local cmd, arg = string.match(msg, "^(%S+)%s*(.*)") + local w = lousy.util.table.values(window.bywidget)[1] + if cmd == "tabopen" then + w:new_tab(arg) + elseif cmd == "winopen" then + w = window.new((arg ~= "") and { arg } or {}) + end + w.win.screen = screen + w.win.urgency_hint = true + end) +end + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/luakit/theme.lua b/luakit/theme.lua new file mode 100644 index 0000000..e53390b --- /dev/null +++ b/luakit/theme.lua @@ -0,0 +1,74 @@ +-------------------------- +-- Default luakit theme -- +-------------------------- + +local theme = {} + +-- Default settings +theme.font = "terminus 8" +theme.fg = "#fff" +theme.bg = "#000" + +-- Genaral colours +theme.success_fg = "#0f0" +theme.loaded_fg = "#33AADD" +theme.error_fg = "#FFF" +theme.error_bg = "#F00" + +-- Warning colours +theme.warning_fg = "#F00" +theme.warning_bg = "#FFF" + +-- Notification colours +theme.notif_fg = "#444" +theme.notif_bg = "#FFF" + +-- Menu colours +theme.menu_fg = "#000" +theme.menu_bg = "#fff" +theme.menu_selected_fg = "#000" +theme.menu_selected_bg = "#FF0" +theme.menu_title_bg = "#fff" +theme.menu_primary_title_fg = "#f00" +theme.menu_secondary_title_fg = "#666" + +-- Proxy manager +theme.proxy_active_menu_fg = '#000' +theme.proxy_active_menu_bg = '#FFF' +theme.proxy_inactive_menu_fg = '#888' +theme.proxy_inactive_menu_bg = '#FFF' + +-- Statusbar specific +theme.sbar_fg = "#fff" +theme.sbar_bg = "#000" + +-- Downloadbar specific +theme.dbar_fg = "#fff" +theme.dbar_bg = "#000" +theme.dbar_error_fg = "#F00" + +-- Input bar specific +--theme.ibar_fg = "#000" +--theme.ibar_bg = "#fff" +theme.ibar_bg = "#000" +theme.ibar_fg = "#fff" + +-- Tab label +--theme.tab_fg = "#888" +theme.tab_fg = "#aaa" +--theme.tab_bg = "#222" +theme.tab_bg = "#000" +theme.tab_ntheme = "#ddd" +theme.selected_fg = "#fff" +--theme.selected_bg = "#000" +theme.selected_bg = "#056" +theme.selected_ntheme = "#ddd" +theme.loading_fg = "#33AADD" +theme.loading_bg = "#000" + +-- Trusted/untrusted ssl colours +theme.trust_fg = "#0F0" +theme.notrust_fg = "#F00" + +return theme +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/luakit/webview.lua b/luakit/webview.lua new file mode 100644 index 0000000..a7a7a59 --- /dev/null +++ b/luakit/webview.lua @@ -0,0 +1,350 @@ +-------------------------- +-- WebKit WebView class -- +-------------------------- + +-- Webview class table +webview = {} + +-- Table of functions which are called on new webview widgets. +webview.init_funcs = { + -- Set useragent + set_useragent = function (view, w) + view.user_agent = globals.useragent + end, + + -- Check if checking ssl certificates + checking_ssl = function (view, w) + local ca_file = soup.ssl_ca_file + if ca_file and os.exists(ca_file) then + w.checking_ssl = true + end + end, + + -- Update window and tab titles + title_update = function (view, w) + view:add_signal("property::title", function (v) + w:update_tablist() + if w.view == v then + w:update_win_title() + end + end) + end, + + -- Update uri label in statusbar + uri_update = function (view, w) + view:add_signal("property::uri", function (v) + w:update_tablist() + if w.view == v then + w:update_uri() + end + end) + end, + + -- Update history indicator + hist_update = function (view, w) + view:add_signal("load-status", function (v, status) + if w.view == v then + w:update_hist() + end + end) + end, + + -- Update tab titles + tablist_update = function (view, w) + view:add_signal("load-status", function (v, status) + if status == "provisional" or status == "finished" or status == "failed" then + w:update_tablist() + end + end) + end, + + -- Update scroll widget + scroll_update = function (view, w) + view:add_signal("expose", function (v) + if w.view == v then + w:update_scroll() + end + end) + end, + + -- Update progress widget + progress_update = function (view, w) + for _, sig in ipairs({"load-status", "property::progress"}) do + view:add_signal(sig, function (v) + if w.view == v then + w:update_progress() + w:update_ssl() + end + end) + end + end, + + -- Display hovered link in statusbar + link_hover_display = function (view, w) + view:add_signal("link-hover", function (v, link) + if w.view == v and link then + w:update_uri(link) + end + end) + view:add_signal("link-unhover", function (v) + if w.view == v then + w:update_uri() + end + end) + end, + + -- Clicking a form field automatically enters insert mode. + form_insert_mode = function (view, w) + view:add_signal("button-press", function (v, mods, button, context) + -- Clear start search marker + (w.search_state or {}).marker = nil + + if button == 1 and context.editable then + view:emit_signal("form-active") + end + end) + -- Emit root-active event in button release to prevent "missing" + -- buttons or links when the input bar hides. + view:add_signal("button-release", function (v, mods, button, context) + if button == 1 and not context.editable then + view:emit_signal("root-active") + end + end) + view:add_signal("form-active", function () + if not w.mode.passthrough then + w:set_mode("insert") + end + end) + view:add_signal("root-active", function () + if w.mode.reset_on_focus ~= false then + w:set_mode() + end + end) + end, + + -- Catch keys in non-passthrough modes + mode_key_filter = function (view, w) + view:add_signal("key-press", function () + if not w.mode.passthrough then + return true + end + end) + end, + + -- Try to match a button event to a users button binding else let the + -- press hit the webview. + button_bind_match = function (view, w) + view:add_signal("button-release", function (v, mods, button, context) + (w.search_state or {}).marker = nil + if w:hit(mods, button, { context = context }) then + return true + end + end) + end, + + -- Reset the mode on navigation + mode_reset_on_nav = function (view, w) + view:add_signal("load-status", function (v, status) + if status == "provisional" and w.view == v then + if w.mode.reset_on_navigation ~= false then + w:set_mode() + end + end + end) + end, + + -- Domain properties + domain_properties = function (view, w) + view:add_signal("load-status", function (v, status) + if status ~= "committed" or v.uri == "about:blank" then return end + -- Get domain + local domain = lousy.uri.parse(v.uri).host + -- Strip leading www. + domain = string.match(domain or "", "^www%.(.+)") or domain or "all" + -- Build list of domain props tables to join & load. + -- I.e. for luakit.org load .luakit.org, luakit.org, .org + local props = {domain_props.all or {}, domain_props[domain] or {}} + repeat + table.insert(props, 2, domain_props["."..domain] or {}) + domain = string.match(domain, "%.(.+)") + until not domain + -- Join all property tables + for k, v in pairs(lousy.util.table.join(unpack(props))) do + info("Domain prop: %s = %s (%s)", k, tostring(v), domain) + view[k] = v + end + end) + end, + + -- Action to take on mime type decision request. + mime_decision = function (view, w) + -- Return true to accept or false to reject from this signal. + view:add_signal("mime-type-decision", function (v, uri, mime) + info("Requested link: %s (%s)", uri, mime) + -- i.e. block binary files like *.exe + --if mime == "application/octet-stream" then + -- return false + --end + end) + end, + + -- Action to take on window open request. + --window_decision = function (view, w) + -- view:add_signal("new-window-decision", function (v, uri, reason) + -- if reason == "link-clicked" then + -- window.new({uri}) + -- else + -- w:new_tab(uri) + -- end + -- return true + -- end) + --end, + + create_webview = function (view, w) + -- Return a newly created webview in a new tab + view:add_signal("create-web-view", function (v) + return w:new_tab() + end) + end, + + -- Creates context menu popup from table (and nested tables). + -- Use `true` for menu separators. + --populate_popup = function (view, w) + -- view:add_signal("populate-popup", function (v) + -- return { + -- true, + -- { "_Toggle Source", function () w:toggle_source() end }, + -- { "_Zoom", { + -- { "Zoom _In", function () w:zoom_in() end }, + -- { "Zoom _Out", function () w:zoom_out() end }, + -- true, + -- { "Zoom _Reset", function () w:zoom_set() end }, }, }, + -- } + -- end) + --end, + + -- Action to take on resource request. + resource_request_decision = function (view, w) + view:add_signal("resource-request-starting", function(v, uri) + info("Requesting: %s", uri) + -- Return false to cancel the request. + end) + end, +} + +-- These methods are present when you index a window instance and no window +-- method is found in `window.methods`. The window then checks if there is an +-- active webview and calls the following methods with the given view instance +-- as the first argument. All methods must take `view` & `w` as the first two +-- arguments. +webview.methods = { + -- Reload with or without ignoring cache + reload = function (view, w, bypass_cache) + if bypass_cache then + view:reload_bypass_cache() + else + view:reload() + end + end, + + -- Toggle source view + toggle_source = function (view, w, show) + if show == nil then + view.view_source = not view.view_source + else + view.view_source = show + end + view:reload() + end, + + -- Zoom functions + zoom_in = function (view, w, step, full_zoom) + view.full_content_zoom = not not full_zoom + step = step or globals.zoom_step or 0.1 + view.zoom_level = view.zoom_level + step + end, + + zoom_out = function (view, w, step, full_zoom) + view.full_content_zoom = not not full_zoom + step = step or globals.zoom_step or 0.1 + view.zoom_level = math.max(0.01, view.zoom_level) - step + end, + + zoom_set = function (view, w, level, full_zoom) + view.full_content_zoom = not not full_zoom + view.zoom_level = level or 1.0 + end, + + -- History traversing functions + back = function (view, w, n) + view:go_back(n or 1) + end, + + forward = function (view, w, n) + view:go_forward(n or 1) + end, +} + +function webview.methods.scroll(view, w, new) + local s = view.scroll + for _, axis in ipairs{ "x", "y" } do + -- Relative px movement + if rawget(new, axis.."rel") then + s[axis] = s[axis] + new[axis.."rel"] + + -- Relative page movement + elseif rawget(new, axis .. "pagerel") then + s[axis] = s[axis] + math.ceil(s[axis.."page_size"] * new[axis.."pagerel"]) + + -- Absolute px movement + elseif rawget(new, axis) then + local n = new[axis] + if n == -1 then + s[axis] = s[axis.."max"] + else + s[axis] = n + end + + -- Absolute page movement + elseif rawget(new, axis.."page") then + s[axis] = math.ceil(s[axis.."page_size"] * new[axis.."page"]) + + -- Absolute percent movement + elseif rawget(new, axis .. "pct") then + s[axis] = math.ceil(s[axis.."max"] * (new[axis.."pct"]/100)) + end + end +end + +function webview.new(w) + local view = widget{type = "webview"} + + view.show_scrollbars = false + view.enforce_96_dpi = false + + -- Call webview init functions + for k, func in pairs(webview.init_funcs) do + func(view, w) + end + return view +end + +-- Insert webview method lookup on window structure +table.insert(window.indexes, 1, function (w, k) + if k == "view" then + local view = w.tabs[w.tabs:current()] + if view and type(view) == "widget" and view.type == "webview" then + w.view = view + return view + end + end + -- Lookup webview method + local func = webview.methods[k] + if not func then return end + local view = w.view + if view then + return function (_, ...) return func(view, w, ...) end + end +end) + +-- vim: et:sw=4:ts=8:sts=4:tw=80 diff --git a/luakit/window.lua b/luakit/window.lua new file mode 100644 index 0000000..5e921d6 --- /dev/null +++ b/luakit/window.lua @@ -0,0 +1,859 @@ +------------------ +-- Window class -- +------------------ + +require "lfs" + +-- Window class table +window = {} + +-- List of active windows by window widget +window.bywidget = setmetatable({}, { __mode = "k" }) + +-- Widget construction aliases +local function entry() return widget{type="entry"} end +local function eventbox() return widget{type="eventbox"} end +local function hbox() return widget{type="hbox"} end +local function label() return widget{type="label"} end +local function notebook() return widget{type="notebook"} end +local function vbox() return widget{type="vbox"} end + +-- Build and pack window widgets +function window.build() + -- Create a table for widgets and state variables for a window + local w = { + win = widget{type="window"}, + ebox = eventbox(), + layout = vbox(), + paned = widget{type="vpaned"}, + tabs = notebook(), + -- Tablist widget + tablist = lousy.widget.tablist(), + -- Status bar widgets + sbar = { + layout = hbox(), + ebox = eventbox(), + -- Left aligned widgets + l = { + layout = hbox(), + ebox = eventbox(), + uri = label(), + hist = label(), + loaded = label(), + }, + -- Fills space between the left and right aligned widgets + sep = eventbox(), + -- Right aligned widgets + r = { + layout = hbox(), + ebox = eventbox(), + buf = label(), + ssl = label(), + tabi = label(), + scroll = label(), + }, + }, + + -- Vertical menu window widget (completion results, bookmarks, qmarks, ..) + menu = lousy.widget.menu(), + + -- Input bar widgets + ibar = { + layout = hbox(), + ebox = eventbox(), + prompt = label(), + input = entry(), + }, + closed_tabs = {} + } + + -- Assemble window + w.ebox.child = w.paned + w.paned:pack1(w.layout) + w.win.child = w.ebox + + -- Pack tablist + w.layout:pack(w.tablist.widget) + + -- Pack notebook + w.layout:pack(w.tabs, { expand = true, fill = true }) + + -- Pack left-aligned statusbar elements + local l = w.sbar.l + l.layout:pack(l.uri) + l.layout:pack(l.hist) + l.layout:pack(l.loaded) + l.ebox.child = l.layout + + -- Pack right-aligned statusbar elements + local r = w.sbar.r + r.layout:pack(r.buf) + r.layout:pack(r.ssl) + r.layout:pack(r.tabi) + r.layout:pack(r.scroll) + r.ebox.child = r.layout + + -- Pack status bar elements + local s = w.sbar + s.layout:pack(l.ebox) + s.layout:pack(s.sep, { expand = true, fill = true }) + s.layout:pack(r.ebox) + s.ebox.child = s.layout + w.layout:pack(s.ebox) + + -- Pack menu widget + w.layout:pack(w.menu.widget) + w.menu:hide() + + -- Pack input bar + local i = w.ibar + i.layout:pack(i.prompt) + i.layout:pack(i.input, { expand = true, fill = true }) + i.ebox.child = i.layout + w.layout:pack(i.ebox) + + -- Other settings + i.input.show_frame = false + w.tabs.show_tabs = false + l.loaded:hide() + l.hist:hide() + l.uri.selectable = true + r.ssl:hide() + + -- Allows indexing of window struct by window widget + window.bywidget[w.win] = w + + return w +end + +-- Table of functions to call on window creation. Normally used to add signal +-- handlers to the new windows widgets. +window.init_funcs = { + -- Attach notebook widget signals + notebook_signals = function (w) + w.tabs:add_signal("page-added", function (nbook, view, idx) + luakit.idle_add(function () + w:update_tab_count() + w:update_tablist() + return false + end) + end) + w.tabs:add_signal("switch-page", function (nbook, view, idx) + w.view = nil + w:set_mode() + -- Update widgets after tab switch + luakit.idle_add(function () + w:update_tab_count() + w:update_win_title() + w:update_uri() + w:update_progress() + w:update_tablist() + w:update_buf() + w:update_ssl() + w:update_hist() + return false + end) + end) + w.tabs:add_signal("page-reordered", function (nbook, view, idx) + w:update_tab_count() + w:update_tablist() + end) + end, + + last_win_check = function (w) + w.win:add_signal("destroy", function () + -- call the quit function if this was the last window left + if #luakit.windows == 0 then luakit.quit() end + if w.close_win then w:close_win() end + end) + end, + + key_press_match = function (w) + w.win:add_signal("key-press", function (_, mods, key) + -- Match & exec a bind + local success, match = xpcall( + function () return w:hit(mods, key) end, + function (err) w:error(debug.traceback(err, 3)) end) + + if success and match then + return true + end + end) + end, + + tablist_tab_click = function (w) + w.tablist:add_signal("tab-clicked", function (_, index, mods, button) + if button == 1 then + w.tabs:switch(index) + return true + elseif button == 2 then + w:close_tab(w.tabs[index]) + return true + end + end) + end, + + apply_window_theme = function (w) + local s, i = w.sbar, w.ibar + + -- Set foregrounds + for wi, v in pairs({ + [s.l.uri] = theme.uri_sbar_fg, + [s.l.hist] = theme.hist_sbar_fg, + [s.l.loaded] = theme.sbar_loaded_fg, + [s.r.buf] = theme.buf_sbar_fg, + [s.r.tabi] = theme.tabi_sbar_fg, + [s.r.scroll] = theme.scroll_sbar_fg, + [i.prompt] = theme.prompt_ibar_fg, + [i.input] = theme.input_ibar_fg, + }) do wi.fg = v end + + -- Set backgrounds + for wi, v in pairs({ + [s.l.ebox] = theme.sbar_bg, + [s.r.ebox] = theme.sbar_bg, + [s.sep] = theme.sbar_bg, + [s.ebox] = theme.sbar_bg, + [i.ebox] = theme.ibar_bg, + [i.input] = theme.input_ibar_bg, + }) do wi.bg = v end + + -- Set fonts + for wi, v in pairs({ + [s.l.uri] = theme.uri_sbar_font, + [s.l.hist] = theme.hist_sbar_font, + [s.l.loaded] = theme.sbar_loaded_font, + [s.r.buf] = theme.buf_sbar_font, + [s.r.ssl] = theme.ssl_sbar_font, + [s.r.tabi] = theme.tabi_sbar_font, + [s.r.scroll] = theme.scroll_sbar_font, + [i.prompt] = theme.prompt_ibar_font, + [i.input] = theme.input_ibar_font, + }) do wi.font = v end + end, + + set_default_size = function (w) + local size = globals.default_window_size or "800x600" + if string.match(size, "^%d+x%d+$") then + w.win:set_default_size(string.match(size, "^(%d+)x(%d+)$")) + else + warn("E: window.lua: invalid window size: %q", size) + end + end, + + set_window_icon = function (w) + local path = (luakit.dev_paths and os.exists("./extras/luakit.png")) or + os.exists("/usr/share/pixmaps/luakit.png") + if path then w.win.icon = path end + end, + + clear_urgency_hint = function (w) + w.win:add_signal("focus", function () + w.win.urgency_hint = false + end) + end, +} + +-- Helper functions which operate on the window widgets or structure. +window.methods = { + -- Wrapper around the bind plugin's hit method + hit = function (w, mods, key, opts) + local opts = lousy.util.table.join(opts or {}, { + enable_buffer = w:is_mode("normal"), + buffer = w.buffer, + }) + + local caught, newbuf = lousy.bind.hit(w, w.binds, mods, key, opts) + if w.win then -- Check binding didn't cause window to exit + w.buffer = newbuf + w:update_buf() + end + return caught + end, + + -- Wrapper around the bind plugin's match_cmd method + match_cmd = function (w, buffer) + return lousy.bind.match_cmd(w, get_mode("command").binds, buffer) + end, + + -- enter command or characters into command line + enter_cmd = function (w, cmd, opts) + w:set_mode("command") + w:set_input(cmd, opts) + end, + + -- run command as if typed into the command line + run_cmd = function (w, cmd, opts) + w:enter_cmd(cmd, opts) + w:activate() + end, + + -- insert a string into the command line at the current cursor position + insert_cmd = function (w, str) + if not str then return end + local i = w.ibar.input + local text = i.text + local pos = i.position + local left, right = string.sub(text, 1, pos), string.sub(text, pos+1) + i.text = left .. str .. right + i.position = pos + #str + end, + + -- Emulates pressing the Return key in input field + activate = function (w) + w.ibar.input:emit_signal("activate") + end, + + del_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 and pos > 1 then + local left, right = string.sub(text, 2, pos), string.sub(text, pos+1) + if not string.find(left, "%s") then + left = "" + elseif string.find(left, "%w+%s*$") then + left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1) + elseif string.find(left, "%W+%s*$") then + left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1) + end + i.text = string.sub(text, 1, 1) .. left .. right + i.position = #left + 1 + end + end, + + del_line = function (w) + local i = w.ibar.input + if not string.match(i.text, "^[:/?]$") then + i.text = string.sub(i.text, 1, 1) + i.position = -1 + end + end, + + del_backward_char = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + + if pos > 1 then + i.text = string.sub(text, 0, pos - 1) .. string.sub(text, pos + 1) + i.position = pos - 1 + end + end, + + del_forward_char = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + + i.text = string.sub(text, 0, pos) .. string.sub(text, pos + 2) + i.position = pos + end, + + beg_line = function (w) + local i = w.ibar.input + i.position = 1 + end, + + end_line = function (w) + local i = w.ibar.input + i.position = -1 + end, + + forward_char = function (w) + local i = w.ibar.input + i.position = i.position + 1 + end, + + backward_char = function (w) + local i = w.ibar.input + local pos = i.position + if pos > 1 then + i.position = pos - 1 + end + end, + + forward_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 then + local right = string.sub(text, pos+1) + if string.find(right, "%w+") then + local _, move = string.find(right, "%w+") + i.position = pos + move + end + end + end, + + backward_word = function (w) + local i = w.ibar.input + local text = i.text + local pos = i.position + if text and #text > 1 and pos > 1 then + local left = string.reverse(string.sub(text, 2, pos)) + if string.find(left, "%w+") then + local _, move = string.find(left, "%w+") + i.position = pos - move + end + end + end, + + -- Shows a notification until the next keypress of the user. + notify = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt(msg, { fg = theme.notif_fg, bg = theme.notif_bg }) + end, + + warning = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt(msg, { fg = theme.warning_fg, bg = theme.warning_bg }) + end, + + error = function (w, msg, set_mode) + if set_mode ~= false then w:set_mode() end + w:set_prompt("Error: "..msg, { fg = theme.error_fg, bg = theme.error_bg }) + end, + + -- Set and display the prompt + set_prompt = function (w, text, opts) + local prompt, ebox, opts = w.ibar.prompt, w.ibar.ebox, opts or {} + prompt:hide() + -- Set theme + fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg + if prompt.fg ~= fg then prompt.fg = fg end + if ebox.bg ~= bg then ebox.bg = bg end + -- Set text or remain hidden + if text then + prompt.text = lousy.util.escape(text) + prompt:show() + end + end, + + -- Set display and focus the input bar + set_input = function (w, text, opts) + local input, opts = w.ibar.input, opts or {} + input:hide() + -- Set theme + fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg + if input.fg ~= fg then input.fg = fg end + if input.bg ~= bg then input.bg = bg end + -- Set text or remain hidden + if text then + input.text = "" + input:show() + input:focus() + input.text = text + input.position = opts.pos or -1 + end + end, + + -- GUI content update functions + update_tab_count = function (w) + w.sbar.r.tabi.text = string.format("[%d/%d]", w.tabs:current(), w.tabs:count()) + end, + + update_win_title = function (w) + local uri, title = w.view.uri, w.view.title + title = (title or "luakit") .. ((uri and " - " .. uri) or "") + local max = globals.max_title_len or 80 + if #title > max then title = string.sub(title, 1, max) .. "..." end + w.win.title = title + end, + + update_uri = function (w, link) + w.sbar.l.uri.text = lousy.util.escape((link and "Link: " .. link) + or (w.view and w.view.uri) or "about:blank") + end, + + update_progress = function (w) + local p = w.view.progress + local loaded = w.sbar.l.loaded + if not w.view:loading() or p == 1 then + loaded:hide() + else + loaded:show() + loaded.text = string.format("(%d%%)", p * 100) + end + end, + + update_scroll = function (w) + local scroll, label = w.view.scroll, w.sbar.r.scroll + local y, max, text = scroll.y, scroll.ymax + if max == 0 then text = "All" + elseif y == 0 then text = "Top" + elseif y == max then text = "Bot" + else text = string.format("%2d%%", (y / max) * 100) + end + if label.text ~= text then label.text = text end + end, + + update_ssl = function (w) + local trusted = w.view:ssl_trusted() + local ssl = w.sbar.r.ssl + if trusted ~= nil and not w.checking_ssl then + ssl.fg = theme.notrust_fg + ssl.text = "(nocheck)" + ssl:show() + elseif trusted == true then + ssl.fg = theme.trust_fg + ssl.text = "(trust)" + ssl:show() + elseif trusted == false then + ssl.fg = theme.notrust_fg + ssl.text = "(notrust)" + ssl:show() + else + ssl:hide() + end + end, + + update_hist = function (w) + local hist = w.sbar.l.hist + local back, forward = w.view:can_go_back(), w.view:can_go_forward() + local s = (back and "+" or "") .. (forward and "-" or "") + if s ~= "" then + hist.text = '['..s..']' + hist:show() + else + hist:hide() + end + end, + + update_buf = function (w) + local buf = w.sbar.r.buf + if w.buffer then + buf.text = lousy.util.escape(string.format(" %-3s", w.buffer)) + buf:show() + else + buf:hide() + end + end, + + update_binds = function (w, mode) + -- Generate the list of active key & buffer binds for this mode + w.binds = lousy.util.table.join((get_mode(mode) or {}).binds or {}, get_mode('all').binds or {}) + -- Clear & hide buffer + w.buffer = nil + w:update_buf() + end, + + update_tablist = function (w) + local current = w.tabs:current() + local fg, bg, nfg, snfg = theme.tab_fg, theme.tab_bg, theme.tab_ntheme, theme.selected_ntheme + local lfg, bfg, gfg = theme.tab_loading_fg, theme.tab_notrust_fg, theme.tab_trust_fg + local escape = lousy.util.escape + local tabs, tfmt = {}, ' %s %s' + + for i, view in ipairs(w.tabs.children) do + -- Get tab number theme + local ntheme = nfg + if view:loading() then -- Show loading on all tabs + ntheme = lfg + elseif current == i then -- Show ssl trusted/untrusted on current tab + local trusted = view:ssl_trusted() + ntheme = snfg + if trusted == false or (trusted ~= nil and not w.checking_ssl) then + ntheme = bfg + elseif trusted then + ntheme = gfg + end + end + local title = view.title or view.uri or "(Untitled)" + tabs[i] = { + title = string.format(tfmt, ntheme or fg, i, escape(title)), + fg = (current == i and theme.tab_selected_fg) or fg, + bg = (current == i and theme.tab_selected_bg) or bg, + } + end + + if #tabs < 2 then tabs, current = {}, 0 end + w.tablist:update(tabs, current) + end, + + new_tab = function (w, arg, switch, order) + local view + -- Use blank tab first + if w.has_blank and w.tabs:count() == 1 and w.tabs[1].uri == "about:blank" then + view = w.tabs[1] + end + w.has_blank = nil + -- Make new webview widget + if not view then + view = webview.new(w) + -- Get tab order function + if not order and taborder then + order = (switch == false and taborder.default_bg) + or taborder.default + end + pos = w.tabs:insert((order and order(w, view)) or -1, view) + if switch ~= false then w.tabs:switch(pos) end + end + -- Load uri or webview history table + if type(arg) == "string" then view.uri = arg + elseif type(arg) == "table" then view.history = arg end + -- Update statusbar widgets + w:update_tab_count() + w:update_tablist() + return view + end, + + -- close the current tab + close_tab = function (w, view, blank_last) + view = view or w.view + -- Treat a blank last tab as an empty notebook (if blank_last=true) + if blank_last ~= false and w.tabs:count() == 1 then + if not view:loading() and view.uri == "about:blank" then return end + w:new_tab("about:blank", false) + w.has_blank = true + end + -- Save tab history + local tab = {hist = view.history,} + -- And relative location + local index = w.tabs:indexof(view) + if index ~= 1 then tab.after = w.tabs[index-1] end + table.insert(w.closed_tabs, tab) + view:destroy() + w:update_tab_count() + w:update_tablist() + end, + + close_win = function (w, force) + -- Ask plugins if it's OK to close last window + if not force and (#luakit.windows == 1) then + local emsg = luakit.emit_signal("can-close", w) + if emsg then + assert(type(emsg) == "string", "invalid exit error message") + w:error(string.format("Can't close luakit: %s (force close " + .. "with :q! or :wq!)", emsg)) + return false + end + end + + w:emit_signal("close") + + -- Close all tabs + while w.tabs:count() ~= 0 do + w:close_tab(nil, false) + end + + -- Destroy tablist + w.tablist:destroy() + + -- Remove from window index + window.bywidget[w.win] = nil + + -- Clear window struct + w = setmetatable(w, {}) + + -- Recursively remove widgets from window + local children = lousy.util.recursive_remove(w.win) + -- Destroy all widgets + for i, c in ipairs(lousy.util.table.join(children, {w.win})) do + if c.hide then c:hide() end + c:destroy() + end + + -- Remove all window table vars + for k, _ in pairs(w) do w[k] = nil end + + -- Quit if closed last window + if #luakit.windows == 0 then luakit.quit() end + end, + + -- Navigate current view or open new tab + navigate = function (w, uri, view) + view = view or w.view + if view then + local js = string.match(uri, "^javascript:(.+)$") + if js then + return view:eval_js(luakit.uri_decode(js)) + end + view.uri = uri + else + return w:new_tab(uri) + end + end, + + -- Save, restart luakit and reload session. + restart = function (w) + -- Generate luakit launch command. + local args = {({string.gsub(luakit.execpath, " ", "\\ ")})[1]} + if luakit.verbose then table.insert(args, "-v") end + -- Relaunch without libunique bindings? + if luakit.nounique then table.insert(args, "-U") end + + -- Get new config path + local conf + if luakit.confpath ~= "/etc/xdg/luakit/rc.lua" and os.exists(luakit.confpath) then + conf = luakit.confpath + table.insert(args, string.format("-c %q", conf)) + end + + -- Check config has valid syntax + local cmd = table.concat(args, " ") + if luakit.spawn_sync(cmd .. " -k") ~= 0 then + return w:error("Cannot restart, syntax error in configuration file"..((conf and ": "..conf) or ".")) + end + + -- Save session. + local wins = {} + for _, w in pairs(window.bywidget) do table.insert(wins, w) end + session.save(wins) + + -- Replace current process with new luakit instance. + luakit.exec(cmd) + end, + + -- Intelligent open command which can detect a uri or search argument. + search_open = function (w, arg) + local lstring = lousy.util.string + local match, find = string.match, string.find + + -- Detect blank uris + if not arg or match(arg, "^%s*$") then return "about:blank" end + + -- Strip whitespace and split by whitespace into args table + local args = lstring.split(lstring.strip(arg)) + + -- Guess if first argument is an address, search engine, file + if #args == 1 then + local uri = args[1] + if uri == "about:blank" then return uri end + + -- Check if search engine name + if search_engines[uri] then + return string.format(search_engines[uri], "") + end + + -- Navigate if . or / in uri (I.e. domains, IP's, scheme://) + if find(uri, "%.") or find(uri, "/") then return uri end + + -- Navigate if this is a javascript-uri + if find(uri, "^javascript:") then return uri end + + -- Valid hostnames to check + local hosts = { "localhost" } + if globals.load_etc_hosts ~= false then + hosts = lousy.util.get_etc_hosts() + end + + -- Check hostnames + for _, h in pairs(hosts) do + if h == uri or match(uri, "^"..h..":%d+$") then return uri end + end + + -- Check for file in filesystem + if globals.check_filepath ~= false then + if lfs.attributes(uri) then return "file://" .. uri end + end + end + + -- Find search engine (or use search_engines.default) + local engine = "default" + if args[1] and search_engines[args[1]] then + engine = args[1] + table.remove(args, 1) + end + + -- URI encode search terms + local terms = luakit.uri_encode(table.concat(args, " ")) + return string.format(search_engines[engine], terms) + end, + + -- Increase (or decrease) the last found number in the current uri + inc_uri = function (w, arg) + local uri = string.gsub(w.view.uri, "(%d+)([^0-9]*)$", function (num, rest) + return string.format("%0"..#num.."d", tonumber(num) + (arg or 1)) .. rest + end) + return uri + end, + + -- Tab traversing functions + next_tab = function (w, n) + w.tabs:switch((((n or 1) + w.tabs:current() -1) % w.tabs:count()) + 1) + end, + + prev_tab = function (w, n) + w.tabs:switch(((w.tabs:current() - (n or 1) -1) % w.tabs:count()) + 1) + end, + + goto_tab = function (w, n) + if n and (n == -1 or n > 0) then + return w.tabs:switch((n <= w.tabs:count() and n) or -1) + end + end, + + -- For each tab, switches to that tab and calls the given function passing + -- it the view contained in the tab. + each_tab = function (w, fn) + for index = 1, w.tabs:count() do + w:goto_tab(index) + fn(w.tabs[index]) + end + end, + + -- If argument is form-active or root-active, emits signal. Ignores all + -- other signals. + emit_form_root_active_signal = function (w, s) + if s == "form-active" then + w.view:emit_signal("form-active") + elseif s == "root-active" then + w.view:emit_signal("root-active") + end + end, +} + +-- Ordered list of class index functions. Other classes (E.g. webview) are able +-- to add their own index functions to this list. +window.indexes = { + -- Find function in window.methods first + function (w, k) return window.methods[k] end +} + +-- Create new window +function window.new(uris) + local w = window.build() + + -- Set window metatable + setmetatable(w, { + __index = function (_, k) + -- Check widget structure first + local v = rawget(w, k) + if v then return v end + -- Call each window index function + for _, index in ipairs(window.indexes) do + v = index(w, k) + if v then return v end + end + end, + }) + + -- Setup window widget for signals + lousy.signal.setup(w) + + -- Call window init functions + for _, func in pairs(window.init_funcs) do + func(w) + end + + -- Populate notebook with tabs + for _, uri in ipairs(uris or {}) do + w:new_tab(w:search_open(uri), false) + end + + -- Make sure something is loaded + if w.tabs:count() == 0 then + w:new_tab(w:search_open(globals.homepage), false) + end + + -- Set initial mode + w:set_mode() + + -- Show window + w.win:show() + + return w +end + +-- vim: et:sw=4:ts=8:sts=4:tw=80