@ -0,0 +1,143 @@ |
|||||||
|
=================== |
||||||
|
Awesome WM Copycats |
||||||
|
=================== |
||||||
|
|
||||||
|
-------------------------------- |
||||||
|
Power themes for Awesome WM 3.5+ |
||||||
|
-------------------------------- |
||||||
|
|
||||||
|
:Author: Luke Bonham <dada [at] archlinux [dot] info> |
||||||
|
:Version: git |
||||||
|
:License: BY-NC-SA_ |
||||||
|
:Source: https://github.com/copycat-killer/awesome-copycats |
||||||
|
|
||||||
|
Description |
||||||
|
=========== |
||||||
|
|
||||||
|
A set of themes with the aim of making Awesome_ appearance awesome too! |
||||||
|
|
||||||
|
Notable features |
||||||
|
================ |
||||||
|
|
||||||
|
- Autohide widgets (*tell me only when you have to*) |
||||||
|
- Autostart applications |
||||||
|
- Fast MPD and volume shortcuts (first time you see this trick in Awesome) |
||||||
|
- Other shortcuts for copying to the clipboard (goodbye clipboard managers!), toggle wiboxes, widgets popups, screenshots capture, moving and magnifying clients |
||||||
|
- Quake drop-down terminal |
||||||
|
- Calendar with current day highlighted and months switch with a click |
||||||
|
- Elegant notifications for new mails, current song, volume level, hdd critical state, low battery |
||||||
|
- OpenWeatherMap integration |
||||||
|
- Net carrier status notifier |
||||||
|
- Colorful autoupdating icons |
||||||
|
- Symbolic tag names |
||||||
|
- TXT layoutbox |
||||||
|
- PNG wibox bar |
||||||
|
- Custom layouts |
||||||
|
- No borders when there's only one visible client |
||||||
|
- Powerful volume bar |
||||||
|
- Menubar menu |
||||||
|
- Vi-like client focus |
||||||
|
- Nice client border (colors change along with processes status) |
||||||
|
- Non-empty tag browsing |
||||||
|
- Dynamic useless gaps |
||||||
|
- Dynamic tagging |
||||||
|
|
||||||
|
Gallery |
||||||
|
======= |
||||||
|
|
||||||
|
**Multicolor** inspired by tip_ |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/650.png |
||||||
|
|
||||||
|
**Powerarrow Darker** inspired by romockee_ |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/649.png |
||||||
|
|
||||||
|
**Steamburn** copycatted from ok100_ [porting from dwm] |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/648.png |
||||||
|
|
||||||
|
**Blackburn** |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/553.png |
||||||
|
|
||||||
|
**Dremora** |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/652.png |
||||||
|
|
||||||
|
**Rainbow** |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/606.png |
||||||
|
|
||||||
|
**Holo** requested by amouly_ |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/651.png |
||||||
|
|
||||||
|
**Copland** inspired by foozer_ |
||||||
|
|
||||||
|
.. image:: http://dotshare.it/public/images/uploads/655.png |
||||||
|
|
||||||
|
Installation |
||||||
|
============ |
||||||
|
|
||||||
|
Using git you can have the full master branch: :: |
||||||
|
|
||||||
|
git clone --recursive https://github.com/copycat-killer/awesome-copycats.git |
||||||
|
|
||||||
|
then, move the contents of `awesome-copycats` to `~/.config/awesome`. |
||||||
|
|
||||||
|
Otherwise, download the latest two masters of ``awesome-copycats`` and ``lain`` (1_ and 2_), extract the first one, extract the second one into the first one, and put the contents of the first one into your Awesome directory. |
||||||
|
|
||||||
|
Usage |
||||||
|
===== |
||||||
|
|
||||||
|
Switch a theme this way: :: |
||||||
|
|
||||||
|
cd ~/.config/awesome |
||||||
|
cp rc.lua.theme rc.lua |
||||||
|
|
||||||
|
Alternatively, you can use `switch-theme.sh`_, which will also update to the latest commit. |
||||||
|
|
||||||
|
Then customize your ``rc.lua`` and restart Awesome (``Mod4 + ctrl + r``). |
||||||
|
|
||||||
|
Notes |
||||||
|
===== |
||||||
|
|
||||||
|
Only compatible with **stable** versions of Lua and Awesome (git/unstable versions are *not* supported). |
||||||
|
|
||||||
|
Complements are provided by lain_: be sure to meet its dependencies_. |
||||||
|
|
||||||
|
Fonts are Terminus_ (Multicolor, Powerarrow Darker) and Tamsyn_ (other ones). |
||||||
|
|
||||||
|
Every theme has a colorscheme_. |
||||||
|
|
||||||
|
Blackburn and Dremora use Icons_: be sure to have bitmaps enabled if running under Debian_ or Ubuntu_. |
||||||
|
|
||||||
|
Feel free to email me if you have any request. |
||||||
|
|
||||||
|
Notable forks |
||||||
|
============= |
||||||
|
|
||||||
|
Does your fork look very nice? You can send it to me! |
||||||
|
|
||||||
|
The best ones will be linked here. |
||||||
|
|
||||||
|
|
||||||
|
.. _BY-NC-SA: http://creativecommons.org/licenses/by-nc-sa/4.0/ |
||||||
|
.. _Awesome: http://awesome.naquadah.org/ |
||||||
|
.. _tip: https://bbs.archlinux.org/profile.php?id=51327 |
||||||
|
.. _romockee: https://github.com/romockee/powerarrow |
||||||
|
.. _ok100: http://ok100.deviantart.com/art/DWM-January-2013-348656846 |
||||||
|
.. _amouly: https://bbs.archlinux.org/viewtopic.php?pid=1307158#p1307158 |
||||||
|
.. _foozer: http://dotshare.it/dots/499/ |
||||||
|
.. _1: https://github.com/copycat-killer/awesome-copycats/archive/master.zip |
||||||
|
.. _2: https://github.com/copycat-killer/lain/archive/master.zip |
||||||
|
.. _`switch-theme.sh`: https://github.com/copycat-killer/awesome-copycats/issues/36 |
||||||
|
.. _lain: https://github.com/copycat-killer/lain |
||||||
|
.. _dependencies: https://github.com/copycat-killer/lain/wiki |
||||||
|
.. _Terminus: http://terminus-font.sourceforge.net/ |
||||||
|
.. _Tamsyn: http://www.fial.com/~scott/tamsyn-font/ |
||||||
|
.. _colorscheme: https://github.com/copycat-killer/dots/tree/master/.colors |
||||||
|
.. _Icons: https://github.com/copycat-killer/dots/tree/master/.fonts |
||||||
|
.. _Debian: http://weiwu.sdf.org/100921.html |
||||||
|
.. _Ubuntu: https://wiki.ubuntu.com/Fonts#Enabling_Bitmapped_Fonts |
@ -0,0 +1,57 @@ |
|||||||
|
------------------------------------------ |
||||||
|
-- Effortless wmii-style dynamic tagging. |
||||||
|
------------------------------------------ |
||||||
|
-- Lucas de Vries <lucas@glacicle.org> |
||||||
|
-- Licensed under the WTFPL version 2 |
||||||
|
-- * http://sam.zoy.org/wtfpl/COPYING |
||||||
|
----------------------------------------- |
||||||
|
-- Cut version |
||||||
|
----------------------------------------- |
||||||
|
|
||||||
|
-- Grab environment |
||||||
|
local ipairs = ipairs |
||||||
|
local awful = require("awful") |
||||||
|
local table = table |
||||||
|
local capi = { |
||||||
|
screen = screen, |
||||||
|
} |
||||||
|
|
||||||
|
-- Eminent: Effortless wmii-style dynamic tagging |
||||||
|
module("eminent") |
||||||
|
|
||||||
|
-- Grab the original functions we're replacing |
||||||
|
local deflayout = nil |
||||||
|
local orig = { |
||||||
|
new = awful.tag.new, |
||||||
|
taglist = awful.widget.taglist.new, |
||||||
|
filter = awful.widget.taglist.filter.all, |
||||||
|
} |
||||||
|
|
||||||
|
-- Return tags with stuff on them, mark others hidden |
||||||
|
function gettags(screen) |
||||||
|
local tags = {} |
||||||
|
|
||||||
|
for k, t in ipairs(capi.screen[screen]:tags()) do |
||||||
|
if t.selected or #t:clients() > 0 then |
||||||
|
awful.tag.setproperty(t, "hide", false) |
||||||
|
table.insert(tags, t) |
||||||
|
else |
||||||
|
awful.tag.setproperty(t, "hide", true) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return tags |
||||||
|
end |
||||||
|
|
||||||
|
-- Pre-create tags |
||||||
|
awful.tag.new = function (names, screen, layout) |
||||||
|
deflayout = layout and layout[1] or layout |
||||||
|
return orig.new(names, screen, layout) |
||||||
|
end |
||||||
|
|
||||||
|
-- Taglist label functions |
||||||
|
awful.widget.taglist.filter.all = function (t, args) |
||||||
|
if t.selected or #t:clients() > 0 then |
||||||
|
return orig.filter(t, args) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,47 @@ |
|||||||
|
Lain |
||||||
|
==== |
||||||
|
|
||||||
|
-------------------------------------------------- |
||||||
|
Layouts, widgets and utilities for Awesome WM 3.5+ |
||||||
|
-------------------------------------------------- |
||||||
|
|
||||||
|
:Author: Luke Bonham <dada [at] archlinux [dot] info> |
||||||
|
:Version: git |
||||||
|
:License: GNU-GPL2_ |
||||||
|
:Source: https://github.com/copycat-killer/lain |
||||||
|
|
||||||
|
Description |
||||||
|
----------- |
||||||
|
|
||||||
|
Successor of awesome-vain_, this module provides new layouts, a set of widgets and utility functions, in order to improve Awesome_ usability and configurability. |
||||||
|
|
||||||
|
Read the wiki_ for all the info. |
||||||
|
|
||||||
|
Contributions |
||||||
|
------------- |
||||||
|
|
||||||
|
Any contribution is welcome! Feel free to make a pull request. |
||||||
|
|
||||||
|
Just make sure that: |
||||||
|
|
||||||
|
- Your code fits with the general style of the module. In particular, you should use the same indentation pattern that the code uses, and also avoid adding space at the ends of lines. |
||||||
|
|
||||||
|
- Your code its easy to understand, maintainable, and modularized. You should also avoid code duplication wherever possible by adding functions or using ``lain.helpers``. If something is unclear, and you can't write it in such a way that it will be clear, explain it with a comment. |
||||||
|
|
||||||
|
- You test your changes before submitting to make sure that not only your code works, but did not break other parts of the module too! |
||||||
|
|
||||||
|
- You eventually update ``wiki`` submodule with a thorough section. |
||||||
|
|
||||||
|
Contributed widgets have to be put in ``lain/widgets/contrib``. |
||||||
|
|
||||||
|
Screenshots |
||||||
|
----------- |
||||||
|
|
||||||
|
.. image:: http://i.imgur.com/8D9A7lW.png |
||||||
|
.. image:: http://i.imgur.com/9Iv3OR3.png |
||||||
|
.. image:: http://i.imgur.com/STCPcaJ.png |
||||||
|
|
||||||
|
.. _GNU-GPL2: http://www.gnu.org/licenses/gpl-2.0.html |
||||||
|
.. _awesome-vain: https://github.com/vain/awesome-vain |
||||||
|
.. _Awesome: http://awesome.naquadah.org/ |
||||||
|
.. _wiki: https://github.com/copycat-killer/lain/wiki |
@ -0,0 +1,72 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2015, worron |
||||||
|
* (c) 2013, Alexander Yakushev |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
-- Asynchronous io.popen for Awesome WM. |
||||||
|
-- How to use: |
||||||
|
-- asyncshell.request('wscript -Kiev', function(output) wwidget.text = output end) |
||||||
|
|
||||||
|
-- Grab environment |
||||||
|
local awful = require('awful') |
||||||
|
|
||||||
|
-- Initialize tables for module |
||||||
|
asyncshell = { request_table = {}, id_counter = 0 } |
||||||
|
|
||||||
|
-- Request counter |
||||||
|
local function next_id() |
||||||
|
asyncshell.id_counter = (asyncshell.id_counter + 1) % 10000 |
||||||
|
return asyncshell.id_counter |
||||||
|
end |
||||||
|
|
||||||
|
-- Remove given request |
||||||
|
function asyncshell.clear(id) |
||||||
|
if asyncshell.request_table[id] then |
||||||
|
if asyncshell.request_table[id].timer then |
||||||
|
asyncshell.request_table[id].timer:stop() |
||||||
|
asyncshell.request_table[id].timer = nil |
||||||
|
end |
||||||
|
asyncshell.request_table[id] = nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Sends an asynchronous request for an output of the shell command |
||||||
|
-- @param command Command to be executed and taken output from |
||||||
|
-- @param callback Function to be called when the command finishes |
||||||
|
-- @param timeout Maximum amount of time to wait for the result (optional) |
||||||
|
function asyncshell.request(command, callback, timeout) |
||||||
|
local id = next_id() |
||||||
|
asyncshell.request_table[id] = { callback = callback } |
||||||
|
|
||||||
|
local formatted_command = string.gsub(command, '"','\"') |
||||||
|
|
||||||
|
local req = string.format( |
||||||
|
"echo \"asyncshell.deliver(%s, [[\\\"$(%s)\\\"]])\" | awesome-client &", |
||||||
|
id, formatted_command |
||||||
|
) |
||||||
|
|
||||||
|
awful.util.spawn_with_shell(req) |
||||||
|
|
||||||
|
if timeout then |
||||||
|
asyncshell.request_table[id].timer = timer({ timeout = timeout }) |
||||||
|
asyncshell.request_table[id].timer:connect_signal("timeout", function() asyncshell.clear(id) end) |
||||||
|
asyncshell.request_table[id].timer:start() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Calls the remembered callback function on the output of the shell command |
||||||
|
-- @param id Request ID |
||||||
|
-- @param output Shell command output to be delievered |
||||||
|
function asyncshell.deliver(id, output) |
||||||
|
local output = string.sub(output, 2, -2) |
||||||
|
if asyncshell.request_table[id] then |
||||||
|
asyncshell.request_table[id].callback(output) |
||||||
|
asyncshell.clear(id) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return asyncshell |
@ -0,0 +1,116 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local debug = require("debug") |
||||||
|
|
||||||
|
local capi = { timer = timer } |
||||||
|
local io = { open = io.open, |
||||||
|
lines = io.lines, |
||||||
|
popen = io.popen } |
||||||
|
local rawget = rawget |
||||||
|
|
||||||
|
-- Lain helper functions for internal use |
||||||
|
-- lain.helpers |
||||||
|
local helpers = {} |
||||||
|
|
||||||
|
helpers.lain_dir = debug.getinfo(1, 'S').source:match[[^@(.*/).*$]] |
||||||
|
helpers.icons_dir = helpers.lain_dir .. 'icons/' |
||||||
|
helpers.scripts_dir = helpers.lain_dir .. 'scripts/' |
||||||
|
|
||||||
|
-- {{{ Modules loader |
||||||
|
|
||||||
|
function helpers.wrequire(table, key) |
||||||
|
local module = rawget(table, key) |
||||||
|
return module or require(table._NAME .. '.' .. key) |
||||||
|
end |
||||||
|
|
||||||
|
-- }}} |
||||||
|
|
||||||
|
-- {{{ File operations |
||||||
|
|
||||||
|
-- see if the file exists and is readable |
||||||
|
function helpers.file_exists(file) |
||||||
|
local f = io.open(file) |
||||||
|
if f then |
||||||
|
local s = f:read() |
||||||
|
f:close() |
||||||
|
f = s |
||||||
|
end |
||||||
|
return f ~= nil |
||||||
|
end |
||||||
|
|
||||||
|
-- get all lines from a file, returns an empty |
||||||
|
-- list/table if the file does not exist |
||||||
|
function helpers.lines_from(file) |
||||||
|
if not helpers.file_exists(file) then return {} end |
||||||
|
lines = {} |
||||||
|
for line in io.lines(file) do |
||||||
|
lines[#lines + 1] = line |
||||||
|
end |
||||||
|
return lines |
||||||
|
end |
||||||
|
|
||||||
|
-- get first line of a file, return nil if |
||||||
|
-- the file does not exist |
||||||
|
function helpers.first_line(file) |
||||||
|
return helpers.lines_from(file)[1] |
||||||
|
end |
||||||
|
|
||||||
|
-- get first non empty line from a file, |
||||||
|
-- returns nil otherwise |
||||||
|
function helpers.first_nonempty_line(file) |
||||||
|
for k,v in pairs(helpers.lines_from(file)) do |
||||||
|
if #v then return v end |
||||||
|
end |
||||||
|
return nil |
||||||
|
end |
||||||
|
|
||||||
|
-- }}} |
||||||
|
|
||||||
|
-- {{{ Timer maker |
||||||
|
|
||||||
|
helpers.timer_table = {} |
||||||
|
|
||||||
|
function helpers.newtimer(name, timeout, fun, nostart) |
||||||
|
helpers.timer_table[name] = capi.timer({ timeout = timeout }) |
||||||
|
helpers.timer_table[name]:connect_signal("timeout", fun) |
||||||
|
helpers.timer_table[name]:start() |
||||||
|
if not nostart then |
||||||
|
helpers.timer_table[name]:emit_signal("timeout") |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- }}} |
||||||
|
|
||||||
|
-- {{{ Pipe operations |
||||||
|
|
||||||
|
-- read the full output of a pipe (command) |
||||||
|
function helpers.read_pipe(cmd) |
||||||
|
local f = assert(io.popen(cmd)) |
||||||
|
local output = f:read("*all") |
||||||
|
f:close() |
||||||
|
return output |
||||||
|
end |
||||||
|
|
||||||
|
-- }}} |
||||||
|
|
||||||
|
-- {{{ A map utility |
||||||
|
|
||||||
|
helpers.map_table = {} |
||||||
|
|
||||||
|
function helpers.set_map(element, value) |
||||||
|
helpers.map_table[element] = value |
||||||
|
end |
||||||
|
|
||||||
|
function helpers.get_map(element) |
||||||
|
return helpers.map_table[element] |
||||||
|
end |
||||||
|
|
||||||
|
-- }}} |
||||||
|
|
||||||
|
return helpers |
After Width: | Height: | Size: 888 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 233 B |
After Width: | Height: | Size: 235 B |
After Width: | Height: | Size: 235 B |
After Width: | Height: | Size: 233 B |
After Width: | Height: | Size: 169 B |
After Width: | Height: | Size: 169 B |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 191 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 399 B |
After Width: | Height: | Size: 252 B |
After Width: | Height: | Size: 289 B |
After Width: | Height: | Size: 634 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 7.2 KiB |
@ -0,0 +1,3 @@ |
|||||||
|
[Plain Weather Icons](http://merlinthered.deviantart.com/art/plain-weather-icons-157162192), created by [MerlinTheRed](http://merlinthered.deviantart.com/). |
||||||
|
|
||||||
|
<a href="http://creativecommons.org/licenses/by-nc-sa/2.5/"><img src="http://i.creativecommons.org/l/by-nc-sa/2.5/80x15.png" align="right"></a> |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 941 B |
@ -0,0 +1,21 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Lain |
||||||
|
Layouts, widgets and utilities for Awesome WM |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
package.loaded.lain = nil |
||||||
|
|
||||||
|
local lain = |
||||||
|
{ |
||||||
|
layout = require("lain.layout"), |
||||||
|
util = require("lain.util"), |
||||||
|
widgets = require("lain.widgets") |
||||||
|
} |
||||||
|
|
||||||
|
return lain |
@ -0,0 +1,80 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local tag = require("awful.tag") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
|
||||||
|
local cascade = |
||||||
|
{ |
||||||
|
name = "cascade", |
||||||
|
nmaster = 0, |
||||||
|
offset_x = 32, |
||||||
|
offset_y = 8 |
||||||
|
} |
||||||
|
|
||||||
|
function cascade.arrange(p) |
||||||
|
|
||||||
|
-- Cascade windows. |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width. |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset. |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- Screen. |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.x = wa.x + global_border |
||||||
|
wa.y = wa.y + global_border |
||||||
|
|
||||||
|
-- Opening a new window will usually force all existing windows to |
||||||
|
-- get resized. This wastes a lot of CPU time. So let's set a lower |
||||||
|
-- bound to "how_many": This wastes a little screen space but you'll |
||||||
|
-- get a much better user experience. |
||||||
|
local t = tag.selected(p.screen) |
||||||
|
local num_c |
||||||
|
if cascade.nmaster > 0 |
||||||
|
then |
||||||
|
num_c = cascade.nmaster |
||||||
|
else |
||||||
|
num_c = tag.getnmaster(t) |
||||||
|
end |
||||||
|
|
||||||
|
local how_many = #cls |
||||||
|
if how_many < num_c |
||||||
|
then |
||||||
|
how_many = num_c |
||||||
|
end |
||||||
|
|
||||||
|
local current_offset_x = cascade.offset_x * (how_many - 1) |
||||||
|
local current_offset_y = cascade.offset_y * (how_many - 1) |
||||||
|
|
||||||
|
-- Iterate. |
||||||
|
for i = 1,#cls,1 |
||||||
|
do |
||||||
|
local c = cls[i] |
||||||
|
local g = {} |
||||||
|
|
||||||
|
g.x = wa.x + (how_many - i) * cascade.offset_x |
||||||
|
g.y = wa.y + (i - 1) * cascade.offset_y |
||||||
|
g.width = wa.width - current_offset_x |
||||||
|
g.height = wa.height - current_offset_y |
||||||
|
|
||||||
|
c:geometry(g) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return cascade |
@ -0,0 +1,167 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local tag = require("awful.tag") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local cascadetile = |
||||||
|
{ |
||||||
|
name = "cascadetile", |
||||||
|
nmaster = 0, |
||||||
|
ncol = 0, |
||||||
|
mwfact = 0, |
||||||
|
offset_x = 5, |
||||||
|
offset_y = 32, |
||||||
|
extra_padding = 0 |
||||||
|
} |
||||||
|
|
||||||
|
function cascadetile.arrange(p) |
||||||
|
|
||||||
|
-- Layout with one fixed column meant for a master window. Its |
||||||
|
-- width is calculated according to mwfact. Other clients are |
||||||
|
-- cascaded or "tabbed" in a slave column on the right. |
||||||
|
|
||||||
|
-- It's a bit hard to demonstrate the behaviour with ASCII-images... |
||||||
|
-- |
||||||
|
-- (1) (2) (3) (4) |
||||||
|
-- +----------+---+ +----------+---+ +----------+---+ +----------+---+ |
||||||
|
-- | | | | | 3 | | | 4 | | +---+| |
||||||
|
-- | | | -> | | | -> | +---++ -> | +---+|+ |
||||||
|
-- | 1 | 2 | | 1 +---++ | 1 | 3 || | 1 +---+|+| |
||||||
|
-- | | | | | 2 || | +---++| | +---+|+ | |
||||||
|
-- | | | | | || | | 2 | | | | 2 |+ | |
||||||
|
-- +----------+---+ +---------+---++ +--------+---+-+ +------+---+---+ |
||||||
|
|
||||||
|
-- A useless gap (like the dwm patch) can be defined with |
||||||
|
-- beautiful.useless_gap_width. |
||||||
|
local useless_gap = tonumber(beautiful.useless_gap_width) or 0 |
||||||
|
if useless_gap < 0 then useless_gap = 0 end |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- Screen. |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
-- Borders are factored in. |
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
|
||||||
|
-- Width of main column? |
||||||
|
local t = tag.selected(p.screen) |
||||||
|
local mwfact |
||||||
|
if cascadetile.mwfact > 0 |
||||||
|
then |
||||||
|
mwfact = cascadetile.mwfact |
||||||
|
else |
||||||
|
mwfact = tag.getmwfact(t) |
||||||
|
end |
||||||
|
|
||||||
|
-- Make slave windows overlap main window? Do this if ncol is 1. |
||||||
|
local overlap_main |
||||||
|
if cascadetile.ncol > 0 |
||||||
|
then |
||||||
|
overlap_main = cascadetile.ncol |
||||||
|
else |
||||||
|
overlap_main = tag.getncol(t) |
||||||
|
end |
||||||
|
|
||||||
|
-- Minimum space for slave windows? See cascade.lua. |
||||||
|
local num_c |
||||||
|
if cascadetile.nmaster > 0 |
||||||
|
then |
||||||
|
num_c = cascadetile.nmaster |
||||||
|
else |
||||||
|
num_c = tag.getnmaster(t) |
||||||
|
end |
||||||
|
|
||||||
|
local how_many = #cls - 1 |
||||||
|
if how_many < num_c |
||||||
|
then |
||||||
|
how_many = num_c |
||||||
|
end |
||||||
|
local current_offset_x = cascadetile.offset_x * (how_many - 1) |
||||||
|
local current_offset_y = cascadetile.offset_y * (how_many - 1) |
||||||
|
|
||||||
|
if #cls > 0 |
||||||
|
then |
||||||
|
-- Main column, fixed width and height. |
||||||
|
local c = cls[#cls] |
||||||
|
local g = {} |
||||||
|
local mainwid = wa.width * mwfact |
||||||
|
local slavewid = wa.width - mainwid |
||||||
|
|
||||||
|
if overlap_main == 1 |
||||||
|
then |
||||||
|
g.width = wa.width |
||||||
|
|
||||||
|
-- The size of the main window may be reduced a little bit. |
||||||
|
-- This allows you to see if there are any windows below the |
||||||
|
-- main window. |
||||||
|
-- This only makes sense, though, if the main window is |
||||||
|
-- overlapping everything else. |
||||||
|
g.width = g.width - cascadetile.extra_padding |
||||||
|
else |
||||||
|
g.width = mainwid |
||||||
|
end |
||||||
|
|
||||||
|
g.height = wa.height |
||||||
|
g.x = wa.x + global_border |
||||||
|
g.y = wa.y + global_border |
||||||
|
if useless_gap > 0 |
||||||
|
then |
||||||
|
-- Reduce width once and move window to the right. Reduce |
||||||
|
-- height twice, however. |
||||||
|
g.width = g.width - useless_gap |
||||||
|
g.height = g.height - 2 * useless_gap |
||||||
|
g.x = g.x + useless_gap |
||||||
|
g.y = g.y + useless_gap |
||||||
|
|
||||||
|
-- When there's no window to the right, add an additional |
||||||
|
-- gap. |
||||||
|
if overlap_main == 1 |
||||||
|
then |
||||||
|
g.width = g.width - useless_gap |
||||||
|
end |
||||||
|
end |
||||||
|
c:geometry(g) |
||||||
|
|
||||||
|
-- Remaining clients stacked in slave column, new ones on top. |
||||||
|
if #cls > 1 |
||||||
|
then |
||||||
|
for i = (#cls - 1),1,-1 |
||||||
|
do |
||||||
|
c = cls[i] |
||||||
|
g = {} |
||||||
|
g.width = slavewid - current_offset_x |
||||||
|
g.height = wa.height - current_offset_y |
||||||
|
g.x = wa.x + mainwid + (how_many - i) * cascadetile.offset_x |
||||||
|
g.y = wa.y + (i - 1) * cascadetile.offset_y + global_border |
||||||
|
if useless_gap > 0 |
||||||
|
then |
||||||
|
g.width = g.width - 2 * useless_gap |
||||||
|
g.height = g.height - 2 * useless_gap |
||||||
|
g.x = g.x + useless_gap |
||||||
|
g.y = g.y + useless_gap |
||||||
|
end |
||||||
|
c:geometry(g) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return cascadetile |
@ -0,0 +1,163 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010, Nicolas Estibals |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local tag = require("awful.tag") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local math = { ceil = math.ceil, |
||||||
|
floor = math.floor, |
||||||
|
max = math.max } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local centerfair = { name = "centerfair" } |
||||||
|
|
||||||
|
function centerfair.arrange(p) |
||||||
|
-- Layout with fixed number of vertical columns (read from nmaster). |
||||||
|
-- Cols are centerded until there is nmaster columns, then windows |
||||||
|
-- are stacked in the slave columns, with at most ncol clients per |
||||||
|
-- column if possible. |
||||||
|
|
||||||
|
-- with nmaster=3 and ncol=1 you'll have |
||||||
|
-- (1) (2) (3) |
||||||
|
-- +---+---+---+ +-+---+---+-+ +---+---+---+ |
||||||
|
-- | | | | | | | | | | | | | |
||||||
|
-- | | 1 | | -> | | 1 | 2 | | -> | 1 | 2 | 3 | -> |
||||||
|
-- | | | | | | | | | | | | | |
||||||
|
-- +---+---+---+ +-+---+---+-+ +---+---+---+ |
||||||
|
|
||||||
|
-- (4) (5) |
||||||
|
-- +---+---+---+ +---+---+---+ |
||||||
|
-- | | | 3 | | | 2 | 4 | |
||||||
|
-- + 1 + 2 +---+ -> + 1 +---+---+ |
||||||
|
-- | | | 4 | | | 3 | 5 | |
||||||
|
-- +---+---+---+ +---+---+---+ |
||||||
|
|
||||||
|
-- A useless gap (like the dwm patch) can be defined with |
||||||
|
-- beautiful.useless_gap_width . |
||||||
|
local useless_gap = tonumber(beautiful.useless_gap_width) or 0 |
||||||
|
if useless_gap < 0 then useless_gap = 0 end |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- Screen. |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
-- Borders are factored in. |
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
|
||||||
|
-- How many vertical columns? Read from nmaster on the tag. |
||||||
|
local t = tag.selected(p.screen) |
||||||
|
local num_x = centerfair.nmaster or tag.getnmaster(t) |
||||||
|
local ncol = centerfair.ncol or tag.getncol(t) |
||||||
|
if num_x <= 2 then num_x = 2 end |
||||||
|
|
||||||
|
local width = math.floor((wa.width-(num_x+1)*useless_gap) / num_x) |
||||||
|
|
||||||
|
local offset_y = wa.y + useless_gap |
||||||
|
if #cls < num_x |
||||||
|
then |
||||||
|
-- Less clients than the number of columns, let's center it! |
||||||
|
local offset_x = wa.x + useless_gap + (wa.width - #cls*width - (#cls+1)*useless_gap) / 2 |
||||||
|
local g = {} |
||||||
|
g.width = width |
||||||
|
g.height = wa.height - 2*useless_gap - 2 |
||||||
|
g.y = offset_y + global_border |
||||||
|
for i = 1, #cls do |
||||||
|
g.x = offset_x + (#cls - i) * (width + useless_gap + 2) + global_border |
||||||
|
cls[i]:geometry(g) |
||||||
|
end |
||||||
|
else |
||||||
|
-- More clients than the number of columns, let's arrange it! |
||||||
|
local offset_x = wa.x |
||||||
|
if useless_gap > 0 then |
||||||
|
offset_x = offset_x |
||||||
|
end |
||||||
|
|
||||||
|
-- Master client deserves a special treatement |
||||||
|
local g = {} |
||||||
|
g.width = wa.width - (num_x - 1) * width - num_x * 2*useless_gap - 2 |
||||||
|
g.height = wa.height - 2*useless_gap - 2 |
||||||
|
g.x = offset_x + useless_gap + global_border |
||||||
|
g.y = offset_y + global_border |
||||||
|
|
||||||
|
cls[#cls]:geometry(g) |
||||||
|
|
||||||
|
-- Treat the other clients |
||||||
|
|
||||||
|
-- Compute distribution of clients among columns |
||||||
|
local num_y ={} |
||||||
|
do |
||||||
|
local remaining_clients = #cls-1 |
||||||
|
local ncol_min = math.ceil(remaining_clients/(num_x-1)) |
||||||
|
if ncol >= ncol_min |
||||||
|
then |
||||||
|
for i = (num_x-1), 1, -1 do |
||||||
|
if (remaining_clients-i+1) < ncol |
||||||
|
then |
||||||
|
num_y[i] = remaining_clients-i + 1 |
||||||
|
else |
||||||
|
num_y[i] = ncol |
||||||
|
end |
||||||
|
remaining_clients = remaining_clients - num_y[i] |
||||||
|
end |
||||||
|
else |
||||||
|
local rem = remaining_clients % (num_x-1) |
||||||
|
if rem ==0 |
||||||
|
then |
||||||
|
for i = 1, num_x-1 do |
||||||
|
num_y[i] = ncol_min |
||||||
|
end |
||||||
|
else |
||||||
|
for i = 1, num_x-1 do |
||||||
|
num_y[i] = ncol_min - 1 |
||||||
|
end |
||||||
|
for i = 0, rem-1 do |
||||||
|
num_y[num_x-1-i] = num_y[num_x-1-i] + 1 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Compute geometry of the other clients |
||||||
|
local nclient = #cls-1 -- we start with the 2nd client |
||||||
|
g.x = g.x + g.width+useless_gap + 2 |
||||||
|
g.width = width |
||||||
|
|
||||||
|
if useless_gap > 0 then |
||||||
|
g.width = g.width + useless_gap - 2 |
||||||
|
end |
||||||
|
|
||||||
|
for i = 1, (num_x-1) do |
||||||
|
to_remove = 2 |
||||||
|
g.height = math.floor((wa.height - (num_y[i] * useless_gap)) / num_y[i]) |
||||||
|
g.y = offset_y + global_border |
||||||
|
for j = 0, (num_y[i]-2) do |
||||||
|
cls[nclient]:geometry(g) |
||||||
|
nclient = nclient - 1 |
||||||
|
g.y = g.y + g.height+useless_gap + 2 |
||||||
|
to_remove = to_remove + 2 |
||||||
|
end |
||||||
|
g.height = wa.height - num_y[i]*useless_gap - (num_y[i]-1)*g.height - useless_gap - to_remove |
||||||
|
cls[nclient]:geometry(g) |
||||||
|
nclient = nclient - 1 |
||||||
|
g.x = g.x+g.width+useless_gap + 2 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return centerfair |
@ -0,0 +1,131 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local awful = require("awful") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local tonumber = tonumber |
||||||
|
local math = { floor = math.floor } |
||||||
|
|
||||||
|
local centerwork = |
||||||
|
{ |
||||||
|
name = "centerwork", |
||||||
|
top_left = 0, |
||||||
|
top_right = 1, |
||||||
|
bottom_left = 2, |
||||||
|
bottom_right = 3 |
||||||
|
} |
||||||
|
|
||||||
|
function centerwork.arrange(p) |
||||||
|
-- A useless gap (like the dwm patch) can be defined with |
||||||
|
-- beautiful.useless_gap_width . |
||||||
|
local useless_gap = tonumber(beautiful.useless_gap_width) or 0 |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- Screen. |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
-- Borders are factored in. |
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
|
||||||
|
-- Width of main column? |
||||||
|
local t = awful.tag.selected(p.screen) |
||||||
|
local mwfact = awful.tag.getmwfact(t) |
||||||
|
|
||||||
|
if #cls > 0 |
||||||
|
then |
||||||
|
-- Main column, fixed width and height. |
||||||
|
local c = cls[#cls] |
||||||
|
local g = {} |
||||||
|
local mainwid = math.floor(wa.width * mwfact) |
||||||
|
local slavewid = wa.width - mainwid |
||||||
|
local slaveLwid = math.floor(slavewid / 2) |
||||||
|
local slaveRwid = slavewid - slaveLwid |
||||||
|
local slaveThei = math.floor(wa.height / 2) |
||||||
|
local slaveBhei = wa.height - slaveThei |
||||||
|
|
||||||
|
g.height = wa.height - 2 * useless_gap |
||||||
|
g.width = mainwid |
||||||
|
g.x = wa.x + slaveLwid + global_border |
||||||
|
g.y = wa.y + useless_gap + global_border |
||||||
|
|
||||||
|
c:geometry(g) |
||||||
|
|
||||||
|
-- Auxiliary windows. |
||||||
|
if #cls > 1 |
||||||
|
then |
||||||
|
local at = 0 |
||||||
|
for i = (#cls - 1),1,-1 |
||||||
|
do |
||||||
|
-- It's all fixed. If there are more than 5 clients, |
||||||
|
-- those additional clients will float. This is |
||||||
|
-- intentional. |
||||||
|
if at == 4 |
||||||
|
then |
||||||
|
break |
||||||
|
end |
||||||
|
|
||||||
|
c = cls[i] |
||||||
|
g = {} |
||||||
|
|
||||||
|
if at == centerwork.top_left |
||||||
|
then |
||||||
|
-- top left |
||||||
|
g.x = wa.x + useless_gap + global_border |
||||||
|
g.y = wa.y + useless_gap + global_border |
||||||
|
g.width = slaveLwid - 2 * useless_gap |
||||||
|
g.height = slaveThei - useless_gap |
||||||
|
elseif at == centerwork.top_right |
||||||
|
then |
||||||
|
-- top right |
||||||
|
g.x = wa.x + slaveLwid + mainwid + useless_gap + global_border |
||||||
|
g.y = wa.y + useless_gap + global_border |
||||||
|
g.width = slaveRwid - 2 * useless_gap |
||||||
|
g.height = slaveThei - useless_gap |
||||||
|
elseif at == centerwork.bottom_left |
||||||
|
then |
||||||
|
-- bottom left |
||||||
|
g.x = wa.x + useless_gap + global_border |
||||||
|
g.y = wa.y + slaveThei + useless_gap + global_border |
||||||
|
g.width = slaveLwid - 2 * useless_gap |
||||||
|
g.height = slaveBhei - 2 * useless_gap |
||||||
|
elseif at == centerwork.bottom_right |
||||||
|
then |
||||||
|
-- bottom right |
||||||
|
g.x = wa.x + slaveLwid + mainwid + useless_gap + global_border |
||||||
|
g.y = wa.y + slaveThei + useless_gap + global_border |
||||||
|
g.width = slaveRwid - 2 * useless_gap |
||||||
|
g.height = slaveBhei - 2 * useless_gap |
||||||
|
end |
||||||
|
|
||||||
|
c:geometry(g) |
||||||
|
|
||||||
|
at = at + 1 |
||||||
|
end |
||||||
|
|
||||||
|
-- Set remaining clients to floating. |
||||||
|
for i = (#cls - 1 - 4),1,-1 |
||||||
|
do |
||||||
|
c = cls[i] |
||||||
|
awful.client.floating.set(c, true) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return centerwork |
@ -0,0 +1,20 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Lain |
||||||
|
Layouts, widgets and utilities for Awesome WM |
||||||
|
|
||||||
|
Layouts section |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local wrequire = require("lain.helpers").wrequire |
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
local layout = { _NAME = "lain.layout" } |
||||||
|
|
||||||
|
return setmetatable(layout, { __index = wrequire }) |
@ -0,0 +1,138 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local tag = require("awful.tag") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local math = { ceil = math.ceil, |
||||||
|
floor = math.floor, |
||||||
|
max = math.max } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local termfair = { name = "termfair" } |
||||||
|
|
||||||
|
function termfair.arrange(p) |
||||||
|
-- Layout with fixed number of vertical columns (read from nmaster). |
||||||
|
-- New windows align from left to right. When a row is full, a now |
||||||
|
-- one above it is created. Like this: |
||||||
|
|
||||||
|
-- (1) (2) (3) |
||||||
|
-- +---+---+---+ +---+---+---+ +---+---+---+ |
||||||
|
-- | | | | | | | | | | | | |
||||||
|
-- | 1 | | | -> | 2 | 1 | | -> | 3 | 2 | 1 | -> |
||||||
|
-- | | | | | | | | | | | | |
||||||
|
-- +---+---+---+ +---+---+---+ +---+---+---+ |
||||||
|
|
||||||
|
-- (4) (5) (6) |
||||||
|
-- +---+---+---+ +---+---+---+ +---+---+---+ |
||||||
|
-- | 4 | | | | 5 | 4 | | | 6 | 5 | 4 | |
||||||
|
-- +---+---+---+ -> +---+---+---+ -> +---+---+---+ |
||||||
|
-- | 3 | 2 | 1 | | 3 | 2 | 1 | | 3 | 2 | 1 | |
||||||
|
-- +---+---+---+ +---+---+---+ +---+---+---+ |
||||||
|
|
||||||
|
-- A useless gap (like the dwm patch) can be defined with |
||||||
|
-- beautiful.useless_gap_width. |
||||||
|
local useless_gap = tonumber(beautiful.useless_gap_width) or 0 |
||||||
|
if useless_gap < 0 then useless_gap = 0 end |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- Screen. |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
-- Borders are factored in. |
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
|
||||||
|
-- How many vertical columns? |
||||||
|
local t = tag.selected(p.screen) |
||||||
|
local num_x = termfair.nmaster or tag.getnmaster(t) |
||||||
|
|
||||||
|
-- Do at least "desired_y" rows. |
||||||
|
local desired_y = termfair.ncol or tag.getncol(t) |
||||||
|
|
||||||
|
if #cls > 0 |
||||||
|
then |
||||||
|
local num_y = math.max(math.ceil(#cls / num_x), desired_y) |
||||||
|
local cur_num_x = num_x |
||||||
|
local at_x = 0 |
||||||
|
local at_y = 0 |
||||||
|
local remaining_clients = #cls |
||||||
|
local width = math.floor(wa.width / num_x) |
||||||
|
local height = math.floor(wa.height / num_y) |
||||||
|
|
||||||
|
-- We start the first row. Left-align by limiting the number of |
||||||
|
-- available slots. |
||||||
|
if remaining_clients < num_x |
||||||
|
then |
||||||
|
cur_num_x = remaining_clients |
||||||
|
end |
||||||
|
|
||||||
|
-- Iterate in reversed order. |
||||||
|
for i = #cls,1,-1 |
||||||
|
do |
||||||
|
-- Get x and y position. |
||||||
|
local c = cls[i] |
||||||
|
local this_x = cur_num_x - at_x - 1 |
||||||
|
local this_y = num_y - at_y - 1 |
||||||
|
|
||||||
|
-- Calc geometry. |
||||||
|
local g = {} |
||||||
|
if this_x == (num_x - 1) |
||||||
|
then |
||||||
|
g.width = wa.width - (num_x - 1) * width - useless_gap |
||||||
|
else |
||||||
|
g.width = width - useless_gap |
||||||
|
end |
||||||
|
if this_y == (num_y - 1) |
||||||
|
then |
||||||
|
g.height = wa.height - (num_y - 1) * height - useless_gap |
||||||
|
else |
||||||
|
g.height = height - useless_gap |
||||||
|
end |
||||||
|
|
||||||
|
g.x = wa.x + this_x * width + global_border |
||||||
|
g.y = wa.y + this_y * height + global_border |
||||||
|
|
||||||
|
if useless_gap > 0 |
||||||
|
then |
||||||
|
-- All clients tile evenly. |
||||||
|
g.x = g.x + (useless_gap / 2) |
||||||
|
g.y = g.y + (useless_gap / 2) |
||||||
|
|
||||||
|
end |
||||||
|
c:geometry(g) |
||||||
|
remaining_clients = remaining_clients - 1 |
||||||
|
|
||||||
|
-- Next grid position. |
||||||
|
at_x = at_x + 1 |
||||||
|
if at_x == num_x |
||||||
|
then |
||||||
|
-- Row full, create a new one above it. |
||||||
|
at_x = 0 |
||||||
|
at_y = at_y + 1 |
||||||
|
|
||||||
|
-- We start a new row. Left-align. |
||||||
|
if remaining_clients < num_x |
||||||
|
then |
||||||
|
cur_num_x = remaining_clients |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return termfair |
@ -0,0 +1,108 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile, worron |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2012, Josh Komoroske |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local beautiful = require("beautiful") |
||||||
|
local ipairs = ipairs |
||||||
|
local math = { ceil = math.ceil, sqrt = math.sqrt, floor = math.floor, max = math.max } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local uselessfair = {} |
||||||
|
|
||||||
|
-- Transformation functions |
||||||
|
local function swap(geometry) |
||||||
|
return { x = geometry.y, y = geometry.x, width = geometry.height, height = geometry.width } |
||||||
|
end |
||||||
|
|
||||||
|
-- Client geometry correction depending on useless gap and window border |
||||||
|
local function size_correction(c, geometry, useless_gap) |
||||||
|
geometry.width = math.max(geometry.width - 2 * c.border_width - useless_gap, 1) |
||||||
|
geometry.height = math.max(geometry.height - 2 * c.border_width - useless_gap, 1) |
||||||
|
geometry.x = geometry.x + useless_gap / 2 |
||||||
|
geometry.y = geometry.y + useless_gap / 2 |
||||||
|
end |
||||||
|
|
||||||
|
-- Main tiling function |
||||||
|
local function fair(p, orientation) |
||||||
|
|
||||||
|
-- Theme vars |
||||||
|
local useless_gap = beautiful.useless_gap_width or 0 |
||||||
|
local global_border = beautiful.global_border_width or 0 |
||||||
|
|
||||||
|
-- Aliases |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
|
||||||
|
-- Nothing to tile here |
||||||
|
if #cls == 0 then return end |
||||||
|
|
||||||
|
-- Workarea size correction depending on useless gap and global border |
||||||
|
wa.height = wa.height - 2 * global_border - useless_gap |
||||||
|
wa.width = wa.width - 2 * global_border - useless_gap |
||||||
|
wa.x = wa.x + useless_gap / 2 + global_border |
||||||
|
wa.y = wa.y + useless_gap / 2 + global_border |
||||||
|
|
||||||
|
-- Geometry calculation |
||||||
|
local row, col = 0, 0 |
||||||
|
|
||||||
|
local rows = math.ceil(math.sqrt(#cls)) |
||||||
|
local cols = math.ceil(#cls / rows) |
||||||
|
|
||||||
|
for i, c in ipairs(cls) do |
||||||
|
local g = {} |
||||||
|
|
||||||
|
-- find tile orientation for current client and swap geometry if need |
||||||
|
local need_swap = (orientation == "east" and #cls <= 2) or (orientation == "south" and #cls > 2) |
||||||
|
local area = need_swap and swap(wa) or wa |
||||||
|
|
||||||
|
-- calculate geometry |
||||||
|
if #cls < (cols * rows) and row == cols - 1 then |
||||||
|
g.width = area.width / (rows - ((cols * rows) - #cls)) |
||||||
|
else |
||||||
|
g.width = area.width / rows |
||||||
|
end |
||||||
|
|
||||||
|
g.height = area.height / cols |
||||||
|
g.x = area.x + col * g.width |
||||||
|
g.y = area.y + row * g.height |
||||||
|
|
||||||
|
-- turn back to real if geometry was swapped |
||||||
|
if need_swap then g = swap(g) end |
||||||
|
|
||||||
|
-- window size correction depending on useless gap and window border |
||||||
|
size_correction(c, g, useless_gap) |
||||||
|
|
||||||
|
-- set geometry |
||||||
|
c:geometry(g) |
||||||
|
|
||||||
|
-- update tile grid coordinates |
||||||
|
col = i % rows |
||||||
|
row = math.floor(i / rows) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Layout constructor |
||||||
|
local function construct_layout(name, direction) |
||||||
|
return { |
||||||
|
name = name, |
||||||
|
-- @p screen The screen number to tile |
||||||
|
arrange = function(p) return fair(p, direction) end |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
-- Build layouts with different tile direction |
||||||
|
uselessfair.vertical = construct_layout("uselessfair", "south") |
||||||
|
uselessfair.horizontal = construct_layout("uselessfairh", "east") |
||||||
|
|
||||||
|
-- Module aliase |
||||||
|
uselessfair.arrange = uselessfair.vertical.arrange |
||||||
|
uselessfair.name = uselessfair.vertical.name |
||||||
|
|
||||||
|
return uselessfair |
@ -0,0 +1,123 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2009, Uli Schlachter |
||||||
|
* (c) 2008, Julien Danjolu |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local beautiful = require("beautiful") |
||||||
|
local ipairs = ipairs |
||||||
|
local tonumber = tonumber |
||||||
|
local math = require("math") |
||||||
|
|
||||||
|
local uselesspiral = {} |
||||||
|
|
||||||
|
local function spiral(p, spiral) |
||||||
|
-- A useless gap (like the dwm patch) can be defined with |
||||||
|
-- beautiful.useless_gap_width. |
||||||
|
local useless_gap = tonumber(beautiful.useless_gap_width) or 0 |
||||||
|
if useless_gap < 0 then useless_gap = 0 end |
||||||
|
|
||||||
|
-- A global border can be defined with |
||||||
|
-- beautiful.global_border_width |
||||||
|
local global_border = tonumber(beautiful.global_border_width) or 0 |
||||||
|
if global_border < 0 then global_border = 0 end |
||||||
|
|
||||||
|
-- Themes border width requires an offset |
||||||
|
local bw = tonumber(beautiful.border_width) or 0 |
||||||
|
|
||||||
|
-- get our orientation right |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
local n = #cls -- number of windows total; k = which window number |
||||||
|
|
||||||
|
wa.height = wa.height - ((global_border * 2) + (bw * 2)) |
||||||
|
wa.width = wa.width - ((global_border * 2) + (bw * 2)) |
||||||
|
|
||||||
|
local static_wa = wa |
||||||
|
|
||||||
|
for k, c in ipairs(cls) do |
||||||
|
if k < n then |
||||||
|
if k % 2 == 0 then |
||||||
|
wa.height = (wa.height / 2) |
||||||
|
else |
||||||
|
wa.width = (wa.width / 2) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if k % 4 == 0 and spiral then |
||||||
|
wa.x = wa.x - wa.width |
||||||
|
elseif k % 2 == 0 or |
||||||
|
(k % 4 == 3 and k < n and spiral) then |
||||||
|
wa.x = wa.x + wa.width |
||||||
|
end |
||||||
|
|
||||||
|
if k % 4 == 1 and k ~= 1 and spiral then |
||||||
|
wa.y = wa.y - wa.height |
||||||
|
elseif k % 2 == 1 and k ~= 1 or |
||||||
|
(k % 4 == 0 and k < n and spiral) then |
||||||
|
wa.y = wa.y + wa.height |
||||||
|
end |
||||||
|
|
||||||
|
local wa2 = {} |
||||||
|
wa2.x = wa.x + (useless_gap / 2) + global_border |
||||||
|
wa2.y = wa.y + (useless_gap / 2) + global_border |
||||||
|
wa2.height = wa.height - (useless_gap / 2) |
||||||
|
wa2.width = wa.width - (useless_gap / 2) |
||||||
|
|
||||||
|
-- Useless gap. |
||||||
|
if useless_gap > 0 |
||||||
|
then |
||||||
|
-- Top and left clients are shrinked by two steps and |
||||||
|
-- get moved away from the border. Other clients just |
||||||
|
-- get shrinked in one direction. |
||||||
|
|
||||||
|
top = false |
||||||
|
left = false |
||||||
|
|
||||||
|
if wa2.y == static_wa.y then |
||||||
|
top = true |
||||||
|
end |
||||||
|
|
||||||
|
if wa2.x == static_wa.x then |
||||||
|
left = true |
||||||
|
end |
||||||
|
|
||||||
|
if top then |
||||||
|
wa2.height = wa2.height - useless_gap |
||||||
|
wa2.y = wa2.y - (useless_gap / 2) |
||||||
|
else |
||||||
|
wa2.height = wa2.height - (useless_gap / 2) |
||||||
|
end |
||||||
|
|
||||||
|
if left then |
||||||
|
wa2.width = wa2.width - useless_gap |
||||||
|
wa2.x = wa2.x - (useless_gap / 2) |
||||||
|
else |
||||||
|
wa2.width = wa2.width - (useless_gap / 2) |
||||||
|
end |
||||||
|
end |
||||||
|
-- End of useless gap. |
||||||
|
|
||||||
|
c:geometry(wa2) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
--- Dwindle layout |
||||||
|
uselesspiral.dwindle = {} |
||||||
|
uselesspiral.dwindle.name = "uselessdwindle" |
||||||
|
function uselesspiral.dwindle.arrange(p) |
||||||
|
return spiral(p, false) |
||||||
|
end |
||||||
|
|
||||||
|
--- Spiral layout |
||||||
|
uselesspiral.name = "uselesspiral" |
||||||
|
function uselesspiral.arrange(p) |
||||||
|
return spiral(p, true) |
||||||
|
end |
||||||
|
|
||||||
|
return uselesspiral |
@ -0,0 +1,226 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, projektile, worron |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2009, Donald Ephraim Curtis |
||||||
|
* (c) 2008, Julien Danjolu |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local tag = require("awful.tag") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local ipairs = ipairs |
||||||
|
local math = { floor = math.floor, |
||||||
|
ceil = math.ceil, |
||||||
|
max = math.max, |
||||||
|
min = math.min } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local uselesstile = {} |
||||||
|
|
||||||
|
-- Transformation functions |
||||||
|
local function flip(canvas, geometry) |
||||||
|
return { |
||||||
|
-- vertical only |
||||||
|
x = 2 * canvas.x + canvas.width - geometry.x - geometry.width, |
||||||
|
y = geometry.y, |
||||||
|
width = geometry.width, |
||||||
|
height = geometry.height |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
local function swap(geometry) |
||||||
|
return { x = geometry.y, y = geometry.x, width = geometry.height, height = geometry.width } |
||||||
|
end |
||||||
|
|
||||||
|
-- Find geometry for secondary windows column |
||||||
|
local function cut_column(wa, n, index) |
||||||
|
local width = wa.width / n |
||||||
|
local area = { x = wa.x + (index - 1) * width, y = wa.y, width = width, height = wa.height } |
||||||
|
|
||||||
|
return area |
||||||
|
end |
||||||
|
|
||||||
|
-- Find geometry for certain window in column |
||||||
|
local function cut_row(wa, factor, index, used) |
||||||
|
local height = wa.height * factor.window[index] / factor.total |
||||||
|
local area = { x = wa.x, y = wa.y + used, width = wa.width, height = height } |
||||||
|
|
||||||
|
return area |
||||||
|
end |
||||||
|
|
||||||
|
-- Client geometry correction depending on useless gap and window border |
||||||
|
local function size_correction(c, geometry, useless_gap) |
||||||
|
geometry.width = math.max(geometry.width - 2 * c.border_width - useless_gap, 1) |
||||||
|
geometry.height = math.max(geometry.height - 2 * c.border_width - useless_gap, 1) |
||||||
|
geometry.x = geometry.x + useless_gap / 2 |
||||||
|
geometry.y = geometry.y + useless_gap / 2 |
||||||
|
end |
||||||
|
|
||||||
|
-- Check size factor for group of clients and calculate total |
||||||
|
local function calc_factor(n, winfactors) |
||||||
|
local factor = { window = winfactors, total = 0, min = 1 } |
||||||
|
|
||||||
|
for i = 1, n do |
||||||
|
if not factor.window[i] then |
||||||
|
factor.window[i] = factor.min |
||||||
|
else |
||||||
|
factor.min = math.min(factor.window[i], factor.min) |
||||||
|
if factor.window[i] < 0.05 then factor.window[i] = 0.05 end |
||||||
|
end |
||||||
|
factor.total = factor.total + factor.window[i] |
||||||
|
end |
||||||
|
|
||||||
|
return factor |
||||||
|
end |
||||||
|
|
||||||
|
-- Tile group of clients in given area |
||||||
|
-- @canvas need for proper transformation only |
||||||
|
-- @winfactors table with clients size factors |
||||||
|
local function tile_column(canvas, area, list, useless_gap, transformation, winfactors) |
||||||
|
local used = 0 |
||||||
|
local factor = calc_factor(#list, winfactors) |
||||||
|
|
||||||
|
for i, c in ipairs(list) do |
||||||
|
local g = cut_row(area, factor, i, used) |
||||||
|
used = used + g.height |
||||||
|
|
||||||
|
-- swap workarea dimensions |
||||||
|
if transformation.flip then g = flip(canvas, g) end |
||||||
|
if transformation.swap then g = swap(g) end |
||||||
|
|
||||||
|
-- useless gap and border correction |
||||||
|
size_correction(c, g, useless_gap) |
||||||
|
|
||||||
|
c:geometry(g) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
--Main tile function |
||||||
|
local function tile(p, orientation) |
||||||
|
|
||||||
|
-- Theme vars |
||||||
|
local useless_gap = beautiful.useless_gap_width or 0 |
||||||
|
local global_border = beautiful.global_border_width or 0 |
||||||
|
|
||||||
|
-- Aliases |
||||||
|
local wa = p.workarea |
||||||
|
local cls = p.clients |
||||||
|
local t = tag.selected(p.screen) |
||||||
|
|
||||||
|
-- Nothing to tile here |
||||||
|
if #cls == 0 then return end |
||||||
|
|
||||||
|
-- Get tag prop |
||||||
|
local nmaster = math.min(tag.getnmaster(t), #cls) |
||||||
|
local mwfact = tag.getmwfact(t) |
||||||
|
|
||||||
|
if nmaster == 0 then |
||||||
|
mwfact = 0 |
||||||
|
elseif nmaster == #cls then |
||||||
|
mwfact = 1 |
||||||
|
end |
||||||
|
|
||||||
|
-- clients size factor |
||||||
|
local data = tag.getdata(t).windowfact |
||||||
|
|
||||||
|
if not data then |
||||||
|
data = {} |
||||||
|
tag.getdata(t).windowfact = data |
||||||
|
end |
||||||
|
|
||||||
|
-- Workarea size correction depending on useless gap and global border |
||||||
|
wa.height = wa.height - 2 * global_border - useless_gap |
||||||
|
wa.width = wa.width - 2 * global_border - useless_gap |
||||||
|
wa.x = wa.x + useless_gap / 2 + global_border |
||||||
|
wa.y = wa.y + useless_gap / 2 + global_border |
||||||
|
|
||||||
|
-- Find which transformation we need for given orientation |
||||||
|
local transformation = { |
||||||
|
swap = orientation == 'top' or orientation == 'bottom', |
||||||
|
flip = orientation == 'left' or orientation == 'top' |
||||||
|
} |
||||||
|
|
||||||
|
-- Swap workarea dimensions if orientation vertical |
||||||
|
if transformation.swap then wa = swap(wa) end |
||||||
|
|
||||||
|
-- Split master and other windows |
||||||
|
local cls_master, cls_other = {}, {} |
||||||
|
|
||||||
|
for i, c in ipairs(cls) do |
||||||
|
if i <= nmaster then |
||||||
|
table.insert(cls_master, c) |
||||||
|
else |
||||||
|
table.insert(cls_other, c) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Tile master windows |
||||||
|
local master_area = { |
||||||
|
x = wa.x, |
||||||
|
y = wa.y, |
||||||
|
width = nmaster > 0 and wa.width * mwfact or 0, |
||||||
|
height = wa.height |
||||||
|
} |
||||||
|
|
||||||
|
if not data[0] then data[0] = {} end |
||||||
|
tile_column(wa, master_area, cls_master, useless_gap, transformation, data[0]) |
||||||
|
|
||||||
|
-- Tile other windows |
||||||
|
local other_area = { |
||||||
|
x = wa.x + master_area.width, |
||||||
|
y = wa.y, |
||||||
|
width = wa.width - master_area.width, |
||||||
|
height = wa.height |
||||||
|
} |
||||||
|
|
||||||
|
-- get column number for other windows |
||||||
|
local ncol = math.min(tag.getncol(t), #cls_other) |
||||||
|
|
||||||
|
if ncol == 0 then ncol = 1 end |
||||||
|
|
||||||
|
-- split other windows to column groups |
||||||
|
local last_small_column = ncol - #cls_other % ncol |
||||||
|
local rows_min = math.floor(#cls_other / ncol) |
||||||
|
|
||||||
|
local client_index = 1 |
||||||
|
for i = 1, ncol do |
||||||
|
local position = transformation.flip and ncol - i + 1 or i |
||||||
|
local rows = i <= last_small_column and rows_min or rows_min + 1 |
||||||
|
local column = {} |
||||||
|
|
||||||
|
for j = 1, rows do |
||||||
|
table.insert(column, cls_other[client_index]) |
||||||
|
client_index = client_index + 1 |
||||||
|
end |
||||||
|
|
||||||
|
-- and tile |
||||||
|
local column_area = cut_column(other_area, ncol, position) |
||||||
|
|
||||||
|
if not data[i] then data[i] = {} end |
||||||
|
tile_column(wa, column_area, column, useless_gap, transformation, data[i]) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Layout constructor |
||||||
|
local function construct_layout(name, orientation) |
||||||
|
return { |
||||||
|
name = name, |
||||||
|
-- @p screen number to tile |
||||||
|
arrange = function(p) return tile(p, orientation) end |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
-- Build layouts with different tile direction |
||||||
|
uselesstile.right = construct_layout("uselesstile", "right") |
||||||
|
uselesstile.left = construct_layout("uselesstileleft", "left") |
||||||
|
uselesstile.bottom = construct_layout("uselesstilebottom", "bottom") |
||||||
|
uselesstile.top = construct_layout("uselesstiletop", "top") |
||||||
|
|
||||||
|
-- Module aliase |
||||||
|
uselesstile.arrange = uselesstile.right.arrange |
||||||
|
uselesstile.name = uselesstile.right.name |
||||||
|
|
||||||
|
return uselesstile |
@ -0,0 +1,387 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# |
||||||
|
# Adapted from Eridan's "fs" (cleanup, enhancements and switch to bash/Linux) |
||||||
|
# JM, 10/12/2004 |
||||||
|
# |
||||||
|
# Integrated into Lain in september 2013 |
||||||
|
# https://github.com/copycat-killer/lain |
||||||
|
|
||||||
|
# Requires gawk |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Decoding options |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
USAGE="Usage: $0 [-h(elp)] | [-n(arrow mode)] | [-w(eb output)]" |
||||||
|
|
||||||
|
NARROW_MODE=0 |
||||||
|
WEB_OUTPUT=0 |
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do |
||||||
|
case "$1" in |
||||||
|
"-h" ) |
||||||
|
echo $USAGE |
||||||
|
exit |
||||||
|
;; |
||||||
|
"-d" ) |
||||||
|
DEBUG=1 |
||||||
|
;; |
||||||
|
"-n" ) |
||||||
|
NARROW_MODE=1 |
||||||
|
;; |
||||||
|
"-w" ) |
||||||
|
WEB_OUTPUT=1 |
||||||
|
;; |
||||||
|
* ) |
||||||
|
echo $USAGE |
||||||
|
exit |
||||||
|
;; |
||||||
|
esac |
||||||
|
shift |
||||||
|
done |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Preparations |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
SYSTEM=`uname -s` |
||||||
|
PATTERN="/" |
||||||
|
|
||||||
|
case "$SYSTEM" in |
||||||
|
"Linux" ) |
||||||
|
DF_COMMAND="/usr/bin/env df -k" |
||||||
|
SORT_COMMAND="/usr/bin/env sort -k6" |
||||||
|
AWK_COMMAND="/usr/bin/env awk" |
||||||
|
;; |
||||||
|
* ) |
||||||
|
DF_COMMAND="/bin/df -k" |
||||||
|
SORT_COMMAND="/usr/bin/sort -k6" |
||||||
|
AWK_COMMAND="/usr/bin/env gawk" |
||||||
|
;; |
||||||
|
esac |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Grabbing "df" result |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
DF_RESULT=`$DF_COMMAND` |
||||||
|
if [ ! -z $DEBUG ]; then |
||||||
|
echo "--> DF_RESULT:" |
||||||
|
echo "$DF_RESULT" |
||||||
|
echo "" |
||||||
|
fi |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Preprocessing "df" result, to join split logical lines |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
PREPROCESSING_RESULT=` \ |
||||||
|
echo "$DF_RESULT" | $AWK_COMMAND -v PATTERN=$PATTERN \ |
||||||
|
' |
||||||
|
NF == 1 { |
||||||
|
printf ("%s", $0) |
||||||
|
} |
||||||
|
|
||||||
|
NF == 5 { |
||||||
|
printf ("%s\n", $0) |
||||||
|
} |
||||||
|
|
||||||
|
NF > 6 { |
||||||
|
} |
||||||
|
|
||||||
|
NF == 6 { |
||||||
|
printf ("%s\n", $0) |
||||||
|
}' |
||||||
|
` |
||||||
|
if [ ! -z $DEBUG ]; then |
||||||
|
echo "--> PREPROCESSING_RESULT:" |
||||||
|
echo "$PREPROCESSING_RESULT" |
||||||
|
echo "" |
||||||
|
fi |
||||||
|
|
||||||
|
SORTED_FILE_SYSTEMS_INFO=`echo "$PREPROCESSING_RESULT" | $SORT_COMMAND` |
||||||
|
|
||||||
|
if [ ! -z $DEBUG ]; then |
||||||
|
echo "--> SORTED_FILE_SYSTEMS_INFO:" |
||||||
|
echo "$SORTED_FILE_SYSTEMS_INFO" |
||||||
|
echo "" |
||||||
|
fi |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Computing mount point max length |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
MOUNT_POINT_MAX_LENGTH=` \ |
||||||
|
echo "$SORTED_FILE_SYSTEMS_INFO" | $AWK_COMMAND -v PATTERN=$PATTERN \ |
||||||
|
' |
||||||
|
BEGIN { |
||||||
|
mount_point_length_max = 15; |
||||||
|
} |
||||||
|
|
||||||
|
END { |
||||||
|
printf ("%d", mount_point_length_max); |
||||||
|
} |
||||||
|
|
||||||
|
$0 ~ PATTERN { |
||||||
|
# printf ("$6 = %s\n", $6); |
||||||
|
|
||||||
|
mount_point = $6; |
||||||
|
# printf ("mount_point = %s\n", mount_point); |
||||||
|
|
||||||
|
mount_point_length = length (mount_point); |
||||||
|
# printf ("mount_point_length = %d\n", mount_point_length); |
||||||
|
|
||||||
|
if (mount_point_length > mount_point_length_max) |
||||||
|
mount_point_length_max = mount_point_length; |
||||||
|
}' |
||||||
|
` |
||||||
|
if [ ! -z $DEBUG ]; then |
||||||
|
echo "MOUNT_POINT_MAX_LENGTH: $MOUNT_POINT_MAX_LENGTH" |
||||||
|
fi |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Computing mount point data max size |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
MOUNT_POINT_MAX_SIZE=` \ |
||||||
|
echo "$SORTED_FILE_SYSTEMS_INFO" | $AWK_COMMAND -v PATTERN=$PATTERN \ |
||||||
|
' |
||||||
|
BEGIN { |
||||||
|
mount_point_size_max = 0; |
||||||
|
} |
||||||
|
|
||||||
|
END { |
||||||
|
printf ("%d", mount_point_size_max); |
||||||
|
} |
||||||
|
|
||||||
|
$0 ~ PATTERN { |
||||||
|
# df -k shows k_bytes! |
||||||
|
# printf ("$2 = %s\n", $2); |
||||||
|
|
||||||
|
mount_point_size = $2 * 1024; |
||||||
|
# printf ("mount_point_size = %d\n", mount_point_size); |
||||||
|
|
||||||
|
if (mount_point_size > mount_point_size_max) |
||||||
|
mount_point_size_max = mount_point_size; |
||||||
|
}' |
||||||
|
` |
||||||
|
if [ ! -z $DEBUG ]; then |
||||||
|
echo "MOUNT_POINT_MAX_SIZE: $MOUNT_POINT_MAX_SIZE" |
||||||
|
fi |
||||||
|
|
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
# Let's go! |
||||||
|
# ------------------------------------------------------------------------- |
||||||
|
echo "$SORTED_FILE_SYSTEMS_INFO" | $AWK_COMMAND -v DEBUG=$DEBUG -v PATTERN=$PATTERN -v NARROW_MODE=$NARROW_MODE -v LEFT_COLUMN=$MOUNT_POINT_MAX_LENGTH -v MAX_SIZE=$MOUNT_POINT_MAX_SIZE -v SCALE=$SCALE -v WEB_OUTPUT=$WEB_OUTPUT \ |
||||||
|
' |
||||||
|
# {printf ("$0 = %s\n", $0);} |
||||||
|
# {printf ("$1 = %s\n", $1);} |
||||||
|
# {printf ("PATTERN = %s\n", PATTERN);} |
||||||
|
# {printf ("LEFT_COLUMN = %s\n", LEFT_COLUMN);} |
||||||
|
|
||||||
|
BEGIN { |
||||||
|
k_bytes = 1024.0; |
||||||
|
m_bytes = 1024.0 * k_bytes; |
||||||
|
g_bytes = 1024.0 * m_bytes; |
||||||
|
t_bytes = 1024.0 * g_bytes; |
||||||
|
|
||||||
|
if (WEB_OUTPUT) |
||||||
|
{ |
||||||
|
all_stars = "**************************************************"; |
||||||
|
current_date = strftime ("%d-%m-%Y @ %H:%M:%S", localtime (systime ())); |
||||||
|
free_threshold = 10; # % |
||||||
|
|
||||||
|
printf ("<!-- DEBUT CONTENU -->\n"); |
||||||
|
|
||||||
|
printf ( \ |
||||||
|
"<A NAME=\"top\"></A>\n" \ |
||||||
|
"<P ALIGN=CENTER><SPAN CLASS=\"titleblue\">%s</SPAN><SPAN CLASS=\"textbold\"> -- STATUS OF <SPAN CLASS=\"titlered\">ALCOR</SPAN> FILE SYSTEMS</SPAN></P><BR>\n", |
||||||
|
current_date ) |
||||||
|
|
||||||
|
printf ("<TABLE WIDTH=\"100%%\" BORDER=1>\n"); |
||||||
|
|
||||||
|
printf ( \ |
||||||
|
"<TR>\n" \ |
||||||
|
"<TD ALIGN=LEFT><STRONG>Mount point</STRONG></TD>\n" \ |
||||||
|
"<TD ALIGN=CENTER><STRONG>%% Usato (<SPAN CLASS=\"titleblue\">*</SPAN>)" \ |
||||||
|
" - %% Free (<SPAN CLASS=\"titlegreen\">*</SPAN>)</STRONG></TD>\n" \ |
||||||
|
"<TD ALIGN=CENTER><STRONG>%% Used</STRONG></TD>\n" \ |
||||||
|
"<TD ALIGN=CENTER><STRONG>Free</STRONG></TD>\n" \ |
||||||
|
"<TD ALIGN=CENTER><STRONG>Total</STRONG></TD>\n" \ |
||||||
|
"</TR>\n" ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
narrow_margin = " "; |
||||||
|
# printf ("%-*s", LEFT_COLUMN + 2, "Mount point"); |
||||||
|
if (NARROW_MODE) |
||||||
|
printf ("\n%s", narrow_margin); |
||||||
|
else |
||||||
|
printf ("%-*s", LEFT_COLUMN + 2, ""); |
||||||
|
print " Used Free Total "; |
||||||
|
if (! NARROW_MODE) |
||||||
|
print ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
END { |
||||||
|
if (WEB_OUTPUT) |
||||||
|
{ |
||||||
|
printf ("</TABLE>\n"); |
||||||
|
|
||||||
|
printf ("<!-- FIN CONTENU -->\n"); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (NARROW_MODE) |
||||||
|
printf ("%s", narrow_margin); |
||||||
|
else |
||||||
|
printf ("%-*s", LEFT_COLUMN + 2, ""); |
||||||
|
print "|----|----|----|----|----|----|----|----|----|----|" |
||||||
|
if (NARROW_MODE) |
||||||
|
printf ("\n%s", narrow_margin); |
||||||
|
else |
||||||
|
printf ("%-*s", LEFT_COLUMN + 2, ""); |
||||||
|
print "0 10 20 30 40 50 60 70 80 90 100"; |
||||||
|
print ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$0 ~ PATTERN { |
||||||
|
|
||||||
|
if (index ($0, "members") == 0 && index ($0, "Download") == 0 && index ($0, "admin") == 0) |
||||||
|
{ |
||||||
|
# df -k shows k_bytes! |
||||||
|
|
||||||
|
total_size = $2 * k_bytes; |
||||||
|
free_size = $4 * k_bytes; |
||||||
|
percentage_occupied = substr($5, 0, 3); |
||||||
|
mount_point = $6; |
||||||
|
|
||||||
|
percentage_free = int (100 - percentage_occupied); |
||||||
|
|
||||||
|
# reduction_factor: 2 |
||||||
|
stars_number = int (percentage_occupied / 2); |
||||||
|
|
||||||
|
if (WEB_OUTPUT) |
||||||
|
{ |
||||||
|
posGroup = index (mount_point, "scratch"); |
||||||
|
if (posGroup == 0) |
||||||
|
posGroup = index (mount_point, "u1"); |
||||||
|
if (posGroup == 0) |
||||||
|
posGroup = index (mount_point, "u2"); |
||||||
|
if (posGroup == 0) |
||||||
|
posGroup = index (mount_point, "u4"); |
||||||
|
if (posGroup == 0) |
||||||
|
posGroup = index (mount_point, "u5"); |
||||||
|
|
||||||
|
printf ("<TR>\n"); |
||||||
|
|
||||||
|
if (posGroup > 0 || percentage_free < free_threshold) |
||||||
|
{ |
||||||
|
if (percentage_free < free_threshold) |
||||||
|
{ |
||||||
|
class = "titlered"; |
||||||
|
if (posGroup == 0) |
||||||
|
posGroup = 1; # to display the whole mount_point in this color anyway |
||||||
|
} |
||||||
|
else if ((index (mount_point, "scratch") != 0) || (index (mount_point, "u1") != 0) || (index (mount_point, "u2") != 0)) |
||||||
|
{ |
||||||
|
class = "titleorange"; |
||||||
|
posGroup = 1; # to display the whole mount_point in this color |
||||||
|
} |
||||||
|
else if ((index (mount_point, "u4") != 0) || (index (mount_point, "u5") != 0)) |
||||||
|
{ |
||||||
|
class = "titlebrown"; |
||||||
|
posGroup = 1; # to display the whole mount_point in this color |
||||||
|
} |
||||||
|
|
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=LEFT>%s<SPAN CLASS=\"%s\">%s</SPAN></TD>\n", |
||||||
|
substr (mount_point, 1, posGroup - 1), |
||||||
|
class, |
||||||
|
substr (mount_point, posGroup) ); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
printf ("<TD ALIGN=LEFT>%s</TD>\n", mount_point); |
||||||
|
} |
||||||
|
|
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=CENTER><SPAN CLASS=\"titleblue\">%s</SPAN><SPAN CLASS=\"titlegreen\">%s</SPAN></TD>\n", |
||||||
|
substr (all_stars, 1, stars_number), substr (all_stars, stars_number + 1, 49) ); |
||||||
|
|
||||||
|
if (percentage_free < free_threshold) |
||||||
|
{ |
||||||
|
color_beginning = "<SPAN CLASS=\"titlered\">"; |
||||||
|
color_end = "</SPAN>" |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
color_beginning = ""; |
||||||
|
color_end = "" |
||||||
|
} |
||||||
|
|
||||||
|
if (total_size > 1 * t_bytes) |
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=RIGHT>%s%3d%%%s</TD><TD ALIGN=RIGHT>%5.1f Tb</TD><TD ALIGN=RIGHT>%5.1f Tb</TD>\n", \ |
||||||
|
color_beginning, percentage_occupied, color_end, free_size / t_bytes, total_size / t_bytes \ |
||||||
|
); |
||||||
|
else if (total_size > 1 * g_bytes) |
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=RIGHT>%s%3d%%%s</TD><TD ALIGN=RIGHT>%5.1f Gb</TD><TD ALIGN=RIGHT>%5.1f Gb</TD>\n", \ |
||||||
|
color_beginning, percentage_occupied, color_end, free_size / g_bytes, total_size / g_bytes \ |
||||||
|
); |
||||||
|
else if (total_size > 1 * m_byptes) |
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=RIGHT>%s%3d%%%s</TD><TD ALIGN=RIGHT>%5.1f Mb</TD><TD ALIGN=RIGHT>%5.1f Mb</TD>\n", \ |
||||||
|
color_beginning, percentage_occupied, color_end, free_size / m_bytes, total_size / m_bytes \ |
||||||
|
); |
||||||
|
else |
||||||
|
printf ( \ |
||||||
|
"<TD ALIGN=RIGHT>%s%3d%%%s</TD><TD ALIGN=RIGHT>%5.1f Kb</TD><TD ALIGN=RIGHT>%5.1f Kb</TD>\n", \ |
||||||
|
color_beginning, percentage_occupied, color_end, free_size / k_bytes, total_size / k_bytes \ |
||||||
|
); |
||||||
|
|
||||||
|
printf ("</TR>\n"); |
||||||
|
} |
||||||
|
|
||||||
|
else |
||||||
|
{ |
||||||
|
# printf ("percentage_occupied = %d\n", percentage_occupied); |
||||||
|
# printf ("percentage_free = %d\n", percentage_free); |
||||||
|
|
||||||
|
printf ("%-*s", LEFT_COLUMN + 2, mount_point); |
||||||
|
if (NARROW_MODE) |
||||||
|
printf ("\n%s", narrow_margin); |
||||||
|
|
||||||
|
# printf ("stars_number = %d\n", stars_number); |
||||||
|
|
||||||
|
printf ("|"); |
||||||
|
for (i = 1; i <= stars_number; i++) |
||||||
|
{ |
||||||
|
printf ("%s", "*"); |
||||||
|
} |
||||||
|
for (i = stars_number + 1; i <= 49; i++) |
||||||
|
{ |
||||||
|
printf ("%s", "-"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
if (total_size > 1 * t_bytes) |
||||||
|
printf ( \ |
||||||
|
"| %3d%% %6.1f %6.1f Tb\n", \ |
||||||
|
percentage_occupied, free_size / t_bytes, total_size / t_bytes \ |
||||||
|
); |
||||||
|
else if (total_size > 1 * g_bytes) |
||||||
|
printf ( \ |
||||||
|
"| %3d%% %6.1f %6.1f Gb\n", \ |
||||||
|
percentage_occupied, free_size / g_bytes, total_size / g_bytes \ |
||||||
|
); |
||||||
|
else if (total_size > 1 * m_byptes) |
||||||
|
printf ( \ |
||||||
|
"| %3d%% %6.1f %6.1f Mb\n", \ |
||||||
|
percentage_occupied, free_size / m_bytes, total_size / m_bytes \ |
||||||
|
); |
||||||
|
else |
||||||
|
printf ( \ |
||||||
|
"| %3d%% %6.1f %6.1f Kb\n", \ |
||||||
|
percentage_occupied, free_size / k_bytes, total_size / k_bytes \ |
||||||
|
); |
||||||
|
} |
||||||
|
} # if |
||||||
|
}' |
@ -0,0 +1,68 @@ |
|||||||
|
#!/bin/bash |
||||||
|
# |
||||||
|
# A simple cover fetcher script for current playing song on mpd. |
||||||
|
# |
||||||
|
# Original author: Wolfgang Mueller |
||||||
|
# |
||||||
|
# Adapted for Lain internal use. |
||||||
|
# https://github.com/copycat-killer/lain |
||||||
|
# |
||||||
|
# You can use, edit and redistribute this script in any way you like. |
||||||
|
# |
||||||
|
# Dependencies: imagemagick. |
||||||
|
# |
||||||
|
# Usage: mpdcover <music_directory> <song_file> <cover_resize> <default_art> |
||||||
|
|
||||||
|
# Configuration------------------------------------------------------- |
||||||
|
|
||||||
|
# Music directory |
||||||
|
MUSIC_DIR=$1 |
||||||
|
|
||||||
|
# Song file |
||||||
|
file=$2 |
||||||
|
|
||||||
|
# Regex expression used for image search |
||||||
|
IMG_REG="(Front|front|Cover|cover|Art|art|Folder|folder)\.(jpg|jpeg|png|gif)$" |
||||||
|
|
||||||
|
# Path of temporary resized cover |
||||||
|
TEMP_PATH="/tmp/mpdcover.png" |
||||||
|
|
||||||
|
# Resize cover |
||||||
|
COVER_RESIZE="$3x$3" |
||||||
|
|
||||||
|
if [ $COVER_RESIZE == "x" ]; then |
||||||
|
COVER_RESIZE="100x100" |
||||||
|
fi |
||||||
|
|
||||||
|
# The default cover to use (optional) |
||||||
|
DEFAULT_ART=$4 |
||||||
|
|
||||||
|
# Thumbnail background (transparent) |
||||||
|
COVER_BACKGROUND="none" |
||||||
|
|
||||||
|
#-------------------------------------------------------------------- |
||||||
|
|
||||||
|
# check if anything is playing at all |
||||||
|
[[ -z $file ]] && exit 1 |
||||||
|
|
||||||
|
# Art directory |
||||||
|
art="$MUSIC_DIR/${file%/*}" |
||||||
|
|
||||||
|
# find every file that matches IMG_REG set the first matching file to be the |
||||||
|
# cover. |
||||||
|
cover="$(find "$art/" -maxdepth 1 -type f | egrep -i -m1 "$IMG_REG")" |
||||||
|
|
||||||
|
# when no cover is found, use DEFAULT_ART as cover |
||||||
|
cover="${cover:=$DEFAULT_ART}" |
||||||
|
|
||||||
|
# check if art is available |
||||||
|
if [[ -n $cover ]]; then |
||||||
|
if [[ -n $COVER_RESIZE ]]; then |
||||||
|
convert "$cover" -scale $COVER_RESIZE -gravity "center" -background "$COVER_BACKGROUND" "$TEMP_PATH" |
||||||
|
cover="$TEMP_PATH" |
||||||
|
fi |
||||||
|
else |
||||||
|
rm $TEMP_PATH |
||||||
|
fi |
||||||
|
|
||||||
|
exit 0 |
@ -0,0 +1,713 @@ |
|||||||
|
-- Module options: |
||||||
|
local always_try_using_lpeg = true |
||||||
|
local register_global_module_table = false |
||||||
|
local global_module_name = 'json' |
||||||
|
|
||||||
|
--[==[ |
||||||
|
|
||||||
|
David Kolf's JSON module for Lua 5.1/5.2 |
||||||
|
|
||||||
|
Version 2.5 |
||||||
|
|
||||||
|
|
||||||
|
For the documentation see the corresponding readme.txt or visit |
||||||
|
<http://dkolf.de/src/dkjson-lua.fsl/>. |
||||||
|
|
||||||
|
You can contact the author by sending an e-mail to 'david' at the |
||||||
|
domain 'dkolf.de'. |
||||||
|
|
||||||
|
|
||||||
|
Copyright (C) 2010-2013 David Heiko Kolf |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining |
||||||
|
a copy of this software and associated documentation files (the |
||||||
|
"Software"), to deal in the Software without restriction, including |
||||||
|
without limitation the rights to use, copy, modify, merge, publish, |
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to |
||||||
|
permit persons to whom the Software is furnished to do so, subject to |
||||||
|
the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be |
||||||
|
included in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
||||||
|
|
||||||
|
--]==] |
||||||
|
|
||||||
|
-- global dependencies: |
||||||
|
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = |
||||||
|
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset |
||||||
|
local error, require, pcall, select = error, require, pcall, select |
||||||
|
local floor, huge = math.floor, math.huge |
||||||
|
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = |
||||||
|
string.rep, string.gsub, string.sub, string.byte, string.char, |
||||||
|
string.find, string.len, string.format |
||||||
|
local strmatch = string.match |
||||||
|
local concat = table.concat |
||||||
|
|
||||||
|
local json = { version = "dkjson 2.5" } |
||||||
|
|
||||||
|
if register_global_module_table then |
||||||
|
_G[global_module_name] = json |
||||||
|
end |
||||||
|
|
||||||
|
local _ENV = nil -- blocking globals in Lua 5.2 |
||||||
|
|
||||||
|
pcall (function() |
||||||
|
-- Enable access to blocked metatables. |
||||||
|
-- Don't worry, this module doesn't change anything in them. |
||||||
|
local debmeta = require "debug".getmetatable |
||||||
|
if debmeta then getmetatable = debmeta end |
||||||
|
end) |
||||||
|
|
||||||
|
json.null = setmetatable ({}, { |
||||||
|
__tojson = function () return "null" end |
||||||
|
}) |
||||||
|
|
||||||
|
local function isarray (tbl) |
||||||
|
local max, n, arraylen = 0, 0, 0 |
||||||
|
for k,v in pairs (tbl) do |
||||||
|
if k == 'n' and type(v) == 'number' then |
||||||
|
arraylen = v |
||||||
|
if v > max then |
||||||
|
max = v |
||||||
|
end |
||||||
|
else |
||||||
|
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then |
||||||
|
return false |
||||||
|
end |
||||||
|
if k > max then |
||||||
|
max = k |
||||||
|
end |
||||||
|
n = n + 1 |
||||||
|
end |
||||||
|
end |
||||||
|
if max > 10 and max > arraylen and max > n * 2 then |
||||||
|
return false -- don't create an array with too many holes |
||||||
|
end |
||||||
|
return true, max |
||||||
|
end |
||||||
|
|
||||||
|
local escapecodes = { |
||||||
|
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", |
||||||
|
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" |
||||||
|
} |
||||||
|
|
||||||
|
local function escapeutf8 (uchar) |
||||||
|
local value = escapecodes[uchar] |
||||||
|
if value then |
||||||
|
return value |
||||||
|
end |
||||||
|
local a, b, c, d = strbyte (uchar, 1, 4) |
||||||
|
a, b, c, d = a or 0, b or 0, c or 0, d or 0 |
||||||
|
if a <= 0x7f then |
||||||
|
value = a |
||||||
|
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then |
||||||
|
value = (a - 0xc0) * 0x40 + b - 0x80 |
||||||
|
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then |
||||||
|
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 |
||||||
|
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then |
||||||
|
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 |
||||||
|
else |
||||||
|
return "" |
||||||
|
end |
||||||
|
if value <= 0xffff then |
||||||
|
return strformat ("\\u%.4x", value) |
||||||
|
elseif value <= 0x10ffff then |
||||||
|
-- encode as UTF-16 surrogate pair |
||||||
|
value = value - 0x10000 |
||||||
|
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) |
||||||
|
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) |
||||||
|
else |
||||||
|
return "" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function fsub (str, pattern, repl) |
||||||
|
-- gsub always builds a new string in a buffer, even when no match |
||||||
|
-- exists. First using find should be more efficient when most strings |
||||||
|
-- don't contain the pattern. |
||||||
|
if strfind (str, pattern) then |
||||||
|
return gsub (str, pattern, repl) |
||||||
|
else |
||||||
|
return str |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function quotestring (value) |
||||||
|
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js |
||||||
|
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) |
||||||
|
if strfind (value, "[\194\216\220\225\226\239]") then |
||||||
|
value = fsub (value, "\194[\128-\159\173]", escapeutf8) |
||||||
|
value = fsub (value, "\216[\128-\132]", escapeutf8) |
||||||
|
value = fsub (value, "\220\143", escapeutf8) |
||||||
|
value = fsub (value, "\225\158[\180\181]", escapeutf8) |
||||||
|
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) |
||||||
|
value = fsub (value, "\226\129[\160-\175]", escapeutf8) |
||||||
|
value = fsub (value, "\239\187\191", escapeutf8) |
||||||
|
value = fsub (value, "\239\191[\176-\191]", escapeutf8) |
||||||
|
end |
||||||
|
return "\"" .. value .. "\"" |
||||||
|
end |
||||||
|
json.quotestring = quotestring |
||||||
|
|
||||||
|
local function replace(str, o, n) |
||||||
|
local i, j = strfind (str, o, 1, true) |
||||||
|
if i then |
||||||
|
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) |
||||||
|
else |
||||||
|
return str |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- locale independent num2str and str2num functions |
||||||
|
local decpoint, numfilter |
||||||
|
|
||||||
|
local function updatedecpoint () |
||||||
|
decpoint = strmatch(tostring(0.5), "([^05+])") |
||||||
|
-- build a filter that can be used to remove group separators |
||||||
|
numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" |
||||||
|
end |
||||||
|
|
||||||
|
updatedecpoint() |
||||||
|
|
||||||
|
local function num2str (num) |
||||||
|
return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") |
||||||
|
end |
||||||
|
|
||||||
|
local function str2num (str) |
||||||
|
local num = tonumber(replace(str, ".", decpoint)) |
||||||
|
if not num then |
||||||
|
updatedecpoint() |
||||||
|
num = tonumber(replace(str, ".", decpoint)) |
||||||
|
end |
||||||
|
return num |
||||||
|
end |
||||||
|
|
||||||
|
local function addnewline2 (level, buffer, buflen) |
||||||
|
buffer[buflen+1] = "\n" |
||||||
|
buffer[buflen+2] = strrep (" ", level) |
||||||
|
buflen = buflen + 2 |
||||||
|
return buflen |
||||||
|
end |
||||||
|
|
||||||
|
function json.addnewline (state) |
||||||
|
if state.indent then |
||||||
|
state.bufferlen = addnewline2 (state.level or 0, |
||||||
|
state.buffer, state.bufferlen or #(state.buffer)) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local encode2 -- forward declaration |
||||||
|
|
||||||
|
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
local kt = type (key) |
||||||
|
if kt ~= 'string' and kt ~= 'number' then |
||||||
|
return nil, "type '" .. kt .. "' is not supported as a key by JSON." |
||||||
|
end |
||||||
|
if prev then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "," |
||||||
|
end |
||||||
|
if indent then |
||||||
|
buflen = addnewline2 (level, buffer, buflen) |
||||||
|
end |
||||||
|
buffer[buflen+1] = quotestring (key) |
||||||
|
buffer[buflen+2] = ":" |
||||||
|
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) |
||||||
|
end |
||||||
|
|
||||||
|
local function appendcustom(res, buffer, state) |
||||||
|
local buflen = state.bufferlen |
||||||
|
if type (res) == 'string' then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = res |
||||||
|
end |
||||||
|
return buflen |
||||||
|
end |
||||||
|
|
||||||
|
local function exception(reason, value, state, buffer, buflen, defaultmessage) |
||||||
|
defaultmessage = defaultmessage or reason |
||||||
|
local handler = state.exception |
||||||
|
if not handler then |
||||||
|
return nil, defaultmessage |
||||||
|
else |
||||||
|
state.bufferlen = buflen |
||||||
|
local ret, msg = handler (reason, value, state, defaultmessage) |
||||||
|
if not ret then return nil, msg or defaultmessage end |
||||||
|
return appendcustom(ret, buffer, state) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function json.encodeexception(reason, value, state, defaultmessage) |
||||||
|
return quotestring("<" .. defaultmessage .. ">") |
||||||
|
end |
||||||
|
|
||||||
|
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
local valtype = type (value) |
||||||
|
local valmeta = getmetatable (value) |
||||||
|
valmeta = type (valmeta) == 'table' and valmeta -- only tables |
||||||
|
local valtojson = valmeta and valmeta.__tojson |
||||||
|
if valtojson then |
||||||
|
if tables[value] then |
||||||
|
return exception('reference cycle', value, state, buffer, buflen) |
||||||
|
end |
||||||
|
tables[value] = true |
||||||
|
state.bufferlen = buflen |
||||||
|
local ret, msg = valtojson (value, state) |
||||||
|
if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end |
||||||
|
tables[value] = nil |
||||||
|
buflen = appendcustom(ret, buffer, state) |
||||||
|
elseif value == nil then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "null" |
||||||
|
elseif valtype == 'number' then |
||||||
|
local s |
||||||
|
if value ~= value or value >= huge or -value >= huge then |
||||||
|
-- This is the behaviour of the original JSON implementation. |
||||||
|
s = "null" |
||||||
|
else |
||||||
|
s = num2str (value) |
||||||
|
end |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = s |
||||||
|
elseif valtype == 'boolean' then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = value and "true" or "false" |
||||||
|
elseif valtype == 'string' then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = quotestring (value) |
||||||
|
elseif valtype == 'table' then |
||||||
|
if tables[value] then |
||||||
|
return exception('reference cycle', value, state, buffer, buflen) |
||||||
|
end |
||||||
|
tables[value] = true |
||||||
|
level = level + 1 |
||||||
|
local isa, n = isarray (value) |
||||||
|
if n == 0 and valmeta and valmeta.__jsontype == 'object' then |
||||||
|
isa = false |
||||||
|
end |
||||||
|
local msg |
||||||
|
if isa then -- JSON array |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "[" |
||||||
|
for i = 1, n do |
||||||
|
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
if not buflen then return nil, msg end |
||||||
|
if i < n then |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "," |
||||||
|
end |
||||||
|
end |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "]" |
||||||
|
else -- JSON object |
||||||
|
local prev = false |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "{" |
||||||
|
local order = valmeta and valmeta.__jsonorder or globalorder |
||||||
|
if order then |
||||||
|
local used = {} |
||||||
|
n = #order |
||||||
|
for i = 1, n do |
||||||
|
local k = order[i] |
||||||
|
local v = value[k] |
||||||
|
if v then |
||||||
|
used[k] = true |
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
prev = true -- add a seperator before the next element |
||||||
|
end |
||||||
|
end |
||||||
|
for k,v in pairs (value) do |
||||||
|
if not used[k] then |
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
if not buflen then return nil, msg end |
||||||
|
prev = true -- add a seperator before the next element |
||||||
|
end |
||||||
|
end |
||||||
|
else -- unordered |
||||||
|
for k,v in pairs (value) do |
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) |
||||||
|
if not buflen then return nil, msg end |
||||||
|
prev = true -- add a seperator before the next element |
||||||
|
end |
||||||
|
end |
||||||
|
if indent then |
||||||
|
buflen = addnewline2 (level - 1, buffer, buflen) |
||||||
|
end |
||||||
|
buflen = buflen + 1 |
||||||
|
buffer[buflen] = "}" |
||||||
|
end |
||||||
|
tables[value] = nil |
||||||
|
else |
||||||
|
return exception ('unsupported type', value, state, buffer, buflen, |
||||||
|
"type '" .. valtype .. "' is not supported by JSON.") |
||||||
|
end |
||||||
|
return buflen |
||||||
|
end |
||||||
|
|
||||||
|
function json.encode (value, state) |
||||||
|
state = state or {} |
||||||
|
local oldbuffer = state.buffer |
||||||
|
local buffer = oldbuffer or {} |
||||||
|
state.buffer = buffer |
||||||
|
updatedecpoint() |
||||||
|
local ret, msg = encode2 (value, state.indent, state.level or 0, |
||||||
|
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) |
||||||
|
if not ret then |
||||||
|
error (msg, 2) |
||||||
|
elseif oldbuffer == buffer then |
||||||
|
state.bufferlen = ret |
||||||
|
return true |
||||||
|
else |
||||||
|
state.bufferlen = nil |
||||||
|
state.buffer = nil |
||||||
|
return concat (buffer) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function loc (str, where) |
||||||
|
local line, pos, linepos = 1, 1, 0 |
||||||
|
while true do |
||||||
|
pos = strfind (str, "\n", pos, true) |
||||||
|
if pos and pos < where then |
||||||
|
line = line + 1 |
||||||
|
linepos = pos |
||||||
|
pos = pos + 1 |
||||||
|
else |
||||||
|
break |
||||||
|
end |
||||||
|
end |
||||||
|
return "line " .. line .. ", column " .. (where - linepos) |
||||||
|
end |
||||||
|
|
||||||
|
local function unterminated (str, what, where) |
||||||
|
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) |
||||||
|
end |
||||||
|
|
||||||
|
local function scanwhite (str, pos) |
||||||
|
while true do |
||||||
|
pos = strfind (str, "%S", pos) |
||||||
|
if not pos then return nil end |
||||||
|
local sub2 = strsub (str, pos, pos + 1) |
||||||
|
if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then |
||||||
|
-- UTF-8 Byte Order Mark |
||||||
|
pos = pos + 3 |
||||||
|
elseif sub2 == "//" then |
||||||
|
pos = strfind (str, "[\n\r]", pos + 2) |
||||||
|
if not pos then return nil end |
||||||
|
elseif sub2 == "/*" then |
||||||
|
pos = strfind (str, "*/", pos + 2) |
||||||
|
if not pos then return nil end |
||||||
|
pos = pos + 2 |
||||||
|
else |
||||||
|
return pos |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local escapechars = { |
||||||
|
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", |
||||||
|
["n"] = "\n", ["r"] = "\r", ["t"] = "\t" |
||||||
|
} |
||||||
|
|
||||||
|
local function unichar (value) |
||||||
|
if value < 0 then |
||||||
|
return nil |
||||||
|
elseif value <= 0x007f then |
||||||
|
return strchar (value) |
||||||
|
elseif value <= 0x07ff then |
||||||
|
return strchar (0xc0 + floor(value/0x40), |
||||||
|
0x80 + (floor(value) % 0x40)) |
||||||
|
elseif value <= 0xffff then |
||||||
|
return strchar (0xe0 + floor(value/0x1000), |
||||||
|
0x80 + (floor(value/0x40) % 0x40), |
||||||
|
0x80 + (floor(value) % 0x40)) |
||||||
|
elseif value <= 0x10ffff then |
||||||
|
return strchar (0xf0 + floor(value/0x40000), |
||||||
|
0x80 + (floor(value/0x1000) % 0x40), |
||||||
|
0x80 + (floor(value/0x40) % 0x40), |
||||||
|
0x80 + (floor(value) % 0x40)) |
||||||
|
else |
||||||
|
return nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function scanstring (str, pos) |
||||||
|
local lastpos = pos + 1 |
||||||
|
local buffer, n = {}, 0 |
||||||
|
while true do |
||||||
|
local nextpos = strfind (str, "[\"\\]", lastpos) |
||||||
|
if not nextpos then |
||||||
|
return unterminated (str, "string", pos) |
||||||
|
end |
||||||
|
if nextpos > lastpos then |
||||||
|
n = n + 1 |
||||||
|
buffer[n] = strsub (str, lastpos, nextpos - 1) |
||||||
|
end |
||||||
|
if strsub (str, nextpos, nextpos) == "\"" then |
||||||
|
lastpos = nextpos + 1 |
||||||
|
break |
||||||
|
else |
||||||
|
local escchar = strsub (str, nextpos + 1, nextpos + 1) |
||||||
|
local value |
||||||
|
if escchar == "u" then |
||||||
|
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) |
||||||
|
if value then |
||||||
|
local value2 |
||||||
|
if 0xD800 <= value and value <= 0xDBff then |
||||||
|
-- we have the high surrogate of UTF-16. Check if there is a |
||||||
|
-- low surrogate escaped nearby to combine them. |
||||||
|
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then |
||||||
|
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) |
||||||
|
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then |
||||||
|
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 |
||||||
|
else |
||||||
|
value2 = nil -- in case it was out of range for a low surrogate |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
value = value and unichar (value) |
||||||
|
if value then |
||||||
|
if value2 then |
||||||
|
lastpos = nextpos + 12 |
||||||
|
else |
||||||
|
lastpos = nextpos + 6 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
if not value then |
||||||
|
value = escapechars[escchar] or escchar |
||||||
|
lastpos = nextpos + 2 |
||||||
|
end |
||||||
|
n = n + 1 |
||||||
|
buffer[n] = value |
||||||
|
end |
||||||
|
end |
||||||
|
if n == 1 then |
||||||
|
return buffer[1], lastpos |
||||||
|
elseif n > 1 then |
||||||
|
return concat (buffer), lastpos |
||||||
|
else |
||||||
|
return "", lastpos |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local scanvalue -- forward declaration |
||||||
|
|
||||||
|
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) |
||||||
|
local len = strlen (str) |
||||||
|
local tbl, n = {}, 0 |
||||||
|
local pos = startpos + 1 |
||||||
|
if what == 'object' then |
||||||
|
setmetatable (tbl, objectmeta) |
||||||
|
else |
||||||
|
setmetatable (tbl, arraymeta) |
||||||
|
end |
||||||
|
while true do |
||||||
|
pos = scanwhite (str, pos) |
||||||
|
if not pos then return unterminated (str, what, startpos) end |
||||||
|
local char = strsub (str, pos, pos) |
||||||
|
if char == closechar then |
||||||
|
return tbl, pos + 1 |
||||||
|
end |
||||||
|
local val1, err |
||||||
|
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) |
||||||
|
if err then return nil, pos, err end |
||||||
|
pos = scanwhite (str, pos) |
||||||
|
if not pos then return unterminated (str, what, startpos) end |
||||||
|
char = strsub (str, pos, pos) |
||||||
|
if char == ":" then |
||||||
|
if val1 == nil then |
||||||
|
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" |
||||||
|
end |
||||||
|
pos = scanwhite (str, pos + 1) |
||||||
|
if not pos then return unterminated (str, what, startpos) end |
||||||
|
local val2 |
||||||
|
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) |
||||||
|
if err then return nil, pos, err end |
||||||
|
tbl[val1] = val2 |
||||||
|
pos = scanwhite (str, pos) |
||||||
|
if not pos then return unterminated (str, what, startpos) end |
||||||
|
char = strsub (str, pos, pos) |
||||||
|
else |
||||||
|
n = n + 1 |
||||||
|
tbl[n] = val1 |
||||||
|
end |
||||||
|
if char == "," then |
||||||
|
pos = pos + 1 |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
scanvalue = function (str, pos, nullval, objectmeta, arraymeta) |
||||||
|
pos = pos or 1 |
||||||
|
pos = scanwhite (str, pos) |
||||||
|
if not pos then |
||||||
|
return nil, strlen (str) + 1, "no valid JSON value (reached the end)" |
||||||
|
end |
||||||
|
local char = strsub (str, pos, pos) |
||||||
|
if char == "{" then |
||||||
|
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) |
||||||
|
elseif char == "[" then |
||||||
|
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) |
||||||
|
elseif char == "\"" then |
||||||
|
return scanstring (str, pos) |
||||||
|
else |
||||||
|
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) |
||||||
|
if pstart then |
||||||
|
local number = str2num (strsub (str, pstart, pend)) |
||||||
|
if number then |
||||||
|
return number, pend + 1 |
||||||
|
end |
||||||
|
end |
||||||
|
pstart, pend = strfind (str, "^%a%w*", pos) |
||||||
|
if pstart then |
||||||
|
local name = strsub (str, pstart, pend) |
||||||
|
if name == "true" then |
||||||
|
return true, pend + 1 |
||||||
|
elseif name == "false" then |
||||||
|
return false, pend + 1 |
||||||
|
elseif name == "null" then |
||||||
|
return nullval, pend + 1 |
||||||
|
end |
||||||
|
end |
||||||
|
return nil, pos, "no valid JSON value at " .. loc (str, pos) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function optionalmetatables(...) |
||||||
|
if select("#", ...) > 0 then |
||||||
|
return ... |
||||||
|
else |
||||||
|
return {__jsontype = 'object'}, {__jsontype = 'array'} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function json.decode (str, pos, nullval, ...) |
||||||
|
local objectmeta, arraymeta = optionalmetatables(...) |
||||||
|
return scanvalue (str, pos, nullval, objectmeta, arraymeta) |
||||||
|
end |
||||||
|
|
||||||
|
function json.use_lpeg () |
||||||
|
local g = require ("lpeg") |
||||||
|
|
||||||
|
if g.version() == "0.11" then |
||||||
|
error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" |
||||||
|
end |
||||||
|
|
||||||
|
local pegmatch = g.match |
||||||
|
local P, S, R = g.P, g.S, g.R |
||||||
|
|
||||||
|
local function ErrorCall (str, pos, msg, state) |
||||||
|
if not state.msg then |
||||||
|
state.msg = msg .. " at " .. loc (str, pos) |
||||||
|
state.pos = pos |
||||||
|
end |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
local function Err (msg) |
||||||
|
return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) |
||||||
|
end |
||||||
|
|
||||||
|
local SingleLineComment = P"//" * (1 - S"\n\r")^0 |
||||||
|
local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" |
||||||
|
local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 |
||||||
|
|
||||||
|
local PlainChar = 1 - S"\"\\\n\r" |
||||||
|
local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars |
||||||
|
local HexDigit = R("09", "af", "AF") |
||||||
|
local function UTF16Surrogate (match, pos, high, low) |
||||||
|
high, low = tonumber (high, 16), tonumber (low, 16) |
||||||
|
if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then |
||||||
|
return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) |
||||||
|
else |
||||||
|
return false |
||||||
|
end |
||||||
|
end |
||||||
|
local function UTF16BMP (hex) |
||||||
|
return unichar (tonumber (hex, 16)) |
||||||
|
end |
||||||
|
local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) |
||||||
|
local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP |
||||||
|
local Char = UnicodeEscape + EscapeSequence + PlainChar |
||||||
|
local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") |
||||||
|
local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) |
||||||
|
local Fractal = P"." * R"09"^0 |
||||||
|
local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 |
||||||
|
local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num |
||||||
|
local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) |
||||||
|
local SimpleValue = Number + String + Constant |
||||||
|
local ArrayContent, ObjectContent |
||||||
|
|
||||||
|
-- The functions parsearray and parseobject parse only a single value/pair |
||||||
|
-- at a time and store them directly to avoid hitting the LPeg limits. |
||||||
|
local function parsearray (str, pos, nullval, state) |
||||||
|
local obj, cont |
||||||
|
local npos |
||||||
|
local t, nt = {}, 0 |
||||||
|
repeat |
||||||
|
obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) |
||||||
|
if not npos then break end |
||||||
|
pos = npos |
||||||
|
nt = nt + 1 |
||||||
|
t[nt] = obj |
||||||
|
until cont == 'last' |
||||||
|
return pos, setmetatable (t, state.arraymeta) |
||||||
|
end |
||||||
|
|
||||||
|
local function parseobject (str, pos, nullval, state) |
||||||
|
local obj, key, cont |
||||||
|
local npos |
||||||
|
local t = {} |
||||||
|
repeat |
||||||
|
key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) |
||||||
|
if not npos then break end |
||||||
|
pos = npos |
||||||
|
t[key] = obj |
||||||
|
until cont == 'last' |
||||||
|
return pos, setmetatable (t, state.objectmeta) |
||||||
|
end |
||||||
|
|
||||||
|
local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") |
||||||
|
local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") |
||||||
|
local Value = Space * (Array + Object + SimpleValue) |
||||||
|
local ExpectedValue = Value + Space * Err "value expected" |
||||||
|
ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() |
||||||
|
local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) |
||||||
|
ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() |
||||||
|
local DecodeValue = ExpectedValue * g.Cp () |
||||||
|
|
||||||
|
function json.decode (str, pos, nullval, ...) |
||||||
|
local state = {} |
||||||
|
state.objectmeta, state.arraymeta = optionalmetatables(...) |
||||||
|
local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) |
||||||
|
if state.msg then |
||||||
|
return nil, state.pos, state.msg |
||||||
|
else |
||||||
|
return obj, retpos |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- use this function only once: |
||||||
|
json.use_lpeg = function () return json end |
||||||
|
|
||||||
|
json.using_lpeg = true |
||||||
|
|
||||||
|
return json -- so you can get the module using json = require "dkjson".use_lpeg() |
||||||
|
end |
||||||
|
|
||||||
|
if always_try_using_lpeg then |
||||||
|
pcall (json.use_lpeg) |
||||||
|
end |
||||||
|
|
||||||
|
return json |
@ -0,0 +1,229 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Lain |
||||||
|
Layouts, widgets and utilities for Awesome WM |
||||||
|
|
||||||
|
Utilities section |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local awful = require("awful") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local math = { sqrt = math.sqrt } |
||||||
|
local mouse = mouse |
||||||
|
local pairs = pairs |
||||||
|
local string = { gsub = string.gsub } |
||||||
|
local client = client |
||||||
|
local screen = screen |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local wrequire = require("lain.helpers").wrequire |
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Lain utilities submodule |
||||||
|
-- lain.util |
||||||
|
local util = { _NAME = "lain.util" } |
||||||
|
|
||||||
|
-- Like awful.menu.clients, but only show clients of currently selected |
||||||
|
-- tags. |
||||||
|
function util.menu_clients_current_tags(menu, args) |
||||||
|
-- List of currently selected tags. |
||||||
|
local cls_tags = awful.tag.selectedlist(mouse.screen) |
||||||
|
|
||||||
|
-- Final list of menu items. |
||||||
|
local cls_t = {} |
||||||
|
|
||||||
|
if cls_tags == nil then return nil end |
||||||
|
|
||||||
|
-- For each selected tag get all clients of that tag and add them to |
||||||
|
-- the menu. A click on a menu item will raise that client. |
||||||
|
for i = 1,#cls_tags |
||||||
|
do |
||||||
|
local t = cls_tags[i] |
||||||
|
local cls = t:clients() |
||||||
|
|
||||||
|
for k, c in pairs(cls) |
||||||
|
do |
||||||
|
cls_t[#cls_t + 1] = { awful.util.escape(c.name) or "", |
||||||
|
function () |
||||||
|
c.minimized = false |
||||||
|
client.focus = c |
||||||
|
c:raise() |
||||||
|
end, |
||||||
|
c.icon } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- No clients? Then quit. |
||||||
|
if #cls_t <= 0 then return nil end |
||||||
|
|
||||||
|
-- menu may contain some predefined values, otherwise start with a |
||||||
|
-- fresh menu. |
||||||
|
if not menu then menu = {} end |
||||||
|
|
||||||
|
-- Set the list of items and show the menu. |
||||||
|
menu.items = cls_t |
||||||
|
local m = awful.menu.new(menu) |
||||||
|
m:show(args) |
||||||
|
return m |
||||||
|
end |
||||||
|
|
||||||
|
-- Magnify a client: Set it to "float" and resize it. |
||||||
|
function util.magnify_client(c) |
||||||
|
if not awful.client.floating.get(c) then |
||||||
|
awful.client.floating.set(c, true) |
||||||
|
|
||||||
|
local mg = screen[mouse.screen].geometry |
||||||
|
local tag = awful.tag.selected(mouse.screen) |
||||||
|
local mwfact = awful.tag.getmwfact(tag) |
||||||
|
local g = {} |
||||||
|
g.width = math.sqrt(mwfact) * mg.width |
||||||
|
g.height = math.sqrt(mwfact) * mg.height |
||||||
|
g.x = mg.x + (mg.width - g.width) / 2 |
||||||
|
g.y = mg.y + (mg.height - g.height) / 2 |
||||||
|
c:geometry(g) |
||||||
|
else |
||||||
|
awful.client.floating.set(c, false) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Read the nice value of pid from /proc. |
||||||
|
local function get_nice_value(pid) |
||||||
|
local n = first_line('/proc/' .. pid .. '/stat') |
||||||
|
if n == nil |
||||||
|
then |
||||||
|
-- This should not happen. But I don't want to crash, either. |
||||||
|
return 0 |
||||||
|
end |
||||||
|
|
||||||
|
-- Remove pid and tcomm. This is necessary because tcomm may contain |
||||||
|
-- nasty stuff such as whitespace or additional parentheses... |
||||||
|
n = string.gsub(n, '.*%) ', '') |
||||||
|
|
||||||
|
-- Field number 17 now is the nice value. |
||||||
|
fields = split(n, ' ') |
||||||
|
return tonumber(fields[17]) |
||||||
|
end |
||||||
|
|
||||||
|
-- To be used as a signal handler for "focus" |
||||||
|
-- This requires beautiful.border_focus{,_highprio,_lowprio}. |
||||||
|
function util.niceborder_focus(c) |
||||||
|
local n = get_nice_value(c.pid) |
||||||
|
if n == 0 |
||||||
|
then |
||||||
|
c.border_color = beautiful.border_focus |
||||||
|
elseif n < 0 |
||||||
|
then |
||||||
|
c.border_color = beautiful.border_focus_highprio |
||||||
|
else |
||||||
|
c.border_color = beautiful.border_focus_lowprio |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- To be used as a signal handler for "unfocus" |
||||||
|
-- This requires beautiful.border_normal{,_highprio,_lowprio}. |
||||||
|
function util.niceborder_unfocus(c) |
||||||
|
local n = get_nice_value(c.pid) |
||||||
|
if n == 0 |
||||||
|
then |
||||||
|
c.border_color = beautiful.border_normal |
||||||
|
elseif n < 0 |
||||||
|
then |
||||||
|
c.border_color = beautiful.border_normal_highprio |
||||||
|
else |
||||||
|
c.border_color = beautiful.border_normal_lowprio |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Non-empty tag browsing |
||||||
|
-- direction in {-1, 1} <-> {previous, next} non-empty tag |
||||||
|
function util.tag_view_nonempty(direction, sc) |
||||||
|
local s = sc or mouse.screen or 1 |
||||||
|
local scr = screen[s] |
||||||
|
|
||||||
|
for i = 1, #awful.tag.gettags(s) do |
||||||
|
awful.tag.viewidx(direction,s) |
||||||
|
if #awful.client.visible(s) > 0 then |
||||||
|
return |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- {{{ Dynamic tagging |
||||||
|
-- |
||||||
|
-- Add a new tag |
||||||
|
function util.add_tag(mypromptbox) |
||||||
|
awful.prompt.run({prompt="New tag name: "}, mypromptbox[mouse.screen].widget, |
||||||
|
function(text) |
||||||
|
if text:len() > 0 then |
||||||
|
props = { selected = true } |
||||||
|
tag = awful.tag.add(new_name, props) |
||||||
|
tag.name = text |
||||||
|
tag:emit_signal("property::name") |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
-- Rename current tag |
||||||
|
-- @author: minism |
||||||
|
function util.rename_tag(mypromptbox) |
||||||
|
local tag = awful.tag.selected(mouse.screen) |
||||||
|
awful.prompt.run({prompt="Rename tag: "}, mypromptbox[mouse.screen].widget, |
||||||
|
function(text) |
||||||
|
if text:len() > 0 then |
||||||
|
tag.name = text |
||||||
|
tag:emit_signal("property::name") |
||||||
|
end |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
-- Move current tag |
||||||
|
-- pos in {-1, 1} <-> {previous, next} tag position |
||||||
|
function util.move_tag(pos) |
||||||
|
local tag = awful.tag.selected(mouse.screen) |
||||||
|
local idx = awful.tag.getidx(tag) |
||||||
|
if tonumber(pos) <= -1 then |
||||||
|
awful.tag.move(idx - 1, tag) |
||||||
|
else |
||||||
|
awful.tag.move(idx + 1, tag) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
-- Remove current tag (if empty) |
||||||
|
-- Any rule set on the tag shall be broken |
||||||
|
function util.remove_tag() |
||||||
|
local tag = awful.tag.selected(mouse.screen) |
||||||
|
local prevtag = awful.tag.gettags(mouse.screen)[awful.tag.getidx(tag) - 1] |
||||||
|
awful.tag.delete(tag, prevtag) |
||||||
|
end |
||||||
|
-- |
||||||
|
-- }}} |
||||||
|
|
||||||
|
-- On the fly useless gaps change |
||||||
|
function util.useless_gaps_resize(thatmuch) |
||||||
|
beautiful.useless_gap_width = tonumber(beautiful.useless_gap_width) + thatmuch |
||||||
|
awful.layout.arrange(mouse.screen) |
||||||
|
end |
||||||
|
|
||||||
|
-- On the fly global border change |
||||||
|
function util.global_border_resize(thatmuch) |
||||||
|
beautiful.global_border_width = tonumber(beautiful.global_border_width) + thatmuch |
||||||
|
awful.layout.arrange(mouse.screen) |
||||||
|
end |
||||||
|
|
||||||
|
-- Check if an element exist on a table |
||||||
|
function util.element_in_table(element, tbl) |
||||||
|
for _, i in pairs(tbl) do |
||||||
|
if i == element then |
||||||
|
return true |
||||||
|
end |
||||||
|
end |
||||||
|
return false |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(util, { __index = wrequire }) |
@ -0,0 +1,69 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under MIT License |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2009, Uli Schlachter |
||||||
|
* (c) 2009, Majic |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local beautiful = require("beautiful") |
||||||
|
local tostring = tostring |
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Lain markup util submodule |
||||||
|
-- lain.util.markup |
||||||
|
local markup = {} |
||||||
|
|
||||||
|
local fg = {} |
||||||
|
local bg = {} |
||||||
|
|
||||||
|
-- Convenience tags. |
||||||
|
function markup.bold(text) return '<b>' .. tostring(text) .. '</b>' end |
||||||
|
function markup.italic(text) return '<i>' .. tostring(text) .. '</i>' end |
||||||
|
function markup.strike(text) return '<s>' .. tostring(text) .. '</s>' end |
||||||
|
function markup.underline(text) return '<u>' .. tostring(text) .. '</u>' end |
||||||
|
function markup.monospace(text) return '<tt>' .. tostring(text) .. '</tt>' end |
||||||
|
function markup.big(text) return '<big>' .. tostring(text) .. '</big>' end |
||||||
|
function markup.small(text) return '<small>' .. tostring(text) .. '</small>' end |
||||||
|
|
||||||
|
-- Set the font. |
||||||
|
function markup.font(font, text) |
||||||
|
return '<span font="' .. tostring(font) .. '">' .. tostring(text) ..'</span>' |
||||||
|
end |
||||||
|
|
||||||
|
-- Set the foreground. |
||||||
|
function fg.color(color, text) |
||||||
|
return '<span foreground="' .. tostring(color) .. '">' .. tostring(text) .. '</span>' |
||||||
|
end |
||||||
|
|
||||||
|
-- Set the background. |
||||||
|
function bg.color(color, text) |
||||||
|
return '<span background="' .. tostring(color) .. '">' .. tostring(text) .. '</span>' |
||||||
|
end |
||||||
|
|
||||||
|
-- Context: focus |
||||||
|
function fg.focus(text) return fg.color(beautiful.fg_focus, text) end |
||||||
|
function bg.focus(text) return bg.color(beautiful.bg_focus, text) end |
||||||
|
function markup.focus(text) return bg.focus(fg.focus(text)) end |
||||||
|
|
||||||
|
-- Context: normal |
||||||
|
function fg.normal(text) return fg.color(beautiful.fg_normal, text) end |
||||||
|
function bg.normal(text) return bg.color(beautiful.bg_normal, text) end |
||||||
|
function markup.normal(text) return bg.normal(fg.normal(text)) end |
||||||
|
|
||||||
|
-- Context: urgent |
||||||
|
function fg.urgent(text) return fg.color(beautiful.fg_urgent, text) end |
||||||
|
function bg.urgent(text) return bg.color(beautiful.bg_urgent, text) end |
||||||
|
function markup.urgent(text) return bg.urgent(fg.urgent(text)) end |
||||||
|
|
||||||
|
markup.fg = fg |
||||||
|
markup.bg = bg |
||||||
|
|
||||||
|
-- link markup.{fg,bg}(...) calls to markup.{fg,bg}.color(...) |
||||||
|
setmetatable(markup.fg, { __call = function(_, ...) return markup.fg.color(...) end }) |
||||||
|
setmetatable(markup.bg, { __call = function(_, ...) return markup.bg.color(...) end }) |
||||||
|
|
||||||
|
-- link markup(...) calls to markup.fg.color(...) |
||||||
|
return setmetatable(markup, { __call = function(_, ...) return markup.fg.color(...) end }) |
@ -0,0 +1,102 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2015, Luke Bonham |
||||||
|
* (c) 2015, plotnikovanton |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local wibox = require("wibox") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local gears = require("gears") |
||||||
|
|
||||||
|
-- Lain Cairo separators util submodule |
||||||
|
-- lain.util.separators |
||||||
|
local separators = {} |
||||||
|
|
||||||
|
local height = beautiful.awful_widget_height or 0 |
||||||
|
local width = beautiful.separators_width or 9 |
||||||
|
|
||||||
|
-- [[ Arrow |
||||||
|
|
||||||
|
-- Right |
||||||
|
function separators.arrow_right(col1, col2) |
||||||
|
local widget = wibox.widget.base.make_widget() |
||||||
|
|
||||||
|
widget.fit = function(m, w, h) return width, height end |
||||||
|
|
||||||
|
widget.draw = function(mycross, wibox, cr, width, height) |
||||||
|
if col2 ~= "alpha" then |
||||||
|
cr:set_source_rgb(gears.color.parse_color(col2)) |
||||||
|
cr:new_path() |
||||||
|
cr:move_to(0, 0) |
||||||
|
cr:line_to(width, height/2) |
||||||
|
cr:line_to(width, 0) |
||||||
|
cr:close_path() |
||||||
|
cr:fill() |
||||||
|
|
||||||
|
cr:new_path() |
||||||
|
cr:move_to(0, height) |
||||||
|
cr:line_to(width, height/2) |
||||||
|
cr:line_to(width, height) |
||||||
|
cr:close_path() |
||||||
|
cr:fill() |
||||||
|
end |
||||||
|
|
||||||
|
if col1 ~= "alpha" then |
||||||
|
cr:set_source_rgb(gears.color.parse_color(col1)) |
||||||
|
cr:new_path() |
||||||
|
cr:move_to(0, 0) |
||||||
|
cr:line_to(width, height/2) |
||||||
|
cr:line_to(0, height) |
||||||
|
cr:close_path() |
||||||
|
cr:fill() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return widget |
||||||
|
end |
||||||
|
|
||||||
|
-- Left |
||||||
|
function separators.arrow_left(col1, col2) |
||||||
|
local widget = wibox.widget.base.make_widget() |
||||||
|
|
||||||
|
widget.fit = function(m, w, h) return width, height end |
||||||
|
|
||||||
|
widget.draw = function(mycross, wibox, cr, width, height) |
||||||
|
if col1 ~= "alpha" then |
||||||
|
cr:set_source_rgb(gears.color.parse_color(col1)) |
||||||
|
cr:new_path() |
||||||
|
cr:move_to(width, 0) |
||||||
|
cr:line_to(0, height/2) |
||||||
|
cr:line_to(0, 0) |
||||||
|
cr:close_path() |
||||||
|
cr:fill() |
||||||
|
|
||||||
|
cr:new_path() |
||||||
|
cr:move_to(width, height) |
||||||
|
cr:line_to(0, height/2) |
||||||
|
cr:line_to(0, height) |
||||||
|
cr:close_path() |
||||||
|
cr:fill() |
||||||
|
end |
||||||
|
|
||||||
|
if col2 ~= "alpha" then |
||||||
|
cr:new_path() |
||||||
|
cr:move_to(width, 0) |
||||||
|
cr:line_to(0, height/2) |
||||||
|
cr:line_to(width, height) |
||||||
|
cr:close_path() |
||||||
|
|
||||||
|
cr:set_source_rgb(gears.color.parse_color(col2)) |
||||||
|
cr:fill() |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
return widget |
||||||
|
end |
||||||
|
|
||||||
|
-- ]] |
||||||
|
|
||||||
|
return separators |
@ -0,0 +1,41 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, Luke Bonham |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local newtimer = require("lain.helpers").newtimer |
||||||
|
local async = require("lain.asyncshell") |
||||||
|
local wibox = require("wibox") |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Basic template for custom widgets |
||||||
|
-- Asynchronous version |
||||||
|
-- lain.widgets.abase |
||||||
|
|
||||||
|
local function worker(args) |
||||||
|
local abase = {} |
||||||
|
local args = args or {} |
||||||
|
local timeout = args.timeout or 5 |
||||||
|
local cmd = args.cmd or "" |
||||||
|
local settings = args.settings or function() end |
||||||
|
|
||||||
|
abase.widget = wibox.widget.textbox('') |
||||||
|
|
||||||
|
function abase.update() |
||||||
|
async.request(cmd, function(f) |
||||||
|
output = f |
||||||
|
widget = abase.widget |
||||||
|
settings() |
||||||
|
end) |
||||||
|
end |
||||||
|
|
||||||
|
newtimer(cmd, timeout, abase.update) |
||||||
|
|
||||||
|
return setmetatable(abase, { __index = abase.widget }) |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable({}, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,68 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010, Adrian C. <anrxc@sysphere.org> |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local newtimer = require("lain.helpers").newtimer |
||||||
|
local read_pipe = require("lain.helpers").read_pipe |
||||||
|
|
||||||
|
local wibox = require("wibox") |
||||||
|
|
||||||
|
local string = { match = string.match, |
||||||
|
format = string.format } |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- ALSA volume |
||||||
|
-- lain.widgets.alsa |
||||||
|
local alsa = {} |
||||||
|
|
||||||
|
local function worker(args) |
||||||
|
local args = args or {} |
||||||
|
local timeout = args.timeout or 5 |
||||||
|
local settings = args.settings or function() end |
||||||
|
|
||||||
|
alsa.cmd = args.cmd or "amixer" |
||||||
|
alsa.channel = args.channel or "Master" |
||||||
|
|
||||||
|
alsa.widget = wibox.widget.textbox('') |
||||||
|
|
||||||
|
function alsa.update() |
||||||
|
local mixer = read_pipe(string.format("%s get %s", alsa.cmd, alsa.channel)) |
||||||
|
|
||||||
|
volume_now = {} |
||||||
|
|
||||||
|
volume_now.level, volume_now.status = string.match(mixer, "([%d]+)%%.*%[([%l]*)") |
||||||
|
|
||||||
|
if volume_now.level == nil |
||||||
|
then |
||||||
|
volume_now.level = "0" |
||||||
|
volume_now.status = "off" |
||||||
|
end |
||||||
|
|
||||||
|
if volume_now.status == "" |
||||||
|
then |
||||||
|
if volume_now.level == "0" |
||||||
|
then |
||||||
|
volume_now.status = "off" |
||||||
|
else |
||||||
|
volume_now.status = "on" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
widget = alsa.widget |
||||||
|
settings() |
||||||
|
end |
||||||
|
|
||||||
|
timer_id = string.format("alsa-%s-%s", alsa.cmd, alsa.channel) |
||||||
|
|
||||||
|
newtimer(timer_id, timeout, alsa.update) |
||||||
|
|
||||||
|
return setmetatable(alsa, { __index = alsa.widget }) |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(alsa, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,179 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2013, Rman |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local newtimer = require("lain.helpers").newtimer |
||||||
|
local read_pipe = require("lain.helpers").read_pipe |
||||||
|
|
||||||
|
local awful = require("awful") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local naughty = require("naughty") |
||||||
|
|
||||||
|
local math = { modf = math.modf } |
||||||
|
local mouse = mouse |
||||||
|
local string = { format = string.format, |
||||||
|
match = string.match, |
||||||
|
rep = string.rep } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- ALSA volume bar |
||||||
|
-- lain.widgets.alsabar |
||||||
|
local alsabar = { |
||||||
|
channel = "Master", |
||||||
|
step = "2%", |
||||||
|
|
||||||
|
colors = { |
||||||
|
background = beautiful.bg_normal, |
||||||
|
mute = "#EB8F8F", |
||||||
|
unmute = "#A4CE8A" |
||||||
|
}, |
||||||
|
|
||||||
|
terminal = terminal or "xterm", |
||||||
|
mixer = terminal .. " -e alsamixer", |
||||||
|
|
||||||
|
notifications = { |
||||||
|
font = beautiful.font:sub(beautiful.font:find(""), beautiful.font:find(" ")), |
||||||
|
font_size = "11", |
||||||
|
color = beautiful.fg_normal, |
||||||
|
bar_size = 18, |
||||||
|
screen = 1 |
||||||
|
}, |
||||||
|
|
||||||
|
_current_level = 0, |
||||||
|
_muted = false |
||||||
|
} |
||||||
|
|
||||||
|
function alsabar.notify() |
||||||
|
alsabar.update() |
||||||
|
|
||||||
|
local preset = { |
||||||
|
title = "", |
||||||
|
text = "", |
||||||
|
timeout = 5, |
||||||
|
screen = alsabar.notifications.screen, |
||||||
|
font = alsabar.notifications.font .. " " .. |
||||||
|
alsabar.notifications.font_size, |
||||||
|
fg = alsabar.notifications.color |
||||||
|
} |
||||||
|
|
||||||
|
if alsabar._muted |
||||||
|
then |
||||||
|
preset.title = alsabar.channel .. " - Muted" |
||||||
|
else |
||||||
|
preset.title = alsabar.channel .. " - " .. alsabar._current_level .. "%" |
||||||
|
end |
||||||
|
|
||||||
|
int = math.modf((alsabar._current_level / 100) * alsabar.notifications.bar_size) |
||||||
|
preset.text = "[" |
||||||
|
.. string.rep("|", int) |
||||||
|
.. string.rep(" ", alsabar.notifications.bar_size - int) |
||||||
|
.. "]" |
||||||
|
|
||||||
|
if alsabar.followmouse then |
||||||
|
preset.screen = mouse.screen |
||||||
|
end |
||||||
|
|
||||||
|
if alsabar._notify ~= nil then |
||||||
|
alsabar._notify = naughty.notify ({ |
||||||
|
replaces_id = alsabar._notify.id, |
||||||
|
preset = preset, |
||||||
|
}) |
||||||
|
else |
||||||
|
alsabar._notify = naughty.notify ({ |
||||||
|
preset = preset, |
||||||
|
}) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
local function worker(args) |
||||||
|
local args = args or {} |
||||||
|
local timeout = args.timeout or 5 |
||||||
|
local settings = args.settings or function() end |
||||||
|
local width = args.width or 63 |
||||||
|
local height = args.heigth or 1 |
||||||
|
local ticks = args.ticks or false |
||||||
|
local ticks_size = args.ticks_size or 7 |
||||||
|
local vertical = args.vertical or false |
||||||
|
|
||||||
|
alsabar.cmd = args.cmd or "amixer" |
||||||
|
alsabar.channel = args.channel or alsabar.channel |
||||||
|
alsabar.step = args.step or alsabar.step |
||||||
|
alsabar.colors = args.colors or alsabar.colors |
||||||
|
alsabar.notifications = args.notifications or alsabar.notifications |
||||||
|
alsabar.followmouse = args.followmouse or false |
||||||
|
|
||||||
|
alsabar.bar = awful.widget.progressbar() |
||||||
|
|
||||||
|
alsabar.bar:set_background_color(alsabar.colors.background) |
||||||
|
alsabar.bar:set_color(alsabar.colors.unmute) |
||||||
|
alsabar.tooltip = awful.tooltip({ objects = { alsabar.bar } }) |
||||||
|
alsabar.bar:set_width(width) |
||||||
|
alsabar.bar:set_height(height) |
||||||
|
alsabar.bar:set_ticks(ticks) |
||||||
|
alsabar.bar:set_ticks_size(ticks_size) |
||||||
|
alsabar.bar:set_vertical(vertical) |
||||||
|
|
||||||
|
function alsabar.update() |
||||||
|
-- Get mixer control contents |
||||||
|
local mixer = read_pipe(string.format("%s get %s", alsabar.cmd, alsabar.channel)) |
||||||
|
|
||||||
|
-- Capture mixer control state: [5%] ... ... [on] |
||||||
|
local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)") |
||||||
|
|
||||||
|
if volu == nil then |
||||||
|
volu = 0 |
||||||
|
mute = "off" |
||||||
|
end |
||||||
|
|
||||||
|
alsabar._current_level = tonumber(volu) |
||||||
|
alsabar.bar:set_value(alsabar._current_level / 100) |
||||||
|
if not mute and tonumber(volu) == 0 or mute == "off" |
||||||
|
then |
||||||
|
alsabar._muted = true |
||||||
|
alsabar.tooltip:set_text (" [Muted] ") |
||||||
|
alsabar.bar:set_color(alsabar.colors.mute) |
||||||
|
else |
||||||
|
alsabar._muted = false |
||||||
|
alsabar.tooltip:set_text(string.format(" %s:%s ", alsabar.channel, volu)) |
||||||
|
alsabar.bar:set_color(alsabar.colors.unmute) |
||||||
|
end |
||||||
|
|
||||||
|
volume_now = {} |
||||||
|
volume_now.level = tonumber(volu) |
||||||
|
volume_now.status = mute |
||||||
|
settings() |
||||||
|
end |
||||||
|
|
||||||
|
alsabar.bar:buttons (awful.util.table.join ( |
||||||
|
awful.button ({}, 1, function() |
||||||
|
awful.util.spawn(alsabar.mixer) |
||||||
|
end), |
||||||
|
awful.button ({}, 3, function() |
||||||
|
awful.util.spawn(string.format("%s set %s toggle", alsabar.cmd, alsabar.channel)) |
||||||
|
alsabar.update() |
||||||
|
end), |
||||||
|
awful.button ({}, 4, function() |
||||||
|
awful.util.spawn(string.format("%s set %s %s+", alsabar.cmd, alsabar.channel, alsabar.step)) |
||||||
|
alsabar.update() |
||||||
|
end), |
||||||
|
awful.button ({}, 5, function() |
||||||
|
awful.util.spawn(string.format("%s set %s %s-", alsabar.cmd, alsabar.channel, alsabar.step)) |
||||||
|
alsabar.update() |
||||||
|
end) |
||||||
|
)) |
||||||
|
|
||||||
|
timer_id = string.format("alsabar-%s-%s", alsabar.cmd, alsabar.channel) |
||||||
|
|
||||||
|
newtimer(timer_id, timeout, alsabar.update) |
||||||
|
|
||||||
|
return alsabar |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(alsabar, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,39 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2014, Luke Bonham |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local newtimer = require("lain.helpers").newtimer |
||||||
|
local read_pipe = require("lain.helpers").read_pipe |
||||||
|
|
||||||
|
local wibox = require("wibox") |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Basic template for custom widgets |
||||||
|
-- lain.widgets.base |
||||||
|
|
||||||
|
local function worker(args) |
||||||
|
local base = {} |
||||||
|
local args = args or {} |
||||||
|
local timeout = args.timeout or 5 |
||||||
|
local cmd = args.cmd or "" |
||||||
|
local settings = args.settings or function() end |
||||||
|
|
||||||
|
base.widget = wibox.widget.textbox('') |
||||||
|
|
||||||
|
function base.update() |
||||||
|
output = read_pipe(cmd) |
||||||
|
widget = base.widget |
||||||
|
settings() |
||||||
|
end |
||||||
|
|
||||||
|
newtimer(cmd, timeout, base.update) |
||||||
|
|
||||||
|
return setmetatable(base, { __index = base.widget }) |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable({}, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,149 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local newtimer = require("lain.helpers").newtimer |
||||||
|
local first_line = require("lain.helpers").first_line |
||||||
|
|
||||||
|
local naughty = require("naughty") |
||||||
|
local wibox = require("wibox") |
||||||
|
|
||||||
|
local math = { floor = math.floor } |
||||||
|
local string = { format = string.format } |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Battery infos |
||||||
|
-- lain.widgets.bat |
||||||
|
local bat = {} |
||||||
|
|
||||||
|
local function worker(args) |
||||||
|
local args = args or {} |
||||||
|
local timeout = args.timeout or 30 |
||||||
|
local battery = args.battery or "BAT0" |
||||||
|
local notify = args.notify or "on" |
||||||
|
local settings = args.settings or function() end |
||||||
|
|
||||||
|
bat.widget = wibox.widget.textbox('') |
||||||
|
|
||||||
|
bat_notification_low_preset = { |
||||||
|
title = "Battery low", |
||||||
|
text = "Plug the cable!", |
||||||
|
timeout = 15, |
||||||
|
fg = "#202020", |
||||||
|
bg = "#CDCDCD" |
||||||
|
} |
||||||
|
|
||||||
|
bat_notification_critical_preset = { |
||||||
|
title = "Battery exhausted", |
||||||
|
text = "Shutdown imminent", |
||||||
|
timeout = 15, |
||||||
|
fg = "#000000", |
||||||
|
bg = "#FFFFFF" |
||||||
|
} |
||||||
|
|
||||||
|
function update() |
||||||
|
bat_now = { |
||||||
|
status = "Not present", |
||||||
|
perc = "N/A", |
||||||
|
time = "N/A", |
||||||
|
watt = "N/A" |
||||||
|
} |
||||||
|
|
||||||
|
local bstr = "/sys/class/power_supply/" .. battery |
||||||
|
|
||||||
|
local present = first_line(bstr .. "/present") |
||||||
|
|
||||||
|
if present == "1" |
||||||
|
then |
||||||
|
local rate = first_line(bstr .. "/power_now") or |
||||||
|
first_line(bstr .. "/current_now") |
||||||
|
|
||||||
|
local ratev = first_line(bstr .. "/voltage_now") |
||||||
|
|
||||||
|
local rem = first_line(bstr .. "/energy_now") or |
||||||
|
first_line(bstr .. "/charge_now") |
||||||
|
|
||||||
|
local tot = first_line(bstr .. "/energy_full") or |
||||||
|
first_line(bstr .. "/charge_full") |
||||||
|
|
||||||
|
bat_now.status = first_line(bstr .. "/status") or "N/A" |
||||||
|
|
||||||
|
rate = tonumber(rate) or 1 |
||||||
|
ratev = tonumber(ratev) |
||||||
|
rem = tonumber(rem) |
||||||
|
tot = tonumber(tot) |
||||||
|
|
||||||
|
local time_rat = 0 |
||||||
|
if bat_now.status == "Charging" |
||||||
|
then |
||||||
|
time_rat = (tot - rem) / rate |
||||||
|
elseif bat_now.status == "Discharging" |
||||||
|
then |
||||||
|
time_rat = rem / rate |
||||||
|
end |
||||||
|
|
||||||
|
local hrs = math.floor(time_rat) |
||||||
|
if hrs < 0 then hrs = 0 elseif hrs > 23 then hrs = 23 end |
||||||
|
|
||||||
|
local min = math.floor((time_rat - hrs) * 60) |
||||||
|
if min < 0 then min = 0 elseif min > 59 then min = 59 end |
||||||
|
|
||||||
|
bat_now.time = string.format("%02d:%02d", hrs, min) |
||||||
|
|
||||||
|
bat_now.perc = first_line(bstr .. "/capacity") |
||||||
|
|
||||||
|
if not bat_now.perc then |
||||||
|
local perc = (rem / tot) * 100 |
||||||
|
if perc <= 100 then |
||||||
|
bat_now.perc = string.format("%d", perc) |
||||||
|
elseif perc > 100 then |
||||||
|
bat_now.perc = "100" |
||||||
|
elseif perc < 0 then |
||||||
|
bat_now.perc = "0" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
if rate ~= nil and ratev ~= nil then |
||||||
|
bat_now.watt = string.format("%.2fW", (rate * ratev) / 1e12) |
||||||
|
else |
||||||
|
bat_now.watt = "N/A" |
||||||
|
end |
||||||
|
|
||||||
|
end |
||||||
|
|
||||||
|
widget = bat.widget |
||||||
|
settings() |
||||||
|
|
||||||
|
-- notifications for low and critical states |
||||||
|
if bat_now.status == "Discharging" and notify == "on" and bat_now.perc ~= nil |
||||||
|
then |
||||||
|
local nperc = tonumber(bat_now.perc) or 100 |
||||||
|
if nperc <= 5 |
||||||
|
then |
||||||
|
bat.id = naughty.notify({ |
||||||
|
preset = bat_notification_critical_preset, |
||||||
|
replaces_id = bat.id, |
||||||
|
}).id |
||||||
|
elseif nperc <= 15 |
||||||
|
then |
||||||
|
bat.id = naughty.notify({ |
||||||
|
preset = bat_notification_low_preset, |
||||||
|
replaces_id = bat.id, |
||||||
|
}).id |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
newtimer(battery, timeout, update) |
||||||
|
|
||||||
|
return bat.widget |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(bat, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,62 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
* (c) 2010-2012, Peter Hofmann |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local wibox = require("awful.wibox") |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Creates a thin wibox at a position relative to another wibox |
||||||
|
-- lain.widgets.borderbox |
||||||
|
local borderbox = {} |
||||||
|
|
||||||
|
local function worker(relbox, s, args) |
||||||
|
local where = args.position or 'top' |
||||||
|
local color = args.color or '#FFFFFF' |
||||||
|
local size = args.size or 1 |
||||||
|
local box = nil |
||||||
|
local wiboxarg = { |
||||||
|
position = nil, |
||||||
|
bg = color |
||||||
|
} |
||||||
|
|
||||||
|
if where == 'top' |
||||||
|
then |
||||||
|
wiboxarg.width = relbox.width |
||||||
|
wiboxarg.height = size |
||||||
|
box = wibox(wiboxarg) |
||||||
|
box.x = relbox.x |
||||||
|
box.y = relbox.y - size |
||||||
|
elseif where == 'bottom' |
||||||
|
then |
||||||
|
wiboxarg.width = relbox.width |
||||||
|
wiboxarg.height = size |
||||||
|
box = wibox(wiboxarg) |
||||||
|
box.x = relbox.x |
||||||
|
box.y = relbox.y + relbox.height |
||||||
|
elseif where == 'left' |
||||||
|
then |
||||||
|
wiboxarg.width = size |
||||||
|
wiboxarg.height = relbox.height |
||||||
|
box = wibox(wiboxarg) |
||||||
|
box.x = relbox.x - size |
||||||
|
box.y = relbox.y |
||||||
|
elseif where == 'right' |
||||||
|
then |
||||||
|
wiboxarg.width = size |
||||||
|
wiboxarg.height = relbox.height |
||||||
|
box = wibox(wiboxarg) |
||||||
|
box.x = relbox.x + relbox.width |
||||||
|
box.y = relbox.y |
||||||
|
end |
||||||
|
|
||||||
|
box.screen = s |
||||||
|
return box |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(borderbox, { __call = function(_, ...) return worker(...) end }) |
@ -0,0 +1,140 @@ |
|||||||
|
|
||||||
|
--[[ |
||||||
|
|
||||||
|
Licensed under GNU General Public License v2 |
||||||
|
* (c) 2013, Luke Bonham |
||||||
|
|
||||||
|
--]] |
||||||
|
|
||||||
|
local icons_dir = require("lain.helpers").icons_dir |
||||||
|
|
||||||
|
local awful = require("awful") |
||||||
|
local beautiful = require("beautiful") |
||||||
|
local naughty = require("naughty") |
||||||
|
|
||||||
|
local io = { popen = io.popen } |
||||||
|
local os = { date = os.date } |
||||||
|
local mouse = mouse |
||||||
|
local tonumber = tonumber |
||||||
|
|
||||||
|
local setmetatable = setmetatable |
||||||
|
|
||||||
|
-- Calendar notification |
||||||
|
-- lain.widgets.calendar |
||||||
|
local calendar = {} |
||||||
|
local cal_notification = nil |
||||||
|
|
||||||
|
function calendar:hide() |
||||||
|
if cal_notification ~= nil then |
||||||
|
naughty.destroy(cal_notification) |
||||||
|
cal_notification = nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
function calendar:show(t_out, inc_offset, scr) |
||||||
|
calendar:hide() |
||||||
|
|
||||||
|
local offs = inc_offset or 0 |
||||||
|
local tims = t_out or 0 |
||||||
|
local f, c_text |
||||||
|
local today = tonumber(os.date('%d')) |
||||||
|
local init_t = calendar.cal .. ' ' .. calendar.post_cal .. ' ' .. |
||||||
|
' | sed -r -e "s/_\\x08//g" | sed -r -e "s/(^| )(' |
||||||
|
|
||||||
|
calendar.offset = calendar.offset + offs |
||||||
|
|
||||||
|
if offs == 0 or calendar.offset == 0 |
||||||
|
then -- current month showing, today highlighted |
||||||
|
calendar.offset = 0 |
||||||
|
calendar.notify_icon = calendar.icons .. today .. ".png" |
||||||
|
|
||||||
|
-- bg and fg inverted to highlight today |
||||||
|
f = io.popen( init_t .. today .. |
||||||
|
')($| )/\\1<b><span foreground=\\"' |
||||||
|
.. calendar.bg .. |
||||||
|
'\\" background=\\"' |
||||||
|
.. calendar.fg .. |
||||||
|
'\\">\\2<\\/span><\\/b>\\3/"' ) |
||||||
|
|
||||||
|
else -- no current month showing, no day to highlight |
||||||
|
local month = tonumber(os.date('%m')) |
||||||
|
local year = tonumber(os.date('%Y')) |
||||||
|
|
||||||
|
month = month + calendar.offset |
||||||
|
|
||||||
|
if month > 12 then |
||||||
|
month = month % 12 |
||||||
|
year = year + 1 |
||||||
|
if month <= 0 then |
||||||
|
month = 12 |
||||||
|
end |
||||||
|
elseif month < 1 then |
||||||
|
month = month + 12 |
||||||
|
year = year - 1 |
||||||
|
if month <= 0 then |
||||||
|
month = 1 |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
calendar.notify_icon = nil |
||||||
|
|
||||||
|
f = io.popen(calendar.cal .. ' ' .. month .. ' ' .. year .. ' ' .. |
||||||
|
calendar.post_cal) |
||||||
|
end |
||||||
|
|
||||||
|
c_text = "<tt><span font='" .. calendar.font .. " " |
||||||
|
.. calendar.font_size .. "'><b>" |
||||||
|
.. f:read() .. "</b>\n\n" |
||||||
|
.. f:read() .. "\n" |
||||||
|
.. f:read("*all"):gsub("\n*$", "") |
||||||
|
.. "</span></tt>" |
||||||
|
f:close() |
||||||
|
|
||||||
|
if calendar.followmouse then |
||||||
|
scrp = mouse.screen |
||||||
|
else |
||||||
|
scrp = scr or calendar.scr_pos |
||||||
|
end |
||||||
|
|
||||||
|
cal_notification = naughty.notify({ |
||||||
|
text = c_text, |
||||||
|
icon = calendar.notify_icon, |
||||||
|
position = calendar.position, |
||||||
|
fg = calendar.fg, |
||||||
|
bg = calendar.bg, |
||||||
|
timeout = tims, |
||||||
|
screen = scrp |
||||||
|
}) |
||||||
|
end |
||||||
|
|
||||||
|
function calendar:attach(widget, args) |
||||||
|
local args = args or {} |
||||||
|
|
||||||
|
calendar.cal = args.cal or "/usr/bin/cal" |
||||||
|
calendar.post_cal = args.post_cal or "" |
||||||
|
calendar.icons = args.icons or icons_dir .. "cal/white/" |
||||||
|
calendar.font = args.font or beautiful.font:sub(beautiful.font:find(""), |
||||||
|
beautiful.font:find(" ")) |
||||||
|
calendar.font_size = tonumber(args.font_size) or 11 |
||||||
|
calendar.fg = args.fg or beautiful.fg_normal or "#FFFFFF" |
||||||
|
calendar.bg = args.bg or beautiful.bg_normal or "#FFFFFF" |
||||||
|
calendar.position = args.position or "top_right" |
||||||
|
calendar.scr_pos = args.scr_pos or 1 |
||||||
|
calendar.followmouse = args.followmouse or false |
||||||
|
|
||||||
|
calendar.offset = 0 |
||||||
|
calendar.notify_icon = nil |
||||||
|
|
||||||
|
widget:connect_signal("mouse::enter", function () calendar:show(0, 0, calendar.scr_pos) end) |
||||||
|
widget:connect_signal("mouse::leave", function () calendar:hide() end) |
||||||
|
widget:buttons(awful.util.table.join(awful.button({ }, 1, function () |
||||||
|
calendar:show(0, -1, calendar.scr_pos) end), |
||||||
|
awful.button({ }, 3, function () |
||||||
|
calendar:show(0, 1, calendar.scr_pos) end), |
||||||
|
awful.button({ }, 4, function () |
||||||
|
calendar:show(0, -1, calendar.scr_pos) end), |
||||||
|
awful.button({ }, 5, function () |
||||||
|
calendar:show(0, 1, calendar.scr_pos) end))) |
||||||
|
end |
||||||
|
|
||||||
|
return setmetatable(calendar, { __call = function(_, ...) return create(...) end }) |