Initial commit
This commit is contained in:
1 parent
ed88af090f
commit
05c9c7fc71
448 files changed
+46802
No files matched your search
@@ -0,0 +1,36 @@
|
||||
-- MODULE AUTO-START
|
||||
-- Run all the apps listed in configuration/apps.lua as run_on_start_up only once when awesome start
|
||||
|
||||
local awful = require('awful')
|
||||
local naughty = require('naughty')
|
||||
local apps = require('configuration.apps')
|
||||
local config = require('configuration.config')
|
||||
local debug_mode = config.module.auto_start.debug_mode or false
|
||||
|
||||
local run_once = function(cmd)
|
||||
local findme = cmd
|
||||
local firstspace = cmd:find(' ')
|
||||
if firstspace then
|
||||
findme = cmd:sub(0, firstspace - 1)
|
||||
end
|
||||
awful.spawn.easy_async_with_shell(
|
||||
string.format('pgrep -u $USER -x %s > /dev/null || (%s)', findme, cmd),
|
||||
function(stdout, stderr)
|
||||
-- Debugger
|
||||
if not stderr or stderr == '' or not debug_mode then
|
||||
return
|
||||
end
|
||||
naughty.notification({
|
||||
app_name = 'Start-up Applications',
|
||||
title = '<b>Oof! Error detected when starting an application!</b>',
|
||||
message = stderr:gsub('%\n', ''),
|
||||
timeout = 20,
|
||||
icon = require('beautiful').awesome_icon
|
||||
})
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
for _, app in ipairs(apps.run_on_start_up) do
|
||||
run_once(app)
|
||||
end
|
||||
@@ -0,0 +1,268 @@
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local wibox = require("wibox")
|
||||
local beautiful = require('beautiful')
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
local clickable_container = require('widget.clickable-container')
|
||||
local icons = require('theme.icons')
|
||||
|
||||
local osd_header = wibox.widget {
|
||||
text = 'Brightness',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'left',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local osd_value = wibox.widget {
|
||||
text = '0%',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local slider_osd = wibox.widget {
|
||||
nil,
|
||||
{
|
||||
id = 'bri_osd_slider',
|
||||
bar_shape = gears.shape.rounded_rect,
|
||||
bar_height = dpi(2),
|
||||
bar_color = '#ffffff20',
|
||||
bar_active_color = '#f2f2f2EE',
|
||||
handle_color = '#ffffff',
|
||||
handle_shape = gears.shape.circle,
|
||||
handle_width = dpi(15),
|
||||
handle_border_color = '#00000012',
|
||||
handle_border_width = dpi(1),
|
||||
maximum = 100,
|
||||
widget = wibox.widget.slider
|
||||
},
|
||||
nil,
|
||||
expand = 'none',
|
||||
layout = wibox.layout.align.vertical
|
||||
}
|
||||
|
||||
local bri_osd_slider = slider_osd.bri_osd_slider
|
||||
|
||||
bri_osd_slider:connect_signal(
|
||||
'property::value',
|
||||
function()
|
||||
local brightness_level = bri_osd_slider:get_value()
|
||||
|
||||
awful.spawn('light -S ' .. math.max(brightness_level, 5), false)
|
||||
|
||||
-- Update textbox widget text
|
||||
osd_value.text = brightness_level .. '%'
|
||||
|
||||
-- Update the brightness slider if values here change
|
||||
awesome.emit_signal('widget::brightness:update', brightness_level)
|
||||
|
||||
if awful.screen.focused().show_bri_osd then
|
||||
awesome.emit_signal(
|
||||
'module::brightness_osd:show',
|
||||
true
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
bri_osd_slider:connect_signal(
|
||||
'button::press',
|
||||
function()
|
||||
awful.screen.focused().show_bri_osd = true
|
||||
end
|
||||
)
|
||||
|
||||
bri_osd_slider:connect_signal(
|
||||
'mouse::enter',
|
||||
function()
|
||||
awful.screen.focused().show_bri_osd = true
|
||||
end
|
||||
)
|
||||
|
||||
-- The emit will come from brightness slider
|
||||
awesome.connect_signal(
|
||||
'module::brightness_osd',
|
||||
function(brightness)
|
||||
bri_osd_slider:set_value(brightness)
|
||||
end
|
||||
)
|
||||
|
||||
local icon = wibox.widget {
|
||||
{
|
||||
image = icons.brightness,
|
||||
resize = true,
|
||||
widget = wibox.widget.imagebox
|
||||
},
|
||||
top = dpi(12),
|
||||
bottom = dpi(12),
|
||||
widget = wibox.container.margin
|
||||
}
|
||||
|
||||
local brightness_slider_osd = wibox.widget {
|
||||
icon,
|
||||
slider_osd,
|
||||
spacing = dpi(24),
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
}
|
||||
|
||||
local osd_height = dpi(100)
|
||||
local osd_width = dpi(300)
|
||||
local osd_margin = dpi(10)
|
||||
|
||||
screen.connect_signal(
|
||||
'request::desktop_decoration',
|
||||
function(s)
|
||||
local s = s or {}
|
||||
s.show_bri_osd = false
|
||||
|
||||
s.brightness_osd_overlay = awful.popup {
|
||||
widget = {
|
||||
-- Removing this block will cause an error...
|
||||
},
|
||||
ontop = true,
|
||||
visible = false,
|
||||
type = 'notification',
|
||||
screen = s,
|
||||
height = osd_height,
|
||||
width = osd_width,
|
||||
maximum_height = osd_height,
|
||||
maximum_width = osd_width,
|
||||
offset = dpi(5),
|
||||
shape = gears.shape.rectangle,
|
||||
bg = beautiful.transparent,
|
||||
preferred_anchors = 'middle',
|
||||
preferred_positions = {'left', 'right', 'top', 'bottom'}
|
||||
}
|
||||
|
||||
s.brightness_osd_overlay : setup {
|
||||
{
|
||||
{
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
forced_height = dpi(48),
|
||||
osd_header,
|
||||
nil,
|
||||
osd_value
|
||||
},
|
||||
brightness_slider_osd,
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
left = dpi(24),
|
||||
right = dpi(24),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
bg = beautiful.background,
|
||||
shape = gears.shape.rounded_rect,
|
||||
widget = wibox.container.background()
|
||||
}
|
||||
|
||||
-- Reset timer on mouse hover
|
||||
s.brightness_osd_overlay:connect_signal(
|
||||
'mouse::enter',
|
||||
function()
|
||||
awful.screen.focused().show_bri_osd = true
|
||||
awesome.emit_signal('module::brightness_osd:rerun')
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
|
||||
local hide_osd = gears.timer {
|
||||
timeout = 2,
|
||||
autostart = true,
|
||||
callback = function()
|
||||
local focused = awful.screen.focused()
|
||||
focused.brightness_osd_overlay.visible = false
|
||||
focused.show_bri_osd = false
|
||||
end
|
||||
}
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::brightness_osd:rerun',
|
||||
function()
|
||||
if hide_osd.started then
|
||||
hide_osd:again()
|
||||
else
|
||||
hide_osd:start()
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local placement_placer = function()
|
||||
local focused = awful.screen.focused()
|
||||
|
||||
local right_panel = focused.right_panel
|
||||
local left_panel = focused.left_panel
|
||||
local brightness_osd = focused.brightness_osd_overlay
|
||||
|
||||
if right_panel and left_panel then
|
||||
if right_panel.visible then
|
||||
awful.placement.bottom_left(
|
||||
brightness_osd,
|
||||
{
|
||||
margins = {
|
||||
left = osd_margin,
|
||||
right = 0,
|
||||
top = 0,
|
||||
bottom = osd_margin
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if right_panel then
|
||||
if right_panel.visible then
|
||||
awful.placement.bottom_left(
|
||||
brightness_osd,
|
||||
{
|
||||
margins = {
|
||||
left = osd_margin,
|
||||
right = 0,
|
||||
top = 0,
|
||||
bottom = osd_margin
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
awful.placement.bottom_right(
|
||||
brightness_osd,
|
||||
{
|
||||
margins = {
|
||||
left = 0,
|
||||
right = osd_margin,
|
||||
top = 0,
|
||||
bottom = osd_margin
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::brightness_osd:show',
|
||||
function(bool)
|
||||
placement_placer()
|
||||
awful.screen.focused().brightness_osd_overlay.visible = bool
|
||||
if bool then
|
||||
awesome.emit_signal('module::brightness_osd:rerun')
|
||||
awesome.emit_signal(
|
||||
'module::volume_osd:show',
|
||||
false
|
||||
)
|
||||
else
|
||||
if hide_osd.started then
|
||||
hide_osd:stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
Executable
+364
@@ -0,0 +1,364 @@
|
||||
----------------------------------------------------------------------------
|
||||
--- Wallpaper changer module
|
||||
--
|
||||
-- @author Gerome Matilla <gerome.matilla07@gmail.com>
|
||||
-- @copyright 2019 Gerome Matilla
|
||||
-- @module wallchange
|
||||
--
|
||||
--- Nevermind this. Do what you want.
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
-- This module changes wallpaper based on declared time
|
||||
-- It checks the difference between the current time and the next scheduled time
|
||||
-- Then convert it to seconds to set it as a timeout value
|
||||
|
||||
-- Limitations:
|
||||
-- Timeout paused when laptop/pc is suspended or in sleep mode, and there's probably some bugs too so whatever
|
||||
local awful = require('awful')
|
||||
local gears = require('gears')
|
||||
local beautiful = require('beautiful')
|
||||
local filesystem = gears.filesystem
|
||||
local config = require('configuration.config')
|
||||
|
||||
|
||||
-- ========================================
|
||||
-- Configuration
|
||||
-- Change your preference here
|
||||
-- ========================================
|
||||
|
||||
local wall_config = {
|
||||
-- Wallpaper directory. The default is:
|
||||
-- local wall_config.wall_dir = os.getenv('HOME') .. 'Pictures/Wallpapers/'
|
||||
wall_dir = filesystem.get_configuration_dir() .. (config.module.dynamic_wallpaper.wall_dir or 'theme/wallpapers/'),
|
||||
|
||||
-- If there's a picture format that awesome accepts and i missed
|
||||
-- (which i probably did) feel free to add it right here
|
||||
valid_picture_formats = config.module.dynamic_wallpaper.valid_picture_formats or {"jpg", "png", "jpeg"},
|
||||
|
||||
-- Table mapping schedule to wallpaper filename
|
||||
wallpaper_schedule = config.module.dynamic_wallpaper.wallpaper_schedule or {
|
||||
['00:00:00'] = 'midnight-wallpaper.jpg',
|
||||
['06:22:00'] = 'morning-wallpaper.jpg',
|
||||
['12:00:00'] = 'noon-wallpaper.jpg',
|
||||
['17:58:00'] = 'night-wallpaper.jpg'
|
||||
},
|
||||
|
||||
-- Don't stretch wallpaper on multihead setups if true
|
||||
stretch = config.module.dynamic_wallpaper.stretch or false
|
||||
}
|
||||
|
||||
-- ========================================
|
||||
-- Processes
|
||||
-- Don't touch it if it's working
|
||||
-- ========================================
|
||||
|
||||
-- Get current time
|
||||
local current_time = function()
|
||||
return os.date('%H:%M:%S')
|
||||
end
|
||||
|
||||
-- Countdown variable
|
||||
-- In seconds
|
||||
local the_countdown = nil
|
||||
|
||||
-- Parse seconds to HH:MM:SS
|
||||
local function parse_to_time(seconds)
|
||||
-- DST ruined me :(
|
||||
--return os.date("%H:%M:%S", seconds)
|
||||
|
||||
local function format(str)
|
||||
while #str < 2 do
|
||||
str = '0' .. str
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
local function convert(num)
|
||||
return format(tostring(num))
|
||||
end
|
||||
|
||||
local hours = convert(math.floor(seconds / 3600))
|
||||
seconds = seconds - (hours * 3600)
|
||||
|
||||
local minutes = convert(math.floor(seconds / 60))
|
||||
seconds = seconds - (minutes * 60)
|
||||
|
||||
local seconds = convert(math.floor(seconds))
|
||||
|
||||
return (hours .. ':' .. minutes .. ':' .. seconds)
|
||||
|
||||
end
|
||||
|
||||
-- Parse HH:MM:SS to seconds
|
||||
local parse_to_seconds = function(time)
|
||||
|
||||
-- Convert HH in HH:MM:SS
|
||||
local hour_sec = tonumber(string.sub(time, 1, 2)) * 3600
|
||||
|
||||
-- Convert MM in HH:MM:SS
|
||||
local min_sec = tonumber(string.sub(time, 4, 5)) * 60
|
||||
|
||||
-- Get SS in HH:MM:SS
|
||||
local get_sec = tonumber(string.sub(time, 7, 8))
|
||||
|
||||
-- Return computed seconds
|
||||
return (hour_sec + min_sec + get_sec)
|
||||
end
|
||||
|
||||
-- Get time difference
|
||||
local time_diff = function(future, past)
|
||||
local diff = parse_to_seconds(future) - parse_to_seconds(past)
|
||||
if diff < 0 then
|
||||
diff = diff + (24 * 3600) --If time difference is negative, the future is meant for tomorrow
|
||||
end
|
||||
return diff
|
||||
end
|
||||
|
||||
-- Returns a table containing all file paths in a directory
|
||||
local function get_dir_contents(dir)
|
||||
-- Command to give list of files in directory
|
||||
local dir_explore = 'find ' .. dir .. ' -printf "%f\\n"'
|
||||
local lines = io.popen(dir_explore):lines() --Done synchronously because we literally can't continue without files
|
||||
local files = {}
|
||||
for line in lines do
|
||||
table.insert(files, line)
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
-- Returns a table of all the files that were one of the valid file formats
|
||||
local function filter_files_by_format(files, valid_file_formats)
|
||||
local valid_files = {}
|
||||
for _, file in ipairs(files) do
|
||||
for _, format in ipairs(valid_file_formats) do
|
||||
if string.match(file, ".+%." .. format) ~= nil then
|
||||
table.insert(valid_files, file)
|
||||
break --No need to check other formats
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return valid_files
|
||||
end
|
||||
|
||||
-- Returns a table of files that contained any of the keywords, in the same order as the words themselves
|
||||
local function find_files_containing_keywords(files, keywords)
|
||||
local found_files = {}
|
||||
|
||||
for _, word in ipairs(keywords) do --Preserves keyword order inherently, conveniently
|
||||
for _, file in ipairs(files) do
|
||||
-- Check if file is word, contains word at beginning or contains word between 2 non-alphanumeric characters
|
||||
if file == word or string.find(file, "^" .. word .. "[^%a]") or string.find(file, "[^%a]" .. word .. "[^%a]") then
|
||||
found_files[word] = file
|
||||
break --Only return 1 file per word
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return found_files
|
||||
end
|
||||
|
||||
-- Turn an ordered list of files into a scheduled list of files
|
||||
local function auto_schedule(wall_list)
|
||||
local sched = {}
|
||||
for index, file in ipairs(wall_list) do
|
||||
local auto_time = parse_to_time(parse_to_seconds("24:00:00") * (index - 1) / #wall_list)
|
||||
sched[auto_time] = file
|
||||
end
|
||||
|
||||
return sched
|
||||
end
|
||||
|
||||
-- Reformat whatever schedule was specified into an actual schedule
|
||||
if #wall_config.wallpaper_schedule == 0 then
|
||||
local count = 0
|
||||
-- Determine if empty or if manual schedule
|
||||
for k, v in pairs(wall_config.wallpaper_schedule) do
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
if count == 0 then --Schedule is actually empty
|
||||
-- Get all pictures
|
||||
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
|
||||
|
||||
--Sort pictures as sanely as possible
|
||||
local function order_pictures(a, b) --Attempts to mimic default sort but numbers aren't compared as strings
|
||||
if tonumber(a) ~= nil and tonumber(b) ~= nil then
|
||||
return tonumber(a) < tonumber(b)
|
||||
else
|
||||
return a < b
|
||||
end
|
||||
end
|
||||
table.sort(pictures, order_pictures)
|
||||
|
||||
wall_config.wallpaper_schedule = auto_schedule(pictures)
|
||||
|
||||
else --Schedule is manually timed
|
||||
-- Get times as list
|
||||
local ordered_times = {}
|
||||
for time, _ in pairs(wall_config.wallpaper_schedule) do
|
||||
table.insert(ordered_times, time)
|
||||
end
|
||||
|
||||
-- Sort times using seconds as comparison
|
||||
local function order_time_asc(a, b)
|
||||
return parse_to_seconds(a) < parse_to_seconds(b)
|
||||
end
|
||||
table.sort(ordered_times, order_time_asc)
|
||||
|
||||
-- Get ordered list of keywords from ordered times
|
||||
local keywords = {}
|
||||
for index, time in ipairs(ordered_times) do
|
||||
keywords[index] = wall_config.wallpaper_schedule[time]
|
||||
end
|
||||
|
||||
-- Get any pictures that match keywords
|
||||
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
|
||||
pictures = find_files_containing_keywords(pictures, keywords)
|
||||
|
||||
-- Replace keywords with files
|
||||
for index, time in ipairs(ordered_times) do
|
||||
local word = wall_config.wallpaper_schedule[time]
|
||||
if pictures[word] ~= nil then
|
||||
wall_config.wallpaper_schedule[time] = pictures[word]
|
||||
else --To avoid crashes, we'll remove entries with invalid keywords
|
||||
wall_config.wallpaper_schedule[time] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
else --Schedule is list of keywords
|
||||
local keywords = wall_config.wallpaper_schedule
|
||||
|
||||
-- Get any pictures that match keywords
|
||||
local pictures = filter_files_by_format(get_dir_contents(wall_config.wall_dir), wall_config.valid_picture_formats)
|
||||
pictures = find_files_containing_keywords(pictures, keywords)
|
||||
|
||||
-- Order files by keyword (if a file was found for the keyword)
|
||||
local ordered_pictures = {}
|
||||
for _, word in ipairs(keywords) do
|
||||
local file = pictures[word]
|
||||
if file ~= nil then
|
||||
table.insert(ordered_pictures, file)
|
||||
end
|
||||
end
|
||||
|
||||
wall_config.wallpaper_schedule = auto_schedule(ordered_pictures)
|
||||
end
|
||||
|
||||
-- Set wallpaper
|
||||
local set_wallpaper = function(path)
|
||||
if wall_config.stretch then
|
||||
for s in screen do
|
||||
-- Update wallpaper based on the data in the array
|
||||
gears.wallpaper.maximized (path, s)
|
||||
end
|
||||
else
|
||||
-- Update wallpaper based on the data in the array
|
||||
gears.wallpaper.maximized (path)
|
||||
end
|
||||
end
|
||||
|
||||
-- Update wallpaper (used by the manage_timer function)
|
||||
-- I think the gears.wallpaper.maximized is too fast or being ran asynchronously
|
||||
-- So the wallpaper is not being updated on awesome (re)start without this timer
|
||||
-- We need some delay.
|
||||
-- Hey it's working, so whatever
|
||||
local update_wallpaper = function(wall_name)
|
||||
local wall_dir = wall_config.wall_dir .. wall_name
|
||||
set_wallpaper(wall_dir)
|
||||
|
||||
-- Overwrite the default wallpaper
|
||||
-- This is important in case we add an extra monitor
|
||||
beautiful.wallpaper = wall_dir
|
||||
end
|
||||
|
||||
-- Updates variables
|
||||
local manage_timer = function()
|
||||
-- Get current time
|
||||
local time_now = parse_to_seconds(current_time())
|
||||
|
||||
local previous_time = '' --Scheduled time that should activate now
|
||||
local next_time = '' --Time that should activate next
|
||||
|
||||
local first_time = '24:00:00' --First scheduled time registered (to be found)
|
||||
local last_time = '00:00:00' --Last scheduled time registered (to be found)
|
||||
|
||||
-- Find previous_time
|
||||
for time, wallpaper in pairs(wall_config.wallpaper_schedule) do
|
||||
local parsed_time = parse_to_seconds(time)
|
||||
if previous_time == '' or parsed_time > parse_to_seconds(previous_time) then
|
||||
if parsed_time <= time_now then
|
||||
previous_time = time
|
||||
end
|
||||
end
|
||||
|
||||
if parsed_time > parse_to_seconds(last_time) then
|
||||
last_time = time
|
||||
end
|
||||
end
|
||||
|
||||
-- Previous time being blank = no scheduled time today. Therefore
|
||||
-- the last time was yesterday's latest time
|
||||
if previous_time == '' then
|
||||
previous_time = last_time
|
||||
end
|
||||
|
||||
--Find next_time
|
||||
for time, wallpaper in pairs(wall_config.wallpaper_schedule) do
|
||||
local parsed_time = parse_to_seconds(time)
|
||||
if next_time == '' or parsed_time < parse_to_seconds(next_time) then
|
||||
if parsed_time > time_now then
|
||||
next_time = time
|
||||
end
|
||||
end
|
||||
|
||||
if parsed_time < parse_to_seconds(first_time) then
|
||||
first_time = time
|
||||
end
|
||||
end
|
||||
|
||||
-- Next time being blank means that there is no scheduled times left for
|
||||
-- the current day. So next scheduled time is tomorrow's first time
|
||||
if next_time == '' then
|
||||
next_time = first_time
|
||||
end
|
||||
|
||||
-- Update Wallpaper
|
||||
update_wallpaper(wall_config.wallpaper_schedule[previous_time])
|
||||
|
||||
-- Get the time difference to set as timeout for the wall_updater timer below
|
||||
the_countdown = time_diff(next_time, current_time())
|
||||
|
||||
end
|
||||
|
||||
-- Update values at startup
|
||||
manage_timer()
|
||||
|
||||
local wall_updater = gears.timer {
|
||||
-- The timeout is the difference of current time and the scheduled time we set above.
|
||||
timeout = the_countdown,
|
||||
autostart = true,
|
||||
call_now = true,
|
||||
callback = function()
|
||||
-- Emit signal to update wallpaper
|
||||
awesome.emit_signal('module::change_wallpaper')
|
||||
end
|
||||
}
|
||||
|
||||
-- Update wallpaper here and update the timeout for the next schedule
|
||||
awesome.connect_signal(
|
||||
'module::change_wallpaper',
|
||||
function()
|
||||
--set_wallpaper(wall_dir .. wall_data[2])
|
||||
|
||||
-- Update values for the next specified schedule
|
||||
manage_timer()
|
||||
|
||||
-- Update timer timeout for the next specified schedule
|
||||
wall_updater.timeout = the_countdown
|
||||
|
||||
-- Restart timer
|
||||
wall_updater:again()
|
||||
end
|
||||
)
|
||||
@@ -0,0 +1,360 @@
|
||||
local awful = require('awful')
|
||||
local gears = require('gears')
|
||||
local wibox = require('wibox')
|
||||
local beautiful = require('beautiful')
|
||||
local filesystem = gears.filesystem
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
local icons = require('theme.icons')
|
||||
local apps = require('configuration.apps')
|
||||
local clickable_container = require('widget.clickable-container')
|
||||
local config_dir = filesystem.get_configuration_dir()
|
||||
local widget_icon_dir = config_dir .. 'configuration/user-profile/'
|
||||
|
||||
local msg_table = {
|
||||
'See you later, alligator!',
|
||||
'After a while, crocodile.',
|
||||
'Stay out of trouble.',
|
||||
'I’m out of here.',
|
||||
'Yamete, onii-chan~. UwU',
|
||||
'Okay...bye, fry guy!',
|
||||
'Peace out!',
|
||||
'Peace out, bitch!',
|
||||
'Gotta get going.',
|
||||
'Out to the door, dinosaur.',
|
||||
'Don\'t forget to come back!',
|
||||
'Smell ya later!',
|
||||
'In a while, crocodile.',
|
||||
'Adios, amigo.',
|
||||
'Begone!',
|
||||
'Arrivederci.',
|
||||
'Never look back!',
|
||||
'So long, sucker!',
|
||||
'Au revoir!',
|
||||
'Later, skater!',
|
||||
'That\'ll do pig. That\'ll do.',
|
||||
'Happy trails!',
|
||||
'Smell ya later!',
|
||||
'See you soon, baboon!',
|
||||
'Bye Felicia!',
|
||||
'Sayonara!',
|
||||
'Ciao!',
|
||||
'Well.... bye.',
|
||||
'Delete your browser history!',
|
||||
'See you, Space Cowboy!',
|
||||
'Change da world. My final message. Goodbye.',
|
||||
'Find out on the next episode of Dragonball Z...',
|
||||
'Choose wisely!'
|
||||
}
|
||||
|
||||
local greeter_message = wibox.widget {
|
||||
markup = 'Choose wisely!',
|
||||
font = 'Inter UltraLight 48',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local profile_name = wibox.widget {
|
||||
markup = 'user@hostname',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local profile_imagebox = wibox.widget {
|
||||
image = widget_icon_dir .. 'default.svg',
|
||||
resize = true,
|
||||
forced_height = dpi(140),
|
||||
clip_shape = gears.shape.circle,
|
||||
widget = wibox.widget.imagebox
|
||||
}
|
||||
|
||||
local update_profile_pic = function()
|
||||
awful.spawn.easy_async_with_shell(
|
||||
apps.utils.update_profile,
|
||||
function(stdout)
|
||||
stdout = stdout:gsub('%\n','')
|
||||
if not stdout:match('default') then
|
||||
profile_imagebox:set_image(stdout)
|
||||
else
|
||||
profile_imagebox:set_image(widget_icon_dir .. 'default.svg')
|
||||
end
|
||||
profile_imagebox:emit_signal('widget::redraw_needed')
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
update_profile_pic()
|
||||
|
||||
local update_user_name = function()
|
||||
awful.spawn.easy_async_with_shell(
|
||||
[[
|
||||
fullname="$(getent passwd `whoami` | cut -d ':' -f 5 | cut -d ',' -f 1 | tr -d "\n")"
|
||||
if [ -z "$fullname" ];
|
||||
then
|
||||
printf "$(whoami)@$(hostname)"
|
||||
else
|
||||
printf "$fullname"
|
||||
fi
|
||||
]],
|
||||
function(stdout)
|
||||
stdout = stdout:gsub('%\n','')
|
||||
local first_name = stdout:match('(.*)@') or stdout:match('(.-)%s')
|
||||
first_name = first_name:sub(1, 1):upper() .. first_name:sub(2)
|
||||
profile_name:set_markup(stdout)
|
||||
profile_name:emit_signal('widget::redraw_needed')
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
update_user_name()
|
||||
|
||||
local update_greeter_msg = function()
|
||||
greeter_message:set_markup(msg_table[math.random(#msg_table)])
|
||||
greeter_message:emit_signal('widget::redraw_needed')
|
||||
end
|
||||
|
||||
update_greeter_msg()
|
||||
|
||||
local build_power_button = function(name, icon, callback)
|
||||
local power_button_label= wibox.widget {
|
||||
text = name,
|
||||
font = 'Inter Regular 10',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local power_button = wibox.widget {
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
image = icon,
|
||||
widget = wibox.widget.imagebox
|
||||
},
|
||||
margins = dpi(16),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
bg = beautiful.groups_bg,
|
||||
widget = wibox.container.background
|
||||
},
|
||||
shape = gears.shape.rounded_rect,
|
||||
forced_width = dpi(90),
|
||||
forced_height = dpi(90),
|
||||
widget = clickable_container
|
||||
},
|
||||
left = dpi(24),
|
||||
right = dpi(24),
|
||||
widget = wibox.container.margin
|
||||
}
|
||||
|
||||
local exit_screen_item = wibox.widget {
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
spacing = dpi(5),
|
||||
power_button,
|
||||
power_button_label
|
||||
}
|
||||
|
||||
exit_screen_item:connect_signal(
|
||||
'button::release',
|
||||
function()
|
||||
callback()
|
||||
end
|
||||
)
|
||||
return exit_screen_item
|
||||
end
|
||||
|
||||
local suspend_command = function()
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
awful.spawn.with_shell(apps.default.lock .. ' & systemctl suspend')
|
||||
end
|
||||
|
||||
local logout_command = function()
|
||||
awesome.quit()
|
||||
end
|
||||
|
||||
local lock_command = function()
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
awful.spawn.with_shell(apps.default.lock)
|
||||
end
|
||||
|
||||
local poweroff_command = function()
|
||||
awful.spawn.with_shell('poweroff')
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
end
|
||||
|
||||
local reboot_command = function()
|
||||
awful.spawn.with_shell('reboot')
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
end
|
||||
|
||||
local poweroff = build_power_button('Shutdown', icons.power, poweroff_command)
|
||||
local reboot = build_power_button('Restart', icons.restart, reboot_command)
|
||||
local suspend = build_power_button('Sleep', icons.sleep, suspend_command)
|
||||
local logout = build_power_button('Logout', icons.logout, logout_command)
|
||||
local lock = build_power_button('Lock', icons.lock, lock_command)
|
||||
|
||||
local create_exit_screen = function(s)
|
||||
s.exit_screen = wibox
|
||||
{
|
||||
screen = s,
|
||||
type = 'splash',
|
||||
visible = false,
|
||||
ontop = true,
|
||||
bg = beautiful.background,
|
||||
fg = beautiful.fg_normal,
|
||||
height = s.geometry.height,
|
||||
width = s.geometry.width,
|
||||
x = s.geometry.x,
|
||||
y = s.geometry.y
|
||||
}
|
||||
|
||||
s.exit_screen:buttons(
|
||||
gears.table.join(
|
||||
awful.button(
|
||||
{},
|
||||
2,
|
||||
function()
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
end
|
||||
),
|
||||
awful.button(
|
||||
{},
|
||||
3,
|
||||
function()
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
end
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
s.exit_screen : setup {
|
||||
layout = wibox.layout.align.vertical,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.align.vertical,
|
||||
{
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
spacing = dpi(5),
|
||||
{
|
||||
layout = wibox.layout.align.vertical,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
profile_imagebox,
|
||||
nil
|
||||
},
|
||||
nil
|
||||
},
|
||||
profile_name
|
||||
},
|
||||
nil,
|
||||
expand = 'none',
|
||||
layout = wibox.layout.align.horizontal
|
||||
},
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
widget = wibox.container.margin,
|
||||
margins = dpi(15),
|
||||
greeter_message
|
||||
},
|
||||
nil
|
||||
},
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
{
|
||||
{
|
||||
poweroff,
|
||||
reboot,
|
||||
suspend,
|
||||
logout,
|
||||
lock,
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
},
|
||||
spacing = dpi(30),
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
widget = wibox.container.margin,
|
||||
margins = dpi(15)
|
||||
},
|
||||
nil
|
||||
}
|
||||
},
|
||||
nil
|
||||
}
|
||||
end
|
||||
|
||||
screen.connect_signal(
|
||||
'request::desktop_decoration',
|
||||
function(s)
|
||||
create_exit_screen(s)
|
||||
end
|
||||
)
|
||||
|
||||
screen.connect_signal(
|
||||
'removed',
|
||||
function(s)
|
||||
create_exit_screen(s)
|
||||
end
|
||||
)
|
||||
|
||||
local exit_screen_grabber = awful.keygrabber {
|
||||
auto_start = true,
|
||||
stop_event = 'release',
|
||||
keypressed_callback = function(self, mod, key, command)
|
||||
if key == 's' then
|
||||
suspend_command()
|
||||
|
||||
elseif key == 'e' then
|
||||
logout_command()
|
||||
|
||||
elseif key == 'l' then
|
||||
lock_command()
|
||||
|
||||
elseif key == 'p' then
|
||||
poweroff_command()
|
||||
|
||||
elseif key == 'r' then
|
||||
reboot_command()
|
||||
|
||||
elseif key == 'Escape' or key == 'q' or key == 'x' then
|
||||
awesome.emit_signal('module::exit_screen:hide')
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::exit_screen:show',
|
||||
function()
|
||||
for s in screen do
|
||||
s.exit_screen.visible = false
|
||||
end
|
||||
awful.screen.focused().exit_screen.visible = true
|
||||
exit_screen_grabber:start()
|
||||
end
|
||||
)
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::exit_screen:hide',
|
||||
function()
|
||||
update_greeter_msg()
|
||||
exit_screen_grabber:stop()
|
||||
for s in screen do
|
||||
s.exit_screen.visible = false
|
||||
end
|
||||
end
|
||||
)
|
||||
Executable
+849
@@ -0,0 +1,849 @@
|
||||
local wibox = require('wibox')
|
||||
local gears = require('gears')
|
||||
local awful = require('awful')
|
||||
local naughty = require('naughty')
|
||||
local beautiful = require('beautiful')
|
||||
local filesystem = gears.filesystem
|
||||
local config_dir = filesystem.get_configuration_dir()
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
local apps = require('configuration.apps')
|
||||
local widget_icon_dir = config_dir .. 'configuration/user-profile/'
|
||||
local config = require('configuration.config')
|
||||
|
||||
-- Add paths to package.cpath
|
||||
package.cpath = package.cpath .. ';' .. config_dir .. '/library/?.so;' .. '/usr/lib/lua-pam/?.so;'
|
||||
|
||||
-- Configuration table
|
||||
local locker_config = {
|
||||
-- Clock mode
|
||||
military_clock = config.module.lockscreen.military_clock or false,
|
||||
-- Fallback password
|
||||
fallback_password = function()
|
||||
return config.module.lockscreen.fallback_password or 'toor'
|
||||
end,
|
||||
-- Capture a picture using webcam
|
||||
capture_intruder = config.module.lockscreen.capture_intruder or false,
|
||||
-- Save location, auto creates
|
||||
face_capture_dir = config.module.lockscreen.face_capture_dir or '$(xdg-user-dir PICTURES)/Intruders/',
|
||||
-- Blur background
|
||||
blur_background = config.module.lockscreen.blur_background or false,
|
||||
-- Background directory
|
||||
bg_dir = config.module.lockscreen.bg_dir or (config_dir .. 'theme/wallpapers/'),
|
||||
-- Default background
|
||||
bg_image = config.module.lockscreen.bg_image or 'morning-wallpaper.jpg',
|
||||
-- /tmp directory
|
||||
tmp_wall_dir = config.module.lockscreen.tmp_wall_dir or '/tmp/awesomewm/' .. os.getenv('USER') .. '/'
|
||||
}
|
||||
|
||||
-- Useful variables (DO NOT TOUCH THESE)
|
||||
local input_password = nil
|
||||
local lock_again = nil
|
||||
local type_again = true
|
||||
local capture_now = locker_config.capture_intruder
|
||||
local locked_tag = nil
|
||||
|
||||
local uname_text = wibox.widget {
|
||||
id = 'uname_text',
|
||||
markup = '$USER',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local caps_text = wibox.widget {
|
||||
id = 'uname_text',
|
||||
markup = 'Caps Lock is on',
|
||||
font = 'Inter Italic 10',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
opacity = 0.0,
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local profile_imagebox = wibox.widget {
|
||||
id = 'user_icon',
|
||||
image = widget_icon_dir .. 'default.svg',
|
||||
resize = true,
|
||||
forced_height = dpi(130),
|
||||
forced_width = dpi(130),
|
||||
clip_shape = gears.shape.circle,
|
||||
widget = wibox.widget.imagebox
|
||||
}
|
||||
|
||||
local clock_format = '<span font="Inter Bold 52">%H:%M</span>'
|
||||
if not locker_config.military_clock then
|
||||
clock_format = '<span font="Inter Bold 52">%I:%M %p</span>'
|
||||
end
|
||||
|
||||
-- Create clock widget
|
||||
local time = wibox.widget.textclock(clock_format, 1)
|
||||
|
||||
local wanted_text = wibox.widget {
|
||||
markup = 'INTRUDER ALERT!',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local msg_table = {
|
||||
'This incident will be reported.',
|
||||
'We are watching you.',
|
||||
'We know where you live.',
|
||||
'RUN!',
|
||||
'Yamete, Oniichan~ uwu',
|
||||
'This will self-destruct in 5 seconds!',
|
||||
'Image successfully sent!',
|
||||
'You\'re doomed!',
|
||||
'Authentication failed!',
|
||||
'I am watching you.',
|
||||
'I know where you live.',
|
||||
'RUN!',
|
||||
'Your parents must be proud of you.'
|
||||
}
|
||||
|
||||
local wanted_msg = wibox.widget {
|
||||
markup = 'This incident will be reported!',
|
||||
font = 'Inter Regular 10',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local wanted_image = wibox.widget {
|
||||
image = widget_icon_dir .. 'default.svg',
|
||||
resize = true,
|
||||
forced_height = dpi(120),
|
||||
clip_shape = gears.shape.rounded_rect,
|
||||
widget = wibox.widget.imagebox
|
||||
}
|
||||
|
||||
local date_value = function()
|
||||
local ordinal = nil
|
||||
local date = os.date('%d')
|
||||
local day = os.date('%A')
|
||||
local month = os.date('%B')
|
||||
|
||||
local first_digit = string.sub(date, 0, 1)
|
||||
local last_digit = string.sub(date, -1)
|
||||
if first_digit == '0' then
|
||||
date = last_digit
|
||||
end
|
||||
|
||||
if last_digit == '1' and date ~= '11' then
|
||||
ordinal = 'st'
|
||||
elseif last_digit == '2' and date ~= '12' then
|
||||
ordinal = 'nd'
|
||||
elseif last_digit == '3' and date ~= '13' then
|
||||
ordinal = 'rd'
|
||||
else
|
||||
ordinal = 'th'
|
||||
end
|
||||
|
||||
return date .. ordinal .. ' of ' .. month .. ', ' .. day
|
||||
end
|
||||
|
||||
local date = wibox.widget {
|
||||
markup = date_value(),
|
||||
font = 'Inter Bold 20',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local circle_container = wibox.widget {
|
||||
bg = beautiful.transparent,
|
||||
forced_width = dpi(140),
|
||||
forced_height = dpi(140),
|
||||
shape = gears.shape.circle,
|
||||
widget = wibox.container.background
|
||||
}
|
||||
|
||||
local locker_arc = wibox.widget {
|
||||
bg = beautiful.transparent,
|
||||
forced_width = dpi(140),
|
||||
forced_height = dpi(140),
|
||||
shape = function(cr, width, height)
|
||||
gears.shape.arc(cr, width, height, dpi(5), 0, (math.pi / 2), false, false)
|
||||
end,
|
||||
widget = wibox.container.background
|
||||
}
|
||||
|
||||
local rotate_container = wibox.container.rotate()
|
||||
local locker_widget = wibox.widget {
|
||||
{
|
||||
locker_arc,
|
||||
widget = rotate_container
|
||||
},
|
||||
layout = wibox.layout.fixed.vertical
|
||||
}
|
||||
|
||||
-- Rotation direction table
|
||||
local rotation_direction = {'north', 'west', 'south', 'east'}
|
||||
|
||||
-- Red, Green, Yellow, Blue
|
||||
local red = beautiful.system_red_dark
|
||||
local green = beautiful.system_green_dark
|
||||
local yellow = beautiful.system_yellow_dark
|
||||
local blue = beautiful.system_blue_dark
|
||||
|
||||
-- Color table
|
||||
local arc_color = {red, green, yellow, blue}
|
||||
|
||||
-- Processes
|
||||
local locker = function(s)
|
||||
|
||||
local lockscreen = wibox {
|
||||
screen = s,
|
||||
visible = false,
|
||||
ontop = true,
|
||||
type = 'splash',
|
||||
width = s.geometry.width,
|
||||
height = s.geometry.height,
|
||||
bg = beautiful.background,
|
||||
fg = beautiful.fg_normal
|
||||
}
|
||||
|
||||
-- Update username textbox
|
||||
awful.spawn.easy_async_with_shell(
|
||||
[[
|
||||
sh -c '
|
||||
fullname="$(getent passwd `whoami` | cut -d ':' -f 5 | cut -d ',' -f 1 | tr -d "\n")"
|
||||
if [ -z "$fullname" ];
|
||||
then
|
||||
printf "$(whoami)@$(hostname)"
|
||||
else
|
||||
printf "$fullname"
|
||||
fi
|
||||
'
|
||||
]],
|
||||
function(stdout)
|
||||
stdout = stdout:gsub('%\n','')
|
||||
uname_text:set_markup(stdout)
|
||||
end
|
||||
)
|
||||
|
||||
local update_profile_pic = function()
|
||||
awful.spawn.easy_async_with_shell(
|
||||
apps.utils.update_profile,
|
||||
function(stdout)
|
||||
stdout = stdout:gsub('%\n','')
|
||||
if not stdout:match('default') then
|
||||
profile_imagebox:set_image(stdout)
|
||||
else
|
||||
profile_imagebox:set_image(widget_icon_dir .. 'default.svg')
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
-- Update image
|
||||
gears.timer.start_new(
|
||||
2,
|
||||
function()
|
||||
update_profile_pic()
|
||||
end
|
||||
)
|
||||
|
||||
local wanted_poster = awful.popup {
|
||||
widget = {
|
||||
{
|
||||
{
|
||||
wanted_text,
|
||||
{
|
||||
nil,
|
||||
wanted_image,
|
||||
nil,
|
||||
expand = 'none',
|
||||
layout = wibox.layout.align.horizontal
|
||||
},
|
||||
wanted_msg,
|
||||
spacing = dpi(5),
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
margins = dpi(20),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
bg = beautiful.background,
|
||||
shape = gears.shape.rounded_rect,
|
||||
widget = wibox.container.background
|
||||
},
|
||||
bg = beautiful.transparent,
|
||||
type = 'utility',
|
||||
ontop = true,
|
||||
shape = gears.shape.rectangle,
|
||||
maximum_width = dpi(250),
|
||||
maximum_height = dpi(250),
|
||||
hide_on_right_click = false,
|
||||
preferred_anchors = {'middle'},
|
||||
visible = false
|
||||
}
|
||||
|
||||
-- Place wanted poster at the bottom of primary screen
|
||||
awful.placement.top(
|
||||
wanted_poster,
|
||||
{
|
||||
margins = {
|
||||
top = dpi(10)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
-- Check Capslock state
|
||||
local check_caps = function()
|
||||
awful.spawn.easy_async_with_shell(
|
||||
'xset q | grep Caps | cut -d: -f3 | cut -d0 -f1 | tr -d \' \'',
|
||||
function(stdout)
|
||||
if stdout:match('on') then
|
||||
caps_text.opacity = 1.0
|
||||
else
|
||||
caps_text.opacity = 0.0
|
||||
end
|
||||
caps_text:emit_signal('widget::redraw_needed')
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
-- Rotate the color arc on random direction
|
||||
local locker_arc_rotate = function()
|
||||
|
||||
local direction = rotation_direction[math.random(#rotation_direction)]
|
||||
local color = arc_color[math.random(#arc_color)]
|
||||
|
||||
rotate_container.direction = direction
|
||||
locker_arc.bg = color
|
||||
|
||||
rotate_container:emit_signal('widget::redraw_needed')
|
||||
locker_arc:emit_signal('widget::redraw_needed')
|
||||
locker_widget:emit_signal('widget::redraw_needed')
|
||||
end
|
||||
|
||||
-- Check webcam
|
||||
local check_webcam = function()
|
||||
awful.spawn.easy_async_with_shell(
|
||||
'ls -l /dev/video* | grep /dev/video0',
|
||||
function(stdout)
|
||||
if not locker_config.capture_intruder then
|
||||
capture_now = false
|
||||
return
|
||||
end
|
||||
|
||||
if not stdout:match('/dev/video0') then
|
||||
capture_now = false
|
||||
else
|
||||
capture_now = true
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
check_webcam()
|
||||
|
||||
-- Snap an image of the intruder
|
||||
local intruder_capture = function()
|
||||
local capture_image = [[
|
||||
save_dir="]] .. locker_config.face_capture_dir .. [["
|
||||
date="$(date +%Y%m%d_%H%M%S)"
|
||||
file_loc="${save_dir}SUSPECT-${date}.png"
|
||||
|
||||
if [ ! -d "$save_dir" ]; then
|
||||
mkdir -p "$save_dir";
|
||||
fi
|
||||
|
||||
ffmpeg -f video4linux2 -s 800x600 -i /dev/video0 -ss 0:0:2 -frames 1 "${file_loc}"
|
||||
|
||||
canberra-gtk-play -i camera-shutter &
|
||||
echo "${file_loc}"
|
||||
]]
|
||||
|
||||
-- Capture the filthy intruder face
|
||||
awful.spawn.easy_async_with_shell(
|
||||
capture_image,
|
||||
function(stdout)
|
||||
circle_container.bg = beautiful.transparent
|
||||
|
||||
-- Humiliate the intruder by showing his/her hideous face
|
||||
wanted_image:set_image(stdout:gsub('%\n',''))
|
||||
wanted_msg:set_markup(msg_table[math.random(#msg_table)])
|
||||
wanted_poster.visible= true
|
||||
|
||||
awful.placement.top(
|
||||
wanted_poster,
|
||||
{
|
||||
margins = {
|
||||
top = dpi(10)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
wanted_image:emit_signal('widget::redraw_needed')
|
||||
type_again = true
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
-- Login failed
|
||||
local stoprightthereyoucriminalscum = function()
|
||||
circle_container.bg = red .. 'AA'
|
||||
if capture_now then
|
||||
intruder_capture()
|
||||
else
|
||||
gears.timer.start_new(
|
||||
1,
|
||||
function()
|
||||
circle_container.bg = beautiful.transparent
|
||||
type_again = true
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Login successful
|
||||
local generalkenobi_ohhellothere = function()
|
||||
circle_container.bg = green .. 'AA'
|
||||
|
||||
-- Add a little delay before unlocking completely
|
||||
gears.timer.start_new(
|
||||
1,
|
||||
function()
|
||||
if capture_now then
|
||||
-- Hide wanted poster
|
||||
wanted_poster.visible = false
|
||||
end
|
||||
|
||||
-- Hide all the lockscreen on all screen
|
||||
for s in screen do
|
||||
if s.index == 1 then
|
||||
s.lockscreen.visible = false
|
||||
else
|
||||
s.lockscreen_extended.visible = false
|
||||
end
|
||||
end
|
||||
|
||||
circle_container.bg = beautiful.transparent
|
||||
lock_again = true
|
||||
type_again = true
|
||||
|
||||
-- Select old tag
|
||||
-- And restore minimized focused client if there's any
|
||||
if locked_tag then
|
||||
locked_tag.selected = true
|
||||
locked_tag = nil
|
||||
end
|
||||
local c = awful.client.restore()
|
||||
if c then
|
||||
c:emit_signal('request::activate')
|
||||
c:raise()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
-- A backdoor.
|
||||
-- Sometimes I'm too lazy to type so I decided to create this.
|
||||
-- Sometimes my genius is... it's almost frightening.
|
||||
local back_door = function()
|
||||
generalkenobi_ohhellothere()
|
||||
end
|
||||
|
||||
-- Check module if valid
|
||||
local module_check = function(name)
|
||||
if package.loaded[name] then
|
||||
return true
|
||||
else
|
||||
for _, searcher in ipairs(package.searchers or package.loaders) do
|
||||
local loader = searcher(name)
|
||||
if type(loader) == 'function' then
|
||||
package.preload[name] = loader
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Password/key grabber
|
||||
local password_grabber = awful.keygrabber {
|
||||
auto_start = true,
|
||||
stop_event = 'release',
|
||||
mask_event_callback = true,
|
||||
keybindings = {
|
||||
awful.key {
|
||||
modifiers = {'Control'},
|
||||
key = 'u',
|
||||
on_press = function()
|
||||
input_password = nil
|
||||
end
|
||||
},
|
||||
awful.key {
|
||||
modifiers = {'Mod1', 'Mod4', 'Shift', 'Control'},
|
||||
key = 'Return',
|
||||
on_press = function(self)
|
||||
if not type_again then
|
||||
return
|
||||
end
|
||||
self:stop()
|
||||
|
||||
-- Call backdoor
|
||||
back_door()
|
||||
end
|
||||
}
|
||||
},
|
||||
keypressed_callback = function(self, mod, key, command)
|
||||
if not type_again then
|
||||
return
|
||||
end
|
||||
|
||||
-- Clear input string
|
||||
if key == 'Escape' then
|
||||
-- Clear input threshold
|
||||
input_password = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Accept only the single charactered key
|
||||
-- Ignore 'Shift', 'Control', 'Return', 'F1', 'F2', etc., etc.
|
||||
if #key == 1 then
|
||||
locker_arc_rotate()
|
||||
|
||||
if input_password == nil then
|
||||
input_password = key
|
||||
return
|
||||
end
|
||||
input_password = input_password .. key
|
||||
end
|
||||
|
||||
end,
|
||||
keyreleased_callback = function(self, mod, key, command)
|
||||
locker_arc.bg = beautiful.transparent
|
||||
locker_arc:emit_signal('widget::redraw_needed')
|
||||
|
||||
if key == 'Caps_Lock' then
|
||||
check_caps()
|
||||
return
|
||||
end
|
||||
|
||||
if not type_again then
|
||||
return
|
||||
end
|
||||
|
||||
-- Validation
|
||||
if key == 'Return' then
|
||||
-- Validate password
|
||||
local authenticated = false
|
||||
if input_password ~= nil then
|
||||
-- If lua-pam library is 'okay'
|
||||
if module_check('liblua_pam') then
|
||||
local pam = require('liblua_pam')
|
||||
authenticated = pam:auth_current_user(input_password)
|
||||
else
|
||||
-- Library doesn't exist or returns an error due to some reasons (read the manual)
|
||||
-- Use fallback password data
|
||||
authenticated = input_password == locker_config.fallback_password()
|
||||
|
||||
local rtfm = naughty.action {
|
||||
name = 'Read Wiki',
|
||||
icon_only = false
|
||||
}
|
||||
|
||||
local dismiss = naughty.action {
|
||||
name = 'Dismiss',
|
||||
icon_only = false
|
||||
}
|
||||
|
||||
rtfm:connect_signal(
|
||||
'invoked',
|
||||
function()
|
||||
awful.spawn(
|
||||
[[sh -c "
|
||||
xdg-open 'https://github.com/manilarome/the-glorious-dotfiles/wiki'
|
||||
"]],
|
||||
false
|
||||
)
|
||||
end
|
||||
)
|
||||
|
||||
naughty.notification({
|
||||
app_name = 'Security',
|
||||
title = 'WARNING',
|
||||
message = 'You\'re using the fallback password! It\'s recommended to use the PAM Integration!',
|
||||
urgency = 'critical',
|
||||
actions = { rtfm, dismiss }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
if authenticated then
|
||||
-- Come in!
|
||||
self:stop()
|
||||
generalkenobi_ohhellothere()
|
||||
else
|
||||
-- F*ck off, you [REDACTED]!
|
||||
stoprightthereyoucriminalscum()
|
||||
end
|
||||
|
||||
-- Allow typing again and empty password container
|
||||
type_again = false
|
||||
input_password = nil
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
lockscreen : setup {
|
||||
layout = wibox.layout.align.vertical,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
expand = 'none',
|
||||
spacing = dpi(20),
|
||||
{
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
time,
|
||||
nil
|
||||
},
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
date,
|
||||
nil
|
||||
},
|
||||
expand = 'none',
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
{
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
{
|
||||
circle_container,
|
||||
locker_widget,
|
||||
{
|
||||
layout = wibox.layout.align.vertical,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
nil,
|
||||
profile_imagebox,
|
||||
nil
|
||||
},
|
||||
nil,
|
||||
},
|
||||
layout = wibox.layout.stack
|
||||
},
|
||||
uname_text,
|
||||
caps_text
|
||||
},
|
||||
},
|
||||
nil
|
||||
},
|
||||
nil
|
||||
}
|
||||
|
||||
local show_lockscreen = function()
|
||||
-- Why is there a lock_again variable?
|
||||
-- It prevents the user to spam locking while in a process of authentication
|
||||
-- Prevents a potential bug/problem
|
||||
if lock_again == true or lock_again == nil then
|
||||
-- Force update clock widget
|
||||
time:emit_signal('widget::redraw_needed')
|
||||
|
||||
-- Check capslock status
|
||||
check_caps()
|
||||
|
||||
-- Check webcam status
|
||||
check_webcam()
|
||||
|
||||
-- Show all the lockscreen on each screen
|
||||
for s in screen do
|
||||
if s.index == 1 then
|
||||
s.lockscreen.visible = true
|
||||
else
|
||||
s.lockscreen_extended.visible = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Start keygrabbing, but with a little delay to
|
||||
-- give some extra time for the free_keygrab function
|
||||
gears.timer.start_new(
|
||||
0.5,
|
||||
function()
|
||||
-- Start key grabbing for password
|
||||
password_grabber:start()
|
||||
end
|
||||
)
|
||||
|
||||
-- Dont lock again
|
||||
lock_again = false
|
||||
end
|
||||
end
|
||||
|
||||
local free_keygrab = function()
|
||||
-- Kill rofi instance.
|
||||
awful.spawn.with_shell('kill -9 $(pgrep rofi)')
|
||||
|
||||
-- Check if there's a keygrabbing instance.
|
||||
-- If yes, stop it.
|
||||
local keygrabbing_instance = awful.keygrabber.current_instance
|
||||
if keygrabbing_instance then
|
||||
keygrabbing_instance:stop()
|
||||
end
|
||||
|
||||
-- Unselect all tags and minimize the focused client
|
||||
-- These will fix the problem with virtualbox or
|
||||
-- any other program that has keygrabbing enabled
|
||||
if client.focus then
|
||||
client.focus.minimized = true
|
||||
end
|
||||
for _, t in ipairs(mouse.screen.selected_tags) do
|
||||
locked_tag = t
|
||||
t.selected = false
|
||||
end
|
||||
end
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::lockscreen_show',
|
||||
function()
|
||||
if lock_again == true or lock_again == nil then
|
||||
free_keygrab()
|
||||
show_lockscreen()
|
||||
end
|
||||
end
|
||||
)
|
||||
return lockscreen
|
||||
end
|
||||
|
||||
-- This lockscreen is for the extra/multi monitor
|
||||
local locker_ext = function(s)
|
||||
local extended_lockscreen = wibox {
|
||||
screen = s,
|
||||
visible = false,
|
||||
ontop = true,
|
||||
ontype = 'true',
|
||||
x = s.geometry.x,
|
||||
y = s.geometry.y,
|
||||
width = s.geometry.width,
|
||||
height = s.geometry.height,
|
||||
bg = beautiful.background,
|
||||
fg = beautiful.fg_normal
|
||||
}
|
||||
return extended_lockscreen
|
||||
end
|
||||
|
||||
-- Create lockscreen for each screen
|
||||
local create_lock_screens = function(s)
|
||||
if s.index == 1 then
|
||||
s.lockscreen = locker(s)
|
||||
else
|
||||
s.lockscreen_extended = locker_ext(s)
|
||||
end
|
||||
end
|
||||
|
||||
-- Don't show notification popups if the screen is locked
|
||||
local check_lockscreen_visibility = function()
|
||||
focused = awful.screen.focused()
|
||||
if focused.lockscreen and focused.lockscreen.visible then
|
||||
return true
|
||||
end
|
||||
if focused.lockscreen_extended and focused.lockscreen_extended.visible then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Notifications signal
|
||||
naughty.connect_signal(
|
||||
'request::display',
|
||||
function(_)
|
||||
if check_lockscreen_visibility() then
|
||||
naughty.destroy_all_notifications(nil, 1)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- Filter background image
|
||||
local filter_bg_image = function(wall_name, index, ap, width, height)
|
||||
-- Checks if the blur has to be blurred
|
||||
local blur_filter_param = ''
|
||||
if locker_config.blur_background then
|
||||
blur_filter_param = '-filter Gaussian -blur 0x10'
|
||||
end
|
||||
|
||||
-- Create imagemagick command
|
||||
local magic = [[
|
||||
sh -c "
|
||||
if [ ! -d ]] .. locker_config.tmp_wall_dir ..[[ ];
|
||||
then
|
||||
mkdir -p ]] .. locker_config.tmp_wall_dir .. [[;
|
||||
fi
|
||||
convert -quality 100 -brightness-contrast -20x0 ]] .. ' ' .. blur_filter_param .. ' '.. locker_config.bg_dir .. wall_name ..
|
||||
[[ -gravity center -crop ]] .. ap .. [[:1 +repage -resize ]] .. width .. 'x' .. height ..
|
||||
[[! ]] .. locker_config.tmp_wall_dir .. index .. wall_name .. [[
|
||||
"]]
|
||||
return magic
|
||||
end
|
||||
|
||||
-- Apply lockscreen background image
|
||||
local apply_ls_bg_image = function(wall_name)
|
||||
-- Iterate through all the screens and create a lockscreen for each of it
|
||||
for s in screen do
|
||||
local index = s.index .. '-'
|
||||
|
||||
-- Get screen geometry
|
||||
local screen_width = s.geometry.width
|
||||
local screen_height = s.geometry.height
|
||||
|
||||
-- Get the right resolution/aspect ratio that will be use as the background
|
||||
local aspect_ratio = screen_width / screen_height
|
||||
aspect_ratio = math.floor(aspect_ratio * 100) / 100
|
||||
|
||||
-- Create image filter command
|
||||
local cmd = nil
|
||||
cmd = filter_bg_image(wall_name, index, aspect_ratio, screen_width, screen_height)
|
||||
|
||||
-- Asign lockscreen to each screen
|
||||
if s.index == 1 then
|
||||
-- Primary screen
|
||||
awful.spawn.easy_async_with_shell(
|
||||
cmd,
|
||||
function()
|
||||
s.lockscreen.bgimage = locker_config.tmp_wall_dir .. index .. wall_name
|
||||
end
|
||||
)
|
||||
else
|
||||
-- Multihead screen/s
|
||||
awful.spawn.easy_async_with_shell(
|
||||
cmd,
|
||||
function()
|
||||
s.lockscreen_extended.bgimage = locker_config.tmp_wall_dir .. index .. wall_name
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a lockscreen and its background for each screen on start-up
|
||||
screen.connect_signal(
|
||||
'request::desktop_decoration',
|
||||
function(s)
|
||||
create_lock_screens(s)
|
||||
apply_ls_bg_image(locker_config.bg_image)
|
||||
end
|
||||
)
|
||||
|
||||
-- Regenerate lockscreens and its background if a screen was removed to avoid errors
|
||||
screen.connect_signal(
|
||||
'removed',
|
||||
function(s)
|
||||
create_lock_screens(s)
|
||||
apply_ls_bg_image(locker_config.bg_image)
|
||||
end
|
||||
)
|
||||
@@ -0,0 +1,246 @@
|
||||
local awful = require('awful')
|
||||
local gears = require('gears')
|
||||
local beautiful = require('beautiful')
|
||||
local apps = require('configuration.apps')
|
||||
local menubar = require('menubar')
|
||||
local awful_menu = awful.menu
|
||||
local menu_gen = menubar.menu_gen
|
||||
local menu_utils = menubar.utils
|
||||
local icon_theme = require('menubar.icon_theme')
|
||||
local hotkeys_popup = require('awful.hotkeys_popup').widget
|
||||
|
||||
local terminal = apps.default.terminal
|
||||
local web_browser = apps.default.web_browser
|
||||
local file_manager = apps.default.file_manager
|
||||
local text_editor = apps.default.text_editor
|
||||
local editor_cmd = terminal .. ' -e ' .. (os.getenv('EDITOR') or 'nano')
|
||||
|
||||
--[[
|
||||
|
||||
Awesome-Freedesktop
|
||||
Freedesktop.org compliant desktop entries and menu
|
||||
|
||||
Menu section
|
||||
|
||||
Licensed under GNU General Public License v2
|
||||
* (c) 2016, Luke Bonham
|
||||
* (c) 2014, Harvey Mittens
|
||||
|
||||
--]]
|
||||
|
||||
local io, pairs, string, table, os = io, pairs, string, table, os
|
||||
|
||||
-- Expecting a wm_name of awesome omits too many applications and tools
|
||||
menu_utils.wm_name = ''
|
||||
|
||||
-- Menu table
|
||||
local menu = {}
|
||||
|
||||
-- Determines if a path points to a directory, by checking if it can be read
|
||||
-- (which is `nil` also for empty files) and if its size is not 0.
|
||||
-- @author blueyed
|
||||
-- @param path the path to check
|
||||
menu.is_dir = function(path)
|
||||
local f = io.open(path)
|
||||
return f and not f:read(0) and f:seek('end') ~= 0 and f:close()
|
||||
end
|
||||
|
||||
-- Remove non existent paths in order to avoid issues
|
||||
local existent_paths = {}
|
||||
for k,v in pairs(menu_gen.all_menu_dirs) do
|
||||
if menu.is_dir(v) then
|
||||
table.insert(existent_paths, v)
|
||||
end
|
||||
end
|
||||
menu_gen.all_menu_dirs = existent_paths
|
||||
|
||||
-- Determines whether an table includes a certain element
|
||||
-- @param tab a given table
|
||||
-- @param val the element to search for
|
||||
-- @return true if the given string is found within the search table; otherwise, false if not
|
||||
menu.has_value = function(tab, val)
|
||||
for index, value in pairs(tab) do
|
||||
if val:find(value) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Use MenuBar parsing utils to build a menu for Awesome
|
||||
-- @return awful.menu
|
||||
menu.build = function(args)
|
||||
local args = args or {}
|
||||
local icon_size = args.icon_size
|
||||
local before = args.before or {}
|
||||
local after = args.after or {}
|
||||
local skip_items = args.skip_items or {}
|
||||
local sub_menu = args.sub_menu or false
|
||||
|
||||
local result = {}
|
||||
local _menu = awful_menu({ items = before })
|
||||
|
||||
menu_gen.generate(function(entries)
|
||||
-- Add category icons
|
||||
for k, v in pairs(menu_gen.all_categories) do
|
||||
table.insert(result, { k, {}, v.icon })
|
||||
end
|
||||
|
||||
-- Get items table
|
||||
for k, v in pairs(entries) do
|
||||
for _, cat in pairs(result) do
|
||||
if cat[1] == v.category then
|
||||
if not menu.has_value(skip_items, v.name) then
|
||||
table.insert(cat[2], { v.name, v.cmdline, v.icon })
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Cleanup things a bit
|
||||
for i = #result, 1, -1 do
|
||||
local v = result[i]
|
||||
if #v[2] == 0 then
|
||||
-- Remove unused categories
|
||||
table.remove(result, i)
|
||||
else
|
||||
--Sort entries alphabetically (by name)
|
||||
table.sort(v[2], function (a, b) return string.byte(a[1]) < string.byte(b[1]) end)
|
||||
-- Replace category name with nice name
|
||||
v[1] = menu_gen.all_categories[v[1]].name
|
||||
end
|
||||
end
|
||||
|
||||
-- Sort categories alphabetically also
|
||||
table.sort(result, function(a, b) return string.byte(a[1]) < string.byte(b[1]) end)
|
||||
|
||||
-- Add menu item to hold the generated menu
|
||||
if sub_menu then
|
||||
result = {{sub_menu, result}}
|
||||
end
|
||||
|
||||
-- Add items to menu
|
||||
for _, v in pairs(result) do _menu:add(v) end
|
||||
for _, v in pairs(after) do _menu:add(v) end
|
||||
end)
|
||||
|
||||
-- Set icon size
|
||||
if icon_size then
|
||||
for _,v in pairs(menu_gen.all_categories) do
|
||||
v.icon = icon_theme():find_icon_path(v.icon_name, icon_size)
|
||||
end
|
||||
end
|
||||
|
||||
-- Hold the menu in the module
|
||||
menu.menu = _menu
|
||||
|
||||
return _menu
|
||||
end
|
||||
|
||||
-- Create a launcher widget and a main menu
|
||||
awesome_menu = {
|
||||
{
|
||||
'Hotkeys',
|
||||
function()
|
||||
hotkeys_popup.show_help(nil, awful.screen.focused())
|
||||
end,
|
||||
menubar.utils.lookup_icon('keyboard')
|
||||
},
|
||||
{
|
||||
'Edit config',
|
||||
editor_cmd .. ' ' .. awesome.conffile,
|
||||
menubar.utils.lookup_icon('accessories-text-editor')
|
||||
},
|
||||
{
|
||||
'Restart',
|
||||
awesome.restart,
|
||||
menubar.utils.lookup_icon('system-restart')
|
||||
},
|
||||
{
|
||||
'Quit',
|
||||
function() awesome.quit() end,
|
||||
menubar.utils.lookup_icon('system-log-out')
|
||||
}
|
||||
}
|
||||
|
||||
local default_app_menu = {
|
||||
{
|
||||
'Terminal',
|
||||
terminal,
|
||||
menubar.utils.lookup_icon('utilities-terminal')
|
||||
},
|
||||
{
|
||||
'Web browser',
|
||||
web_browser,
|
||||
menubar.utils.lookup_icon('webbrowser-app')
|
||||
},
|
||||
{
|
||||
'File Manager',
|
||||
file_manager,
|
||||
menubar.utils.lookup_icon('system-file-manager')
|
||||
},
|
||||
{
|
||||
'Text Editor',
|
||||
text_editor,
|
||||
menubar.utils.lookup_icon('accessories-text-editor')
|
||||
}
|
||||
}
|
||||
|
||||
-- Screenshot menu
|
||||
local screenshot_menu = {
|
||||
{
|
||||
'Full',
|
||||
function()
|
||||
gears.timer.start_new(
|
||||
0.1,
|
||||
function()
|
||||
awful.spawn.easy_async_with_shell(apps.utils.full_screenshot)
|
||||
end
|
||||
)
|
||||
end,
|
||||
menubar.utils.lookup_icon('accessories-screenshot')
|
||||
},
|
||||
{
|
||||
'Area',
|
||||
function()
|
||||
gears.timer.start_new(
|
||||
0.1,
|
||||
function()
|
||||
awful.spawn.easy_async_with_shell(apps.utils.area_screenshot)
|
||||
end,
|
||||
menubar.utils.lookup_icon('accessories-screenshot')
|
||||
)
|
||||
end,
|
||||
menubar.utils.lookup_icon('accessories-screenshot')
|
||||
}
|
||||
}
|
||||
|
||||
local tools_menu = {
|
||||
{
|
||||
'Awesome',
|
||||
awesome_menu,
|
||||
beautiful.awesome_icon
|
||||
},
|
||||
{
|
||||
'Take a Screenshot',
|
||||
screenshot_menu,
|
||||
menubar.utils.lookup_icon('accessories-screenshot')
|
||||
},
|
||||
{
|
||||
'End Session',
|
||||
function()
|
||||
awesome.emit_signal('module::exit_screen:show')
|
||||
end,
|
||||
menubar.utils.lookup_icon('system-shutdown')
|
||||
}
|
||||
}
|
||||
|
||||
mymainmenu = menu.build({
|
||||
-- Not actually the size, but the quality of the icon
|
||||
icon_size = 48,
|
||||
before = default_app_menu,
|
||||
after = tools_menu
|
||||
})
|
||||
|
||||
mylauncher = awful.widget.launcher({image = beautiful.awesome_icon, menu = mymainmenu})
|
||||
@@ -0,0 +1,257 @@
|
||||
local gears = require('gears')
|
||||
local wibox = require('wibox')
|
||||
local awful = require('awful')
|
||||
local ruled = require('ruled')
|
||||
local naughty = require('naughty')
|
||||
local menubar = require('menubar')
|
||||
local beautiful = require('beautiful')
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
local clickable_container = require('widget.clickable-container')
|
||||
|
||||
-- Defaults
|
||||
naughty.config.defaults.ontop = true
|
||||
naughty.config.defaults.icon_size = dpi(32)
|
||||
naughty.config.defaults.timeout = 5
|
||||
naughty.config.defaults.title = 'System Notification'
|
||||
naughty.config.defaults.margin = dpi(16)
|
||||
naughty.config.defaults.border_width = 0
|
||||
naughty.config.defaults.position = 'top_right'
|
||||
naughty.config.defaults.shape = function(cr, w, h)
|
||||
gears.shape.rounded_rect(cr, w, h, dpi(6))
|
||||
end
|
||||
|
||||
-- Apply theme variables
|
||||
naughty.config.padding = dpi(8)
|
||||
naughty.config.spacing = dpi(8)
|
||||
naughty.config.icon_dirs = {
|
||||
'/usr/share/icons/Tela',
|
||||
'/usr/share/icons/Tela-blue-dark',
|
||||
'/usr/share/icons/Papirus/',
|
||||
'/usr/share/icons/la-capitaine-icon-theme/',
|
||||
'/usr/share/icons/gnome/',
|
||||
'/usr/share/icons/hicolor/',
|
||||
'/usr/share/pixmaps/'
|
||||
}
|
||||
naughty.config.icon_formats = { 'svg', 'png', 'jpg', 'gif' }
|
||||
|
||||
|
||||
-- Presets / rules
|
||||
|
||||
ruled.notification.connect_signal(
|
||||
'request::rules',
|
||||
function()
|
||||
|
||||
-- Critical notifs
|
||||
ruled.notification.append_rule {
|
||||
rule = { urgency = 'critical' },
|
||||
properties = {
|
||||
font = 'Inter Bold 10',
|
||||
bg = '#ff0000',
|
||||
fg = '#ffffff',
|
||||
margin = dpi(16),
|
||||
position = 'top_right',
|
||||
implicit_timeout = 0
|
||||
}
|
||||
}
|
||||
|
||||
-- Normal notifs
|
||||
ruled.notification.append_rule {
|
||||
rule = { urgency = 'normal' },
|
||||
properties = {
|
||||
font = 'Inter Regular 10',
|
||||
bg = beautiful.transparent,
|
||||
fg = beautiful.fg_normal,
|
||||
margin = dpi(16),
|
||||
position = 'top_right',
|
||||
implicit_timeout = 5
|
||||
}
|
||||
}
|
||||
|
||||
-- Low notifs
|
||||
ruled.notification.append_rule {
|
||||
rule = { urgency = 'low' },
|
||||
properties = {
|
||||
font = 'Inter Regular 10',
|
||||
bg = beautiful.transparent,
|
||||
fg = beautiful.fg_normal,
|
||||
margin = dpi(16),
|
||||
position = 'top_right',
|
||||
implicit_timeout = 5
|
||||
}
|
||||
}
|
||||
end
|
||||
)
|
||||
|
||||
-- Error handling
|
||||
naughty.connect_signal(
|
||||
'request::display_error',
|
||||
function(message, startup)
|
||||
naughty.notification {
|
||||
urgency = 'critical',
|
||||
title = 'Oops, an error happened'..(startup and ' during startup!' or '!'),
|
||||
message = message,
|
||||
app_name = 'System Notification',
|
||||
icon = beautiful.awesome_icon
|
||||
}
|
||||
end
|
||||
)
|
||||
|
||||
-- XDG icon lookup
|
||||
naughty.connect_signal(
|
||||
'request::icon',
|
||||
function(n, context, hints)
|
||||
if context ~= 'app_icon' then return end
|
||||
|
||||
local path = menubar.utils.lookup_icon(hints.app_icon) or
|
||||
menubar.utils.lookup_icon(hints.app_icon:lower())
|
||||
|
||||
if path then
|
||||
n.icon = path
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
-- Connect to naughty on display signal
|
||||
naughty.connect_signal(
|
||||
'request::display',
|
||||
function(n)
|
||||
|
||||
-- Actions Blueprint
|
||||
local actions_template = wibox.widget {
|
||||
notification = n,
|
||||
base_layout = wibox.widget {
|
||||
spacing = dpi(0),
|
||||
layout = wibox.layout.flex.horizontal
|
||||
},
|
||||
widget_template = {
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
id = 'text_role',
|
||||
font = 'Inter Regular 10',
|
||||
widget = wibox.widget.textbox
|
||||
},
|
||||
widget = wibox.container.place
|
||||
},
|
||||
widget = clickable_container
|
||||
},
|
||||
bg = beautiful.groups_bg,
|
||||
shape = gears.shape.rounded_rect,
|
||||
forced_height = dpi(30),
|
||||
widget = wibox.container.background
|
||||
},
|
||||
margins = dpi(4),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
style = { underline_normal = false, underline_selected = true },
|
||||
widget = naughty.list.actions
|
||||
}
|
||||
|
||||
-- Notifbox Blueprint
|
||||
naughty.layout.box {
|
||||
notification = n,
|
||||
type = 'notification',
|
||||
screen = awful.screen.preferred(),
|
||||
shape = gears.shape.rectangle,
|
||||
widget_template = {
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
{
|
||||
markup = n.app_name or 'System Notification',
|
||||
font = 'Inter Bold 10',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
|
||||
},
|
||||
margins = beautiful.notification_margin,
|
||||
widget = wibox.container.margin,
|
||||
},
|
||||
bg = beautiful.background,
|
||||
widget = wibox.container.background,
|
||||
},
|
||||
{
|
||||
{
|
||||
{
|
||||
resize_strategy = 'center',
|
||||
widget = naughty.widget.icon,
|
||||
},
|
||||
margins = beautiful.notification_margin,
|
||||
widget = wibox.container.margin,
|
||||
},
|
||||
{
|
||||
{
|
||||
layout = wibox.layout.align.vertical,
|
||||
expand = 'none',
|
||||
nil,
|
||||
{
|
||||
{
|
||||
align = 'left',
|
||||
widget = naughty.widget.title
|
||||
},
|
||||
{
|
||||
align = 'left',
|
||||
widget = naughty.widget.message,
|
||||
},
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
nil
|
||||
},
|
||||
margins = beautiful.notification_margin,
|
||||
widget = wibox.container.margin,
|
||||
},
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
},
|
||||
fill_space = true,
|
||||
spacing = beautiful.notification_margin,
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
},
|
||||
-- Margin between the fake background
|
||||
-- Set to 0 to preserve the 'titlebar' effect
|
||||
margins = dpi(0),
|
||||
widget = wibox.container.margin,
|
||||
},
|
||||
bg = beautiful.transparent,
|
||||
widget = wibox.container.background,
|
||||
},
|
||||
-- Actions
|
||||
actions_template,
|
||||
spacing = dpi(4),
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
},
|
||||
bg = beautiful.transparent,
|
||||
id = 'background_role',
|
||||
widget = naughty.container.background,
|
||||
},
|
||||
strategy = 'min',
|
||||
width = dpi(160),
|
||||
widget = wibox.container.constraint,
|
||||
},
|
||||
strategy = 'max',
|
||||
width = beautiful.notification_max_width or dpi(500),
|
||||
widget = wibox.container.constraint
|
||||
},
|
||||
bg = beautiful.background,
|
||||
shape = gears.shape.rounded_rect,
|
||||
widget = wibox.container.background
|
||||
}
|
||||
}
|
||||
|
||||
-- Destroy popups if dont_disturb mode is on
|
||||
-- Or if the right_panel is visible
|
||||
local focused = awful.screen.focused()
|
||||
if _G.dont_disturb or
|
||||
(focused.right_panel and focused.right_panel.visible) then
|
||||
naughty.destroy_all_notifications(nil, 1)
|
||||
end
|
||||
|
||||
end
|
||||
)
|
||||
@@ -0,0 +1,112 @@
|
||||
local awful = require('awful')
|
||||
local ruled = require('ruled')
|
||||
local beautiful = require('beautiful')
|
||||
local app = require('configuration.apps').default.quake
|
||||
local client_keys = require('configuration.client.keys')
|
||||
local client_buttons = require('configuration.client.buttons')
|
||||
local quake_id = nil
|
||||
local quake_client = nil
|
||||
local quake_opened = false
|
||||
|
||||
local quake_properties = function()
|
||||
return {
|
||||
skip_decoration = true,
|
||||
titlebars_enabled = false,
|
||||
switch_to_tags = false,
|
||||
opacity = 0.95,
|
||||
floating = true,
|
||||
skip_taskbar = true,
|
||||
ontop = true,
|
||||
above = true,
|
||||
sticky = true,
|
||||
hidden = not quake_opened,
|
||||
maximized_horizontal = true,
|
||||
skip_center = true,
|
||||
round_corners = false,
|
||||
keys = client_keys,
|
||||
buttons = client_buttons,
|
||||
placement = awful.placement.top,
|
||||
shape = beautiful.client_shape_rectangle
|
||||
}
|
||||
end
|
||||
|
||||
ruled.client.connect_signal(
|
||||
'request::rules',
|
||||
function()
|
||||
ruled.client.append_rule {
|
||||
id = 'quake_terminal',
|
||||
rule_any = {
|
||||
instance = {
|
||||
'QuakeTerminal'
|
||||
}
|
||||
},
|
||||
properties = quake_properties()
|
||||
}
|
||||
end
|
||||
)
|
||||
|
||||
local create_quake = function()
|
||||
-- Check if there's already an instance of 'QuakeTerminal'.
|
||||
-- If yes, recover it - use it again.
|
||||
local quake_term = function (c)
|
||||
return ruled.client.match(c, {instance = 'QuakeTerminal'})
|
||||
end
|
||||
for c in awful.client.iterate(quake_term) do
|
||||
-- 'QuakeTerminal' instance detected
|
||||
-- Re-apply its properties
|
||||
ruled.client.execute(c, quake_properties())
|
||||
quake_id = c.pid
|
||||
c:emit_signal('request::activate')
|
||||
return
|
||||
end
|
||||
-- No 'QuakeTerminal' instance, spawn one
|
||||
quake_id = awful.spawn(app, quake_properties())
|
||||
end
|
||||
|
||||
local quake_open = function()
|
||||
quake_client.hidden = false
|
||||
quake_client:emit_signal('request::activate')
|
||||
end
|
||||
|
||||
local quake_close = function()
|
||||
quake_client.hidden = true
|
||||
end
|
||||
|
||||
local quake_toggle = function()
|
||||
quake_opened = not quake_opened
|
||||
if not quake_client then
|
||||
create_quake()
|
||||
else
|
||||
if quake_opened then
|
||||
quake_open()
|
||||
else
|
||||
quake_close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::quake_terminal:toggle',
|
||||
function()
|
||||
quake_toggle();
|
||||
end
|
||||
)
|
||||
|
||||
client.connect_signal(
|
||||
'manage',
|
||||
function(c)
|
||||
if c.pid == quake_id then
|
||||
quake_client = c
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
client.connect_signal(
|
||||
'unmanage',
|
||||
function(c)
|
||||
if c.pid == quake_id then
|
||||
quake_opened = false
|
||||
quake_client = nil
|
||||
end
|
||||
end
|
||||
)
|
||||
@@ -0,0 +1,278 @@
|
||||
local awful = require('awful')
|
||||
local gears = require('gears')
|
||||
local beautiful = require('beautiful')
|
||||
local wibox = require('wibox')
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
awful.titlebar.enable_tooltip = true
|
||||
awful.titlebar.fallback_name = 'Client'
|
||||
|
||||
local double_click_event_handler = function(double_click_event)
|
||||
if double_click_timer then
|
||||
double_click_timer:stop()
|
||||
double_click_timer = nil
|
||||
double_click_event()
|
||||
return
|
||||
end
|
||||
double_click_timer = gears.timer.start_new(
|
||||
0.20,
|
||||
function()
|
||||
double_click_timer = nil
|
||||
return false
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
local create_click_events = function(c)
|
||||
-- Titlebar button/click events
|
||||
local buttons = gears.table.join(
|
||||
awful.button(
|
||||
{},
|
||||
1,
|
||||
function()
|
||||
double_click_event_handler(function()
|
||||
if c.floating then
|
||||
c.floating = false
|
||||
return
|
||||
end
|
||||
c.maximized = not c.maximized
|
||||
c:raise()
|
||||
return
|
||||
end)
|
||||
c:activate {context = 'titlebar', action = 'mouse_move'}
|
||||
end
|
||||
),
|
||||
awful.button(
|
||||
{},
|
||||
3,
|
||||
function()
|
||||
c:activate {context = 'titlebar', action = 'mouse_resize'}
|
||||
end
|
||||
)
|
||||
)
|
||||
return buttons
|
||||
end
|
||||
|
||||
local create_vertical_bar = function(c, pos, bg, size)
|
||||
|
||||
-- Check if passed position is valid
|
||||
if (pos == 'top' or pos == 'bottom') then
|
||||
pos = 'left'
|
||||
bg = '#FF00FF'
|
||||
end
|
||||
|
||||
awful.titlebar(c, {position = pos, bg = bg, size = size}) : setup {
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.closebutton(c),
|
||||
awful.titlebar.widget.maximizedbutton(c),
|
||||
awful.titlebar.widget.minimizebutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
{
|
||||
buttons = create_click_events(c),
|
||||
layout = wibox.layout.flex.vertical
|
||||
},
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.ontopbutton(c),
|
||||
awful.titlebar.widget.floatingbutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
layout = wibox.layout.align.vertical
|
||||
}
|
||||
end
|
||||
|
||||
local create_horizontal_bar = function(c, pos, bg, size)
|
||||
|
||||
-- Check if passed position is valid
|
||||
if (pos == 'left' or pos == 'right') then
|
||||
pos = 'top'
|
||||
bg = '#FF00FF'
|
||||
end
|
||||
|
||||
awful.titlebar(c, {position = pos, bg = bg, size = size}) : setup {
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.closebutton(c),
|
||||
awful.titlebar.widget.maximizedbutton(c),
|
||||
awful.titlebar.widget.minimizebutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
{
|
||||
buttons = create_click_events(c),
|
||||
layout = wibox.layout.flex.horizontal
|
||||
},
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.ontopbutton(c),
|
||||
awful.titlebar.widget.floatingbutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
layout = wibox.layout.align.horizontal
|
||||
}
|
||||
end
|
||||
|
||||
local create_vertical_bar_dialog = function(c, pos, bg, size)
|
||||
|
||||
-- Check if passed position is valid
|
||||
if (pos == 'top' or pos == 'bottom') then
|
||||
pos = 'left'
|
||||
bg = '#FF00FF'
|
||||
end
|
||||
|
||||
awful.titlebar(c, {position = pos, bg = bg, size = size}) : setup {
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.closebutton(c),
|
||||
awful.titlebar.widget.minimizebutton(c),
|
||||
awful.titlebar.widget.ontopbutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
{
|
||||
buttons = create_click_events(c),
|
||||
layout = wibox.layout.flex.vertical
|
||||
},
|
||||
nil,
|
||||
layout = wibox.layout.align.vertical
|
||||
}
|
||||
end
|
||||
|
||||
local create_horizontal_bar_dialog = function(c, pos, bg, size)
|
||||
|
||||
-- Check if passed position is valid
|
||||
if (pos == 'left' or pos == 'right') then
|
||||
pos = 'top'
|
||||
bg = '#FF00FF'
|
||||
end
|
||||
|
||||
awful.titlebar(c, {position = pos, bg = bg, size = size}) : setup {
|
||||
{
|
||||
{
|
||||
awful.titlebar.widget.closebutton(c),
|
||||
awful.titlebar.widget.ontopbutton(c),
|
||||
awful.titlebar.widget.minimizebutton(c),
|
||||
spacing = dpi(7),
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
},
|
||||
margins = dpi(10),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
{
|
||||
buttons = create_click_events(c),
|
||||
layout = wibox.layout.flex.horizontal
|
||||
},
|
||||
nil,
|
||||
layout = wibox.layout.align.horizontal
|
||||
}
|
||||
end
|
||||
|
||||
client.connect_signal(
|
||||
'request::titlebars',
|
||||
function(c)
|
||||
|
||||
-- Customize here
|
||||
if c.type == 'normal' then
|
||||
|
||||
if c.class == 'kitty' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'firefox' then
|
||||
create_vertical_bar(c, 'left', beautiful.background, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'XTerm' or c.class == 'UXTerm' then
|
||||
create_horizontal_bar(c, 'top',
|
||||
beautiful.xresources.get_current_theme().background, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'ark' or c.class == 'dolphin' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
elseif c.instance == 'transmission-qt' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Gimp-2.10' or c.class == 'Inkscape' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Com.github.johnfactotum.Foliate' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Arandr' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Ettercap' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().base_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Google-chrome' or c.class == 'Chromium' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().base_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'TelegramDesktop' then
|
||||
create_vertical_bar(c, 'left', '#17212b', beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Kvantum Manager' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'qt5ct' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Nemo' then
|
||||
create_horizontal_bar(c, 'top',
|
||||
beautiful.gtk.get_theme_variables().base_color, beautiful.titlebar_size)
|
||||
|
||||
else
|
||||
create_vertical_bar(c, 'left', beautiful.background, beautiful.titlebar_size)
|
||||
end
|
||||
|
||||
elseif c.type == 'dialog' then
|
||||
|
||||
if c.role == 'GtkFileChooserDialog' then
|
||||
create_vertical_bar_dialog(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'firefox' then
|
||||
create_vertical_bar_dialog(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Gimp-2.10' then
|
||||
create_vertical_bar_dialog(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
elseif c.class == 'Arandr' then
|
||||
create_vertical_bar(c, 'left',
|
||||
beautiful.gtk.get_theme_variables().bg_color, beautiful.titlebar_size)
|
||||
|
||||
else
|
||||
create_vertical_bar_dialog(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
end
|
||||
|
||||
elseif c.type == 'modal' then
|
||||
create_vertical_bar(c, 'left', '#00000099', beautiful.titlebar_size)
|
||||
|
||||
else
|
||||
create_vertical_bar(c, 'left', beautiful.background, beautiful.titlebar_size)
|
||||
end
|
||||
end
|
||||
)
|
||||
@@ -0,0 +1,268 @@
|
||||
local awful = require('awful')
|
||||
local gears = require('gears')
|
||||
local wibox = require('wibox')
|
||||
local beautiful = require('beautiful')
|
||||
local dpi = beautiful.xresources.apply_dpi
|
||||
local clickable_container = require('widget.clickable-container')
|
||||
local icons = require('theme.icons')
|
||||
|
||||
local osd_header = wibox.widget {
|
||||
text = 'Volume',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'left',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local osd_value = wibox.widget {
|
||||
text = '0%',
|
||||
font = 'Inter Bold 12',
|
||||
align = 'center',
|
||||
valign = 'center',
|
||||
widget = wibox.widget.textbox
|
||||
}
|
||||
|
||||
local slider_osd = wibox.widget {
|
||||
nil,
|
||||
{
|
||||
id = 'vol_osd_slider',
|
||||
bar_shape = gears.shape.rounded_rect,
|
||||
bar_height = dpi(2),
|
||||
bar_color = '#ffffff20',
|
||||
bar_active_color = '#f2f2f2EE',
|
||||
handle_color = '#ffffff',
|
||||
handle_shape = gears.shape.circle,
|
||||
handle_width = dpi(15),
|
||||
handle_border_color = '#00000012',
|
||||
handle_border_width = dpi(1),
|
||||
maximum = 100,
|
||||
widget = wibox.widget.slider
|
||||
},
|
||||
nil,
|
||||
expand = 'none',
|
||||
layout = wibox.layout.align.vertical
|
||||
}
|
||||
|
||||
local vol_osd_slider = slider_osd.vol_osd_slider
|
||||
|
||||
vol_osd_slider:connect_signal(
|
||||
'property::value',
|
||||
function()
|
||||
local volume_level = vol_osd_slider:get_value()
|
||||
awful.spawn('amixer -D pulse sset Master ' .. volume_level .. '%', false)
|
||||
|
||||
-- Update textbox widget text
|
||||
osd_value.text = volume_level .. '%'
|
||||
|
||||
-- Update the volume slider if values here change
|
||||
awesome.emit_signal('widget::volume:update', volume_level)
|
||||
|
||||
if awful.screen.focused().show_vol_osd then
|
||||
awesome.emit_signal(
|
||||
'module::volume_osd:show',
|
||||
true
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
vol_osd_slider:connect_signal(
|
||||
'button::press',
|
||||
function()
|
||||
awful.screen.focused().show_vol_osd = true
|
||||
end
|
||||
)
|
||||
|
||||
vol_osd_slider:connect_signal(
|
||||
'mouse::enter',
|
||||
function()
|
||||
awful.screen.focused().show_vol_osd = true
|
||||
end
|
||||
)
|
||||
|
||||
-- The emit will come from the volume-slider
|
||||
awesome.connect_signal(
|
||||
'module::volume_osd',
|
||||
function(volume)
|
||||
vol_osd_slider:set_value(volume)
|
||||
end
|
||||
)
|
||||
|
||||
local icon = wibox.widget {
|
||||
{
|
||||
image = icons.volume,
|
||||
resize = true,
|
||||
widget = wibox.widget.imagebox
|
||||
},
|
||||
top = dpi(12),
|
||||
bottom = dpi(12),
|
||||
widget = wibox.container.margin
|
||||
}
|
||||
|
||||
local volume_slider_osd = wibox.widget {
|
||||
icon,
|
||||
slider_osd,
|
||||
spacing = dpi(24),
|
||||
layout = wibox.layout.fixed.horizontal
|
||||
}
|
||||
|
||||
local osd_height = dpi(100)
|
||||
local osd_width = dpi(300)
|
||||
local osd_margin = dpi(10)
|
||||
|
||||
screen.connect_signal(
|
||||
'request::desktop_decoration',
|
||||
function(s)
|
||||
local s = s or {}
|
||||
s.show_vol_osd = false
|
||||
|
||||
-- Create the box
|
||||
s.volume_osd_overlay = awful.popup {
|
||||
widget = {
|
||||
-- Removing this block will cause an error...
|
||||
},
|
||||
ontop = true,
|
||||
visible = false,
|
||||
type = 'notification',
|
||||
screen = s,
|
||||
height = osd_height,
|
||||
width = osd_width,
|
||||
maximum_height = osd_height,
|
||||
maximum_width = osd_width,
|
||||
offset = dpi(5),
|
||||
shape = gears.shape.rectangle,
|
||||
bg = beautiful.transparent,
|
||||
preferred_anchors = 'middle',
|
||||
preferred_positions = {'left', 'right', 'top', 'bottom'}
|
||||
}
|
||||
|
||||
s.volume_osd_overlay : setup {
|
||||
{
|
||||
{
|
||||
{
|
||||
layout = wibox.layout.align.horizontal,
|
||||
expand = 'none',
|
||||
forced_height = dpi(48),
|
||||
osd_header,
|
||||
nil,
|
||||
osd_value
|
||||
},
|
||||
volume_slider_osd,
|
||||
layout = wibox.layout.fixed.vertical
|
||||
},
|
||||
left = dpi(24),
|
||||
right = dpi(24),
|
||||
widget = wibox.container.margin
|
||||
},
|
||||
bg = beautiful.background,
|
||||
shape = gears.shape.rounded_rect,
|
||||
widget = wibox.container.background()
|
||||
}
|
||||
|
||||
-- Reset timer on mouse hover
|
||||
s.volume_osd_overlay:connect_signal(
|
||||
'mouse::enter',
|
||||
function()
|
||||
awful.screen.focused().show_vol_osd = true
|
||||
awesome.emit_signal('module::volume_osd:rerun')
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
|
||||
local hide_osd = gears.timer {
|
||||
timeout = 2,
|
||||
autostart = true,
|
||||
callback = function()
|
||||
local focused = awful.screen.focused()
|
||||
focused.volume_osd_overlay.visible = false
|
||||
focused.show_vol_osd = false
|
||||
end
|
||||
}
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::volume_osd:rerun',
|
||||
function()
|
||||
if hide_osd.started then
|
||||
hide_osd:again()
|
||||
else
|
||||
hide_osd:start()
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local placement_placer = function()
|
||||
local focused = awful.screen.focused()
|
||||
|
||||
local right_panel = focused.right_panel
|
||||
local left_panel = focused.left_panel
|
||||
local volume_osd = focused.volume_osd_overlay
|
||||
|
||||
if right_panel and left_panel then
|
||||
if right_panel.visible then
|
||||
awful.placement.bottom_left(
|
||||
volume_osd,
|
||||
{
|
||||
margins = {
|
||||
left = osd_margin,
|
||||
right = 0,
|
||||
top = 0,
|
||||
bottom = osd_margin
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if right_panel then
|
||||
if right_panel.visible then
|
||||
awful.placement.bottom_left(
|
||||
volume_osd,
|
||||
{
|
||||
margins = {
|
||||
left = osd_margin,
|
||||
right = 0,
|
||||
top = 0,
|
||||
bottom = osd_margin
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
awful.placement.bottom_right(
|
||||
volume_osd,
|
||||
{
|
||||
margins = {
|
||||
left = 0,
|
||||
right = osd_margin,
|
||||
top = 0,
|
||||
bottom = osd_margin,
|
||||
},
|
||||
honor_workarea = true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
awesome.connect_signal(
|
||||
'module::volume_osd:show',
|
||||
function(bool)
|
||||
placement_placer()
|
||||
awful.screen.focused().volume_osd_overlay.visible = bool
|
||||
if bool then
|
||||
awesome.emit_signal('module::volume_osd:rerun')
|
||||
awesome.emit_signal(
|
||||
'module::brightness_osd:show',
|
||||
false
|
||||
)
|
||||
else
|
||||
if hide_osd.started then
|
||||
hide_osd:stop()
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
Reference in new issue
Block a user