Add the wallpaper module

This commit is contained in:
Grumph 2021-01-23 22:31:04 +01:00
parent 1f6cd7374c
commit 2ffdfc918c
6 changed files with 422 additions and 0 deletions

59
helpers/color.lua Normal file
View File

@ -0,0 +1,59 @@
local _color = {}
--- Try to guess if a color is dark or light.
--
-- @string color The color with hexadecimal HTML format `"#RRGGBB"`.
-- @treturn bool `true` if the color is dark, `false` if it is light.
function _color.is_dark(color)
-- Try to determine if the color is dark or light
local numeric_value = 0;
for s in color:gmatch("[a-fA-F0-9][a-fA-F0-9]") do
numeric_value = numeric_value + tonumber("0x"..s);
end
return (numeric_value < 383)
end
--- Lighten a color.
--
-- @string color The color to lighten with hexadecimal HTML format `"#RRGGBB"`.
-- @int[opt=26] amount How much light from 0 to 255. Default is around 10%.
-- @treturn string The lighter color
function _color.lighten(color, amount)
amount = amount or 26
local c = {
r = tonumber("0x"..color:sub(2,3)),
g = tonumber("0x"..color:sub(4,5)),
b = tonumber("0x"..color:sub(6,7)),
}
c.r = c.r + amount
c.r = c.r < 0 and 0 or c.r
c.r = c.r > 255 and 255 or c.r
c.g = c.g + amount
c.g = c.g < 0 and 0 or c.g
c.g = c.g > 255 and 255 or c.g
c.b = c.b + amount
c.b = c.b < 0 and 0 or c.b
c.b = c.b > 255 and 255 or c.b
return string.format('#%02x%02x%02x', c.r, c.g, c.b)
end
--- Darken a color.
--
-- @string color The color to darken with hexadecimal HTML format `"#RRGGBB"`.
-- @int[opt=26] amount How much dark from 0 to 255. Default is around 10%.
-- @treturn string The darker color
function _color.darken(color, amount)
amount = amount or 26
return _color.lighten(color, -amount)
end
return _color

39
helpers/filesystem.lua Normal file
View File

