Initial commit

This commit is contained in:
jorraan committed 2021-11-29 19:57:00 +11:00
1 parent ed88af090f
commit 05c9c7fc71
448 files changed
+46802

No files matched your search

+36
View File
@@ -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
+268
View File
@@ -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
)
+364
View File
@@ -0,0 +1,364 @@
----------------------------------------------------------------------------
--- Wallpaper changer module
--
-- @author Gerome Matilla &lt;gerome.matilla07@gmail.com&gt;
-- @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
)
+360
View File
@@ -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
)
+849
View File
@@ -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
)
+246
View File
@@ -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})
+257
View File
@@ -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
)
+112
View File
@@ -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
)
+278
View File
@@ -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
)
+268
View File
@@ -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
)