@ -0,0 +1,39 @@
local Gio = require("lgi").Gio
local _filesystem = {}
--- Get a list of files from a given directory.
-- @string path The directory to search.
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
-- If ommited, all files are considered.
-- @bool[opt=false] recursive List files from subdirectories
-- @staticfct bling.helpers.filesystem.get_random_file_from_dir
function _filesystem.list_directory_files(path, exts, recursive)
recursive = recursive or false
local files, valid_exts = {}, {}
-- Transforms { "jpg", ... } into { [jpg] = #, ... }
if exts then for i, j in ipairs(exts) do valid_exts[j:lower()] = i end end
-- Build a table of files from the path with the required extensions
local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
if file_list then
for file in function() return file_list:next_file() end do
local file_type = file:get_file_type()
if file_type == "REGULAR" then
local file_name = file:get_display_name()
if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then
table.insert(files, file_name)
end
elseif recursive and file_type == "DIRECTORY" then
local file_name = file:get_display_name()
files = gears.table.join(files, list_directory_files(file_name, exts, recursive))
end
end
end
return files
end
return _filesystem

29
helpers/time.lua Normal file
View File

@ -0,0 +1,29 @@
local time = {}
--- Parse a time string to seconds (from midnight)
--
-- @string time The time (`HH:MM:SS`)
-- @treturn int The number of seconds since 00:00:00
function time.hhmmss_to_seconds(time)
hour_sec = tonumber(string.sub(time, 1, 2)) * 3600
min_sec = tonumber(string.sub(time, 4, 5)) * 60
get_sec = tonumber(string.sub(time, 7, 8))
return (hour_sec + min_sec + get_sec)
end
--- Get time difference in seconds.
--
-- @tparam string base The time to compare from (`HH:MM:SS`).
-- @tparam string base The time to compare to (`HH:MM:SS`).
-- @treturn int Number of seconds between the two times.
function time.time_diff(base, compare)
local diff = time.hhmmss_to_seconds(base) - time.hhmmss_to_seconds(compare)
return diff
end
return time

View File

@ -7,5 +7,6 @@
return {
layout = require(... .. ".layout"),
module = require(... .. ".module"),
helpers = require(... .. ".helpers"),
}

View File

@ -1,6 +1,7 @@
return {
window_swallowing = require(... .. ".window_swallowing"),
tiled_wallpaper = require(... .. ".tiled_wallpaper"),
wallpaper = require(... .. ".wallpaper"),
flash_focus = require(... .. ".flash_focus"),
tabbed = require(... .. ".tabbed")
}

293
module/wallpaper.lua Normal file
View File

@ -0,0 +1,293 @@
---------------------------------------------------------------------------
-- High-level declarative function for setting your wallpaper.
--
--
-- An easy way to setup a complex wallpaper with slideshow, random, schedule, extensibility.
--
-- @usage
-- local wallpaper = require("wallpaper")
-- -- A silly example
-- wallpaper.setup { -- I want a wallpaper
-- change_timer = 500, -- changing every 5 minutes
-- set_function = wallpaper.setters.random, -- in a random way
-- wallpaper = {"#abcdef",
-- "~/Pictures",
-- wallpaper.setters.awesome}, -- from this list (a color, a directory with pictures and the Awesome wallpaper)
-- recursive = false, -- do not read subfolders of "~/Pictures"
-- position = "centered", -- center it on the screen (for pictures)
-- scale = 2, -- 2 time bigger (for pictures)
-- }
--
-- @author Grumph
-- @copyright 2021 Grumph
--
---------------------------------------------------------------------------
local beautiful = require("beautiful")
local gears = require("gears")
local helpers = require(tostring(...):match(".*bling") .. ".helpers")
local setters = {}
--- Apply a wallpaper.
--
-- This function is a helper that will apply a wallpaper_object,
-- either using gears.wallpaper.set or gears.wallpaper.* higher level functions when applicable.
-- @param wallpaper_object A wallpaper object, either
-- a `pattern` (see `gears.wallpaper.set`)
-- a `surf` (see `gears.wallpaper.centered`)
-- a function that actually sets the wallpaper.
-- @tparam table args The argument table containing any of the arguments below.
-- @int[opt=nil] args.screen The screen to use (as used in `gears.wallpaper` functions)
-- @string[opt=nil or "centered"] args.position The `gears.wallpaper` position function to use.
-- Must be set when wallpaper is a file.
-- It can be `"centered"`, `"fit"`, `"tiled"` or `"maximized"`.
-- @string[opt=beautiful.bg_normal or "black"] args.background See `gears.wallpaper`.
-- @bool[opt=false] args.ignore_aspect See `gears.wallpaper`.
-- @tparam[opt={x=0,y=0}] table args.offset See `gears.wallpaper`.
-- @int[opt=1] args.scale See `gears.wallpaper`.
function apply(wallpaper_object, args)
args.background = args.background or beautiful.bg_normal or "black"
args.ignore_aspect = args.ignore_aspect or false -- false = keep aspect ratio
args.offset = args.offset or {x = 0, y = 0}
args.scale = args.scale or 1
local positions = {
["centered"] = function() gears.wallpaper.centered(wallpaper_object, args.screen, args.background, args.scale) end,
["tiled"] = function() gears.wallpaper.tiled(wallpaper_object, args.screen, args.offset) end,
["maximized"] = function() gears.wallpaper.maximized(wallpaper_object, args.screen, args.ignore_aspect, args.offset) end,
["fit"] = function() gears.wallpaper.fit(wallpaper_object, args.screen, args.background) end,
}
if type(wallpaper_object) == "string" and gears.filesystem.file_readable(wallpaper_object) then
-- path of an image file, we use a position function
local p = args.position or "centered"
positions[p]()
elseif type(wallpaper_object) == "function" then
-- function
wallpaper_object(args)
elseif (not gears.color.ensure_pango_color(wallpaper_object, nil)) and args.position then
-- if the user sets a position function, wallpaper_object should be a cairo surface
positions[args.position]()
else
gears.wallpaper.set(wallpaper_object)
end
end
--- Converts `args.wallpaper` to a list of `wallpaper_objects` readable by `apply` function).
--
-- @tparam table args The argument table containing the argument below.
-- @param[opt=`beautiful.wallpaper_path` or `"black"`] args.wallpaper A wallpaper object.
-- It can be a color or a cairo pattern (what `gears.wallpaper.set` understands),
-- a cairo suface (set with gears.wallpaper.set if `args.position` is nil, or with
-- `gears.wallpaper` position functions, see `args.position`),
-- a function similar to args.set_function that will effectively set a wallpaper (usually
-- with `gears.wallpaper` functions),
-- a path to a file,
-- path to a directory containing images,
-- or a list with any of the previous choices.
-- @tparam[opt=`{"jpg", "jpeg", "png", "bmp"}`] table args.image_formats A list of
-- file extensions to filter when `args.wallpaper` is a directory.
-- @bool[opt=true] args.recursive Either to recurse or not when `args.wallpaper` is a directory.
-- @treturn table A list of `wallpaper_objects` (what `apply` can read).
-- @see apply
function prepare_list(args)
args.image_formats = args.image_formats or {"jpg", "jpeg", "png", "bmp"}
args.recursive = args.recursive or true
local wallpapers = (args.wallpaper
or beautiful.wallpaper_path
or "black")
local res = {}
if type(wallpapers) ~= "table" then
wallpapers = {wallpapers}
end
for _, w in ipairs(wallpapers) do
-- w is either:
-- - a directory path (string)
-- - an image path or a color (string)
-- - a cairo surface or a cairo pattern
-- - a function for setting the wallpaper
if type(w) == "string" and gears.filesystem.dir_readable(w) then
local file_list = helpers.filesystem.list_directory_files(w, args.image_formats, args.recursive)
for _, f in ipairs(file_list) do
res[#res + 1] = w .. "/" .. f
end
else
res[#res + 1] = w
end
end
return res
end
local simple_index = 0
--- Set the next wallpaper in a list.
--
-- @tparam table args See `prepare_list` and `apply` arguments
-- @see apply
-- @see prepare_list
function setters.simple(args)
local wallpapers = prepare_list(args)
simple_index = (simple_index % #wallpapers) + 1
apply(wallpapers[simple_index], args)
end
--- Set a random wallpaper from a list.
--
-- @tparam table args See `prepare_list` and `apply` arguments
-- @see apply
-- @see prepare_list
function setters.random(args)
local wallpapers = prepare_list(args)
apply(wallpapers[math.random(#wallpapers)], args)
end
local simple_schedule_object = nil
--- A schedule setter.
--
-- This simple schedule setter was freely inspired by [dynamic-wallpaper](https://github.com/manilarome/awesome-glorious-widgets/blob/master/dynamic-wallpaper/init.lua).
-- @tparam table args The argument table containing any of the arguments below.
-- @tparam table args.wallpaper The schedule table, with the form
-- {
-- ["HH:MM:SS"] = wallpaper,
-- ["HH:MM:SS"] = wallpaper2,
-- }
-- The wallpapers definition can be anything the `schedule_set_function` can read
-- (what you would place in `args.wallpaper` for this function),
-- @tparam[opt=`setters.simple`] function args.wallpaper_set_function The set_function used by default
function setters.simple_schedule(args)
local function update_wallpaper()
local fake_args = gears.table.join(args, {wallpaper = args.wallpaper[simple_schedule_object.closest_lower_time]})
simple_schedule_object.schedule_set_function(fake_args)
end
if not simple_schedule_object then
simple_schedule_object = {}
-- initialize the schedule object, so we don't do it for every call
simple_schedule_object.schedule_set_function = args.schedule_set_function or setters.simple
-- we get the sorted time keys
simple_schedule_object.times = {}
for k in pairs(args.wallpaper) do table.insert(simple_schedule_object.times, k) end
table.sort(simple_schedule_object.times)
-- now we get the closest time which is below current time (the current applicable period)
local function update_timer()
local current_time = os.date("%H:%M:%S")
local next_time = simple_schedule_object.times[1]
simple_schedule_object.closest_lower_time = simple_schedule_object.times[#(simple_schedule_object.times)]
for _, k in ipairs(simple_schedule_object.times) do
if k > current_time then
next_time = k
break
end
simple_schedule_object.closest_lower_time = k
end
simple_schedule_object.timer.timeout = helpers.time.time_diff(next_time, current_time)
if simple_schedule_object.timer.timeout < 0 then
-- the next_time is the day after, so we add 24 hours to the timer
simple_schedule_object.timer.timeout = simple_schedule_object.timer.timeout + 86400
end
simple_schedule_object.timer:again()
update_wallpaper()
end
simple_schedule_object.timer = gears.timer {
callback = update_timer,
}
update_timer()
else
-- if called again (usually when the change_timer is set), we just change the wallpaper depending on current parameters
update_wallpaper()
end
end
--- Set the AWESOME wallpaper.
--
-- @tparam table args The argument table containing the argument below.
-- @param[opt=`beautiful.bg_normal`] args.colors.bg The bg color.
-- If the default is used, the color is darkened if `beautiful.bg_normal` is light
-- or lightned if `beautiful.bg_normal` is dark.
-- @param[opt=`beautiful.fg_normal`] args.colors.fg The fg color.
-- @param[opt=`beautiful.fg_focus`] args.colors.alt_fg The alt_fg color.
--
-- see beautiful.theme_assets.wallpaper
function setters.awesome_wallpaper(args)
local colors = {bg = beautiful.bg_normal, fg = beautiful.fg_normal, alt_fg = beautiful.bg_focus }
colors.bg = helpers.color.is_dark(beautiful.bg_normal)
and helpers.color.lighten(colors.bg)
or helpers.color.darken(colors.bg)
if (type(args.colors) == "table") then
colors.bg = args.colors.bg or colors.bg
colors.fg = args.colors.fg or colors.fg
colors.alt_fg = args.colors.alt_fg or colors.alt_fg
end
-- Generate wallpaper:
if not args.screen then
for s in screen do
gears.wallpaper.set(
beautiful.theme_assets.wallpaper(colors.bg, colors.fg, colors.alt_fg, s)
)
end
else
gears.wallpaper.set(
beautiful.theme_assets.wallpaper(colors.bg, colors.fg, colors.alt_fg, args.screen)
)
end
end
--- Setup a wallpaper.
--
-- @tparam table args Parameters for the wallpaper. It may also contain all parameters your `args.set_function` needs
-- @int[opt=nil] args.screen The screen to use (as used in `gears.wallpaper` functions)
-- @int[opt=nil] args.change_timer Time in seconds for wallpaper changes
-- @tparam[opt=`setters.awesome` or `setters.simple`] function args.set_function A function to set the wallpaper
-- It takes args as parameter (the same args as the setup function).
-- This function is called at `"request::wallpaper"` `screen` signals and at `args.change_timer` timeouts.
-- There is no obligation, but for consistency, the function should use `args.wallpaper` as a feeder.
-- If `args.wallpaper` is defined, the default function is `setters.simple`, else it will be `setters.awesome`.
--
-- @usage
-- local wallpaper = require("wallpaper")
-- wallpaper.setup {
-- change_timer = 631, -- Prime number is better
-- set_function = wallpaper.setters.random,
-- -- parameters for the random setter
-- wallpaper = '/data/pictures/wallpapers',
-- position = "maximized",
-- }
--
-- @see apply
-- @see prepare_list
-- @see setters.simple
function setup(args)
local config = args or {}
config.set_function = config.set_function or (config.wallpaper and setters.simple or setters.awesome_wallpaper)
local function set_wallpaper(s)
config.screen = s or config.screen
config.set_function(config)
end
if config.change_timer and config.change_timer > 0 then
gears.timer {
timeout = config.change_timer,
call_now = false,
autostart = true,
callback = function() set_wallpaper() end
}
end
screen.connect_signal("request::wallpaper", set_wallpaper)
end
return {
setup = setup,
setters = setters,
apply = apply,
prepare_list = prepare_list,
}