Compare commits
No commits in common. "master" and "1.0" have entirely different histories.
|
@ -1,8 +1,4 @@
|
|||
/desktop.conf
|
||||
/wallpaper.txt
|
||||
/links
|
||||
/libs/pam
|
||||
/libs/pam.so
|
||||
/doc
|
||||
/macros
|
||||
/macros/*
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
file = {"libs","modules","widgets"}
|
||||
format = "markdown"
|
||||
title = "Reno Desktop documentation"
|
||||
description = "Reno Desktop is an AwesomeWM config that harnesses the power of AwesomeWM to bring the essential functionality of a desktop environment."
|
31
.luarc.json
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"diagnostics": {
|
||||
"enable": true,
|
||||
"globals": [
|
||||
"awesome",
|
||||
"button",
|
||||
"dbus",
|
||||
"client",
|
||||
"mouse",
|
||||
"screen",
|
||||
"root"
|
||||
]
|
||||
},
|
||||
"runtime": {
|
||||
"version": "Lua 5.3",
|
||||
"path": [
|
||||
"/usr/share/awesome/lib/?/?.lua"
|
||||
],
|
||||
"pathStrict": true
|
||||
},
|
||||
"workspace": {
|
||||
"library": [
|
||||
"runtime/lua",
|
||||
"/usr/share/awesome/lib"
|
||||
],
|
||||
"checkThirdParty": false,
|
||||
"maxPreload": 2000,
|
||||
"preloadFileSize": 1000
|
||||
},
|
||||
"telemetry.enable": true
|
||||
}
|
36
README.md
|
@ -1,54 +1,32 @@
|
|||
# Reno desktop - awesomewm || ( openbox && xfce )
|
||||
|
||||
Reno is an evolution of my previous configuration, https://512mb.org/git/Yessiest/awesome. This time, this bad boy can fit 90% more configuration options, through the use of AWMTK2 (or RenoTK, haven't really decided on the name).
|
||||
Reno is an evolution of my previous configuration, https://512mb.org/git/Yessiest/awesome. This time, this bad boy can fit 90% more configuration options, through the use of AWMTK2 (or RenoTK, never really decided on the name).
|
||||
|
||||
Through advancements in the ~~science~~ dark magic art of lua metatablery, AWMTK2 makes it possible to create complex and visually rich themes, while mainting relatively low memory footprint.
|
||||
|
||||
Thanks to the ethically questionable decision of employing JSON to Widget Layout conversion, this desktop allows for customization of panels, context menus, lock screens, and other widget containers of various sizes and shapes.
|
||||
Thanks to the questionably ethical decisions of employing JSON to Widget Layout conversion, this desktop allows for customization of panels, context menus, lock screens, and other widget containers of various sizes and shapes.
|
||||
|
||||
## Screenshots
|
||||
|
||||
#### Current default theme, Reno98
|
||||
![Totally not windows 98 trust me it's legally distinct](https://adastra7.net/git/Yessiest/reno/raw/branch/master/extra/screenshots/reno98.png)
|
||||
|
||||
- Works best with [Chicago95](https://github.com/grassmunk/Chicago95) for GTK and [QTStep (QTStepWMakerDefault in particular)](https://github.com/andbgr/QTStep) on [Kvantum](https://github.com/tsujan/Kvantum).
|
||||
- For Wine apps, use `winecfg`, go to Desktop Integration and set "(No Theme)" under "Theme". A matching QTStep theme is "QTStepBeige", but it doesn't quite match Chicago95, so you may want to configure the window colors using the Item editor, or by installing a custom theme.
|
||||
|
||||
|
||||
#### Unity: a theme that mimics the look of Ubuntu with Ambiance theme
|
||||
![Totally not ubuntu guys how can't you see that](https://adastra7.net/git/Yessiest/reno/raw/branch/master/extra/screenshots/unity.png)
|
||||
|
||||
- Works best with official [Ubuntu Themes](https://launchpad.net/ubuntu-themes) ([AUR package](https://aur.archlinux.org/packages/ubuntu-themes)) for GTK and [Kvantum's](https://github.com/tsujan/Kvantum) default themes KvAmbiance or KvAmbience.
|
||||
- For Wine apps, [Ubuntu Light for Windows XP](http://freddi67.deviantart.com/art/Ubuntu-Light-for-Windows-XP-177514325) theme exists on DeviantArt, which you can install using `winecfg`.
|
||||
|
||||
![Totally not windows 98 trust me it's legally distinct](https://512mb.org/git/Yessiest/reno/raw/branch/master/extra/screenshots/reno98.png)
|
||||
## Installation
|
||||
|
||||
Reno internally requires [ImageMagick](https://imagemagick.org/) to generate thumbnails for images. Please consider installing ImageMagick for things to function correctly.
|
||||
The installation process is not much different from the previous iteration, except this time the window manager does not crash if you don't install luapam. Instead, the lock widget will simply refuse to operate.
|
||||
|
||||
1. `git clone` the repository to your .config folder
|
||||
2. Rename `reno` folder to `awesome`
|
||||
3. Install AwesomeWM (version 4.3 as of right now)
|
||||
4. Change your shell, terminal emulator, browser, keybindings and other settings in `desktop.conf`
|
||||
5. (Optional) Read additional installation steps in `extra/README.md`
|
||||
6. Reload your awesomewm if you have already loaded it.
|
||||
|
||||
## Keybindings and user guide
|
||||
- press win+s
|
||||
- read extra/README.md
|
||||
- enjoy
|
||||
|
||||
## Feedback
|
||||
|
||||
Your feedback would be highly appreciated. However, please understand that I may not be able to answer all of your issues in a timely manner.
|
||||
4. (Optional) Read additional installation steps in `extra/README.md`
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] Port widgets from original config to AWMTK2
|
||||
- [x] Finish Reno98 theme
|
||||
- [ ] Add a GTK configuration app
|
||||
- [x] Port Ubuntu themes from original config to AWMTK2
|
||||
- [ ] Port Ubuntu themes from original config to AWMTK2
|
||||
- [ ] Release docs and recommendations for theme and widget creation
|
||||
- [x] De-hardcode keybinding system
|
||||
- [ ] De-hardcode keybinding system
|
||||
|
||||
## License
|
||||
|
||||
|
|
73
Rakefile
|
@ -1,73 +0,0 @@
|
|||
task default: [:install]
|
||||
ConfigPath = (ENV["XDG_DATA_HOME"] or ENV["HOME"]+'/.config')
|
||||
|
||||
desc "Install (copy) config files"
|
||||
task "install-files" do
|
||||
base = ["libs","modules","themes","widgets","rc.lua","desktop.conf"]
|
||||
sh "mkdir -p #{ConfigPath}/awesome"
|
||||
base.each { |f|
|
||||
cp_r "./#{f}", "#{ConfigPath}/awesome"
|
||||
}
|
||||
end
|
||||
|
||||
desc "Install LuaPAM"
|
||||
task "install-luapam" do
|
||||
unless File.exist? "#{ConfigPath}/awesome/libs/pam.so"
|
||||
sh "sh ./extra/install_luapam.sh"
|
||||
cp "./pam.so","#{ConfigPath}/awesome/libs/"
|
||||
cp_r "./pam","#{ConfigPath}/awesome/libs/"
|
||||
rm "./pam.so"
|
||||
rm_rf "./pam"
|
||||
rm_rf "./lua-pam"
|
||||
else
|
||||
puts "LuaPAM already installed - skipping"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Force install config"
|
||||
task "install-force" => ["install-files","install-luapam"]
|
||||
|
||||
desc "Install config"
|
||||
task :install do
|
||||
installed = true
|
||||
base = ["libs","modules","themes","widgets","rc.lua","desktop.conf"]
|
||||
(base+["libs/pam.so"]).each { |f|
|
||||
installed &= File.exist? "#{ConfigPath}/awesome/#{f}"
|
||||
}
|
||||
if installed
|
||||
puts "Baseline files already installed - skipping"
|
||||
else
|
||||
Rake::Task["install-force"].invoke
|
||||
end
|
||||
end
|
||||
|
||||
desc "Build documentation"
|
||||
task :doc do
|
||||
sh "ldoc ./.ldoc.lua"
|
||||
end
|
||||
|
||||
desc "Install extras"
|
||||
task "install-extra" do
|
||||
cp "./extra/udev/backlight.rules", "/etc/udev/rules.d"
|
||||
mkdir "#{ConfigPath}/autostart"
|
||||
begin
|
||||
sh "sudo cp /usr/share/applications/picom.desktop #{ConfigPath}/autostart/"
|
||||
rescue
|
||||
puts "picom not installed - ignoring"
|
||||
else
|
||||
cp "./extra/picom.conf", "#{ConfigPath}"
|
||||
end
|
||||
puts "Done! Reload awesome to complete installation"
|
||||
end
|
||||
|
||||
desc "Uninstall from .config"
|
||||
task :clean do
|
||||
base = ["libs","modules","themes","widgets","rc.lua"]
|
||||
base.each { |x|
|
||||
rm_rf "#{ConfigPath}/awesome/"+x
|
||||
}
|
||||
end
|
||||
|
||||
desc "Wipe configuration and reinstall from scratch"
|
||||
task reinstall: [:clean,:install]
|
||||
|
111
desktop.conf
|
@ -1,111 +0,0 @@
|
|||
# Global variables
|
||||
[global]
|
||||
# Your preferred terminal emulator
|
||||
terminal = "xterm"
|
||||
# Your preferred browser (opens the first one available by default)
|
||||
browser = "xdg-open about:blank"
|
||||
# Your modkey (Mod4 = Super key (Win key))
|
||||
modkey = "Mod4"
|
||||
# Your theme (one of the themes available in ./themes/)
|
||||
theme = "unity"
|
||||
# Your shell (currently doesn't do much, preferrably should remain the same)
|
||||
shell = "bash"
|
||||
|
||||
# Keybindings
|
||||
# Format: <modifier>(+<modifier>+...)+<key> = "<command or :internal.function>"
|
||||
# "modkey" as modifier will be substituted for modkey variable in [global]
|
||||
[keys]
|
||||
"modkey+Left" = ":root.tag_next"
|
||||
"modkey+Right" = ":root.tag_prev"
|
||||
"modkey+j" = ":root.client_next"
|
||||
"modkey+k" = ":root.client_previous"
|
||||
"modkey+Control+j" = ":root.screen_next"
|
||||
"modkey+Control+k" = ":root.screen_previous"
|
||||
"modkey+Tab" = ":root.client_swap"
|
||||
"modkey+Return" = ":root.spawn_terminal"
|
||||
"modkey+Shift+Return" = ":root.spawn_browser"
|
||||
"modkey+Shift+y" = ":root.toggle_titlebars"
|
||||
|
||||
# Client keys only work if a focused client exists
|
||||
"modkey+Control+o" = ":client.move_to_screen"
|
||||
"modkey+Shift+c" = ":client.kill"
|
||||
"modkey+t" = ":client.cycle_screen"
|
||||
"modkey+o" = ":client.ontop"
|
||||
"modkey+b" = ":client.below"
|
||||
"modkey+f" = ":client.fullscreen"
|
||||
"modkey+n" = ":client.minimize"
|
||||
"modkey+m" = ":client.maximize"
|
||||
"modkey+p" = ":client.pin"
|
||||
"modkey+y" = ":client.toggle_titlebars"
|
||||
|
||||
# Widget keys
|
||||
"modkey+r" = ":dismal.run"
|
||||
"modkey+s" = ":help.show"
|
||||
"modkey+q" = ":client.menu"
|
||||
"modkey+x" = ":supermenu.open"
|
||||
|
||||
"Control+XF86AudioRaiseVolume" = ":client.volume_up"
|
||||
"Control+XF86AudioLowerVolume" = ":client.volume_down"
|
||||
"Control+XF86AudioMute" = ":client.volume_mute"
|
||||
"XF86AudioRaiseVolume" = ":root.volume_up"
|
||||
"XF86AudioLowerVolume" = ":root.volume_down"
|
||||
"XF86AudioMute" = ":root.volume_mute"
|
||||
"XF86MonBrightnessUp" = ":battery.brightness_up"
|
||||
"XF86MonBrightnessDown" = ":battery.brightness_down"
|
||||
"XF86AudioPlay" = ":mpc.play"
|
||||
"XF86AudioPrev" = ":mpc.prev"
|
||||
"XF86AudioNext" = ":mpc.next"
|
||||
|
||||
# Custom keys
|
||||
"Print" = "flameshot gui"
|
||||
"Shift+Print" = "flameshot launcher"
|
||||
|
||||
# Macro recording/playback keys
|
||||
KP_Divide = ":macro.play_1"
|
||||
KP_Multiply = ":macro.play_2"
|
||||
KP_Add = ":macro.record_1"
|
||||
KP_Subtract = ":macro.record_2"
|
||||
KP_Delete = ":macro.loop"
|
||||
|
||||
# Tiling
|
||||
"modkey+Shift+j" = ":layout.swap_next_client"
|
||||
"modkey+Shift+k" = ":layout.swap_prev_client"
|
||||
"modkey+Control+Return" = ":client.swap_to_master"
|
||||
"modkey+l" = ":layout.increase_master"
|
||||
"modkey+h" = ":layout.decrease_master"
|
||||
"modkey+Shift+l" = ":layout.increase_master_count"
|
||||
"modkey+Shift+h" = ":layout.decrease_master_count"
|
||||
"modkey+Control+l" = ":layout.increase_column_count"
|
||||
"modkey+Control+h" = ":layout.decrease_column_count"
|
||||
"modkey+[" = ":layout.next_layout"
|
||||
"modkey+]" = ":layout.prev_layout"
|
||||
|
||||
# Power manager module
|
||||
[powerman]
|
||||
# Bad battery condition warning threshold (in %)
|
||||
battery_quality_min = 33
|
||||
# Low battery warning threshold (in %)
|
||||
battery_capacity_min = 15
|
||||
# Process to execute on low battery
|
||||
on_low_battery = ""
|
||||
# Process to execute on fully charged battery
|
||||
on_charged_battery = ""
|
||||
# Process to execute on critical battery condition
|
||||
on_critical_condition = ""
|
||||
|
||||
# Simple compositor inhibiting
|
||||
[compositor]
|
||||
# Command to use to spawn compositor
|
||||
exec = "picom"
|
||||
|
||||
# Macro system
|
||||
[macros]
|
||||
step = 10
|
||||
|
||||
# Autostart system
|
||||
[autostart]
|
||||
# Enable minimization of apps with "Hidden" in their .desktop file
|
||||
minimize_enable = true
|
||||
# How long to target apps that should be minimized
|
||||
minimize_timeout = 120
|
||||
|
|
@ -11,11 +11,4 @@
|
|||
- install mpc
|
||||
- quick links
|
||||
- create `links` directory in the config directory
|
||||
- add .desktop files of apps you want to have on the quick launcher
|
||||
- anti-aliased window corners (unity theme only)
|
||||
- install picom
|
||||
- drop the picom.conf file into your .config folder
|
||||
- enable picom to run on wm boot (read autostart)
|
||||
- autostart
|
||||
- create "autostart" directory in .config folder
|
||||
- drop .desktop files (files in /usr/share/applications) of apps you want to run on wm boot
|
||||
- add .desktop files with of apps you want to show on the quick launcher
|
|
@ -16,11 +16,12 @@ if [ "$LUA_VERSION" == "" ]; then
|
|||
exit 1
|
||||
fi
|
||||
echo "Target version: $LUA_VERSION"
|
||||
git clone --recursive https://github.com/devurandom/lua-pam lua-pam
|
||||
cd lua-pam
|
||||
git clone --recursive https://github.com/devurandom/lua-pam ~/.config/awesome/lua-pam
|
||||
cd ~/.config/awesome/lua-pam
|
||||
make LUA_VERSION="$LUA_VERSION"
|
||||
mkdir -p ../pam/
|
||||
cp LICENSE ../pam/
|
||||
echo "https://github.com/devurandom/lua-pam" > ../pam/README
|
||||
cp pam.so ../
|
||||
mkdir -p ~/.config/awesome/libs/pam/
|
||||
cp LICENSE ~/.config/awesome/libs/pam/
|
||||
echo "https://github.com/devurandom/lua-pam" > ~/.config/awesome/libs/pam/README
|
||||
cp pam.so ~/.config/awesome/libs/
|
||||
rm -rf ~/.config/awesome/lua-pam
|
||||
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
backend = "glx";
|
||||
vsync = true;
|
||||
# Shadow
|
||||
blur-method = "dual_kawase";
|
||||
blur-strength = 5;
|
||||
blur-size = 7;
|
||||
shadow = true; # Enabled client-side shadows on windows.
|
||||
shadow-radius = 12; # The blur radius for shadows. (default 12)
|
||||
shadow-offset-x = -7; # The left offset for shadows. (default -15)
|
||||
shadow-offset-y = -7; # The top offset for shadows. (default -15)
|
||||
shadow-opacity = 0.25;
|
||||
shadow-exclude = [
|
||||
"! name~=''",
|
||||
"n:e:Notification",
|
||||
"n:e:Plank",
|
||||
"n:e:Docky",
|
||||
"g:e:Synapse",
|
||||
"g:e:Kupfer",
|
||||
"g:e:Conky",
|
||||
"n:w:*Firefox*",
|
||||
"n:w:*Chrome*",
|
||||
"n:w:*Chromium*",
|
||||
"class_g ?= 'Notify-osd'",
|
||||
"class_g ?= 'Cairo-dock'",
|
||||
"class_g ?= 'Xfce4-notifyd'",
|
||||
"class_g ?= 'Xfce4-power-manager'",
|
||||
#"class_g ?= 'awesome'",
|
||||
#"class_g ?= 'Awesome'"
|
||||
];
|
||||
|
||||
# The shadow exclude options are helpful if you have shadows enabled. Due to the way compton draws its shadows, certain applications will have visual glitches
|
||||
# (most applications are fine, only apps that do weird things with xshapes or argb are affected).
|
||||
# This list includes all the affected apps I found in my testing. The "! name~=''" part excludes shadows on any "Unknown" windows, this prevents a visual glitch with the XFWM alt tab switcher.
|
||||
|
||||
# Fading
|
||||
fading = true;
|
||||
# Fade windows during opacity changes.
|
||||
fade-delta = 4; # The time between steps in a fade in milliseconds. (default 10).
|
||||
fade-in-step = 0.03; # Opacity change between steps while fading in. (default 0.028).
|
||||
fade-out-step = 0.03; # Opacity change between steps while fading out. (default 0.03).
|
||||
#no-fading-openclose = true; # Fade windows in/out when opening/closing
|
||||
|
||||
detect-client-opacity = true; # This prevents opacity being ignored for some apps. For example without this enabled my xfce4-notifyd is 100% opacity no matter what.
|
||||
|
||||
# Window type settings
|
||||
wintypes: {
|
||||
tooltip = { fade = true; shadow = false; };
|
||||
# dock = { shadow = false; fade = false; };
|
||||
# dnd = { shadow = false; fade = false; };
|
||||
};
|
||||
|
||||
# Disable fade for awesomewm components (causes lag with custom menus)
|
||||
#fade-exclude = [
|
||||
# "class_g ?= 'awesome'",
|
||||
# "class_g ?= 'Awesome'"
|
||||
#];
|
||||
|
Before Width: | Height: | Size: 272 KiB |
113
libs/asckey.lua
|
@ -1,113 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- library for constructing bindable keys
|
||||
local asckey = {
|
||||
keymap = {}
|
||||
}
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
asckey.set_keymap = function(keymap)
|
||||
for k,v in pairs(keymap) do
|
||||
asckey.keymap[v] = k
|
||||
end
|
||||
end
|
||||
|
||||
asckey.get_keycomb = function(name)
|
||||
local modifiers = {}
|
||||
name = name:gsub("[^%s%+]+%+",function(v)
|
||||
if v == "modkey+" then v = global.modkey.."+" end
|
||||
table.insert(modifiers,v:sub(1,-2))
|
||||
return ""
|
||||
end)
|
||||
return modifiers,name
|
||||
end
|
||||
|
||||
asckey.k = function(name,callback,description,release)
|
||||
if not asckey.keymap[name] then
|
||||
return {}
|
||||
end
|
||||
local modifiers,key = asckey.get_keycomb(asckey.keymap[name])
|
||||
if key:match("^Mouse(%d+)$") then
|
||||
-- After several experiments, the best solution to making the extra mouse buttons bindable
|
||||
-- was this. The drawback is that all bindings for these are global, and they do not return
|
||||
-- the button object itself. Crappy hack, but it will suffice for now.
|
||||
local callback_wrap = function(...)
|
||||
callback(...)
|
||||
-- For some ungodly reason binding to mouse drops all keybindings
|
||||
gears.timer.delayed_call(function()
|
||||
mousegrabber.run(function() return false end,"bogosity")
|
||||
mousegrabber.stop()
|
||||
end)
|
||||
end
|
||||
local new_button = awful.button(modifiers, tonumber(key:match("^Mouse(%d+)$")),
|
||||
callback_wrap,release)
|
||||
awful.rules.rules[1].properties.buttons = gears.table.join(
|
||||
awful.rules.rules[1].properties.buttons,
|
||||
new_button
|
||||
)
|
||||
root.buttons(gears.table.join(
|
||||
root.buttons(),
|
||||
new_button
|
||||
))
|
||||
return {}
|
||||
else
|
||||
local callback_wrap = (description.release_pre and function(...)
|
||||
root.fake_input("key_release",key)
|
||||
callback(...)
|
||||
end) or callback
|
||||
if release then
|
||||
return awful.key(modifiers,key,
|
||||
callback_wrap,
|
||||
description)
|
||||
else
|
||||
return awful.key(modifiers,key,
|
||||
callback_wrap,
|
||||
release, description)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
asckey.custom_binds = function()
|
||||
local custom_keys = {}
|
||||
for comm,_ in pairs(asckey.keymap) do
|
||||
if not comm:match("^:.*") then
|
||||
table.insert(custom_keys,asckey.k(comm,function()
|
||||
awful.spawn(comm)
|
||||
end,{description = comm, group = "custom"}))
|
||||
end
|
||||
end
|
||||
return custom_keys
|
||||
end
|
||||
|
||||
asckey.context = function(keys)
|
||||
local context = {keys = keys, key_lookup = {},active = false}
|
||||
for _,v in pairs(keys) do
|
||||
context.key_lookup[v] = true
|
||||
end
|
||||
function context:activate()
|
||||
context.active = true
|
||||
root.keys(gears.table.join(
|
||||
root.keys(),
|
||||
self.keys
|
||||
))
|
||||
end
|
||||
function context:deactivate()
|
||||
context.active = false
|
||||
local root_keys = root.keys()
|
||||
for i = #root_keys,1,-1 do
|
||||
if self.key_lookup[root_keys[i]] then
|
||||
table.remove(root_keys,i)
|
||||
end
|
||||
end
|
||||
root.keys(root_keys)
|
||||
end
|
||||
return context
|
||||
end
|
||||
|
||||
return asckey
|
296
libs/awmtk2.lua
|
@ -5,37 +5,31 @@
|
|||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- renotk (formerly awmtk2) - template/granular styling library for reno
|
||||
-- RenoTK (formerly AWMTK2) - Template/Granular styling library for Reno
|
||||
local wibox = require("wibox")
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local beautiful = require("beautiful")
|
||||
beautiful.widgets = beautiful.widgets or {}
|
||||
beautiful.templates = beautiful.templates or {}
|
||||
|
||||
local awmtk = {}
|
||||
|
||||
-- {{{ utils
|
||||
awmtk.create_delta = function(name,instance_delta,class_delta,parent_delta,vertical)
|
||||
-- to save memory, we create proxies for lower layers called "deltas"
|
||||
-- this function creates that proxy layer using metatables
|
||||
-- fun story - i used instance_delta instead of {} at first.
|
||||
-- the results were horrifying and confusing.
|
||||
-- {{{ Utils
|
||||
awmtk.create_delta = function(name,instance_delta,class_delta,parent_delta)
|
||||
-- To save memory, we create proxies for lower layers called "deltas"
|
||||
-- This function creates that proxy layer using metatables
|
||||
|
||||
-- Fun story - i used instance_delta instead of {} at first.
|
||||
-- The results were horrifying and confusing.
|
||||
return setmetatable({},{
|
||||
__index = function(_,k)
|
||||
-- per-instance overrides are top priority
|
||||
__index = function(self,k)
|
||||
-- Per-instance overrides are top priority
|
||||
if rawget(instance_delta,k) then
|
||||
return rawget(instance_delta,k)
|
||||
-- class-wide overrides are second in priority
|
||||
-- Class-wide overrides are second in priority
|
||||
elseif type(class_delta[name]) == "table"
|
||||
and rawget(class_delta[name],k) then
|
||||
if vertical and
|
||||
type(rawget(class_delta[name],"vertical")) == "table" and
|
||||
rawget(class_delta[name],"vertical")[k] then
|
||||
return rawget(class_delta[name].vertical,k)
|
||||
else
|
||||
return rawget(class_delta[name],k)
|
||||
end
|
||||
-- parent is fallback
|
||||
-- Parent is fallback
|
||||
elseif parent_delta[k] then
|
||||
return parent_delta[k]
|
||||
end
|
||||
|
@ -43,18 +37,17 @@ awmtk.create_delta = function(name,instance_delta,class_delta,parent_delta,verti
|
|||
})
|
||||
end
|
||||
|
||||
awmtk.create_style = function(name,parent,overrides,vertical)
|
||||
-- a style is essentially a layer of deltas upon the previous (parent) style
|
||||
awmtk.create_style = function(name,parent,overrides)
|
||||
-- A style is essentially a layer of deltas upon the previous (parent) style
|
||||
local new_style = {}
|
||||
local odelta = (overrides and overrides[name]) or {}
|
||||
local cdelta = beautiful.widgets[name] or {}
|
||||
for class_name,parent_class in pairs(parent) do
|
||||
new_style[class_name] = awmtk.create_delta(
|
||||
class_name,
|
||||
local cdelta = (beautiful.widgets and beautiful.widgets[name]) or {}
|
||||
for name,parent_class in pairs(parent) do
|
||||
new_style[name] = awmtk.create_delta(
|
||||
name,
|
||||
odelta,
|
||||
cdelta,
|
||||
parent_class,
|
||||
vertical
|
||||
parent_class
|
||||
)
|
||||
end
|
||||
return new_style
|
||||
|
@ -70,84 +63,43 @@ awmtk.create_template_lib = function(name,parent,overrides)
|
|||
)
|
||||
end
|
||||
|
||||
awmtk.build_templates = function(templates,style,vertical)
|
||||
awmtk.build_templates = function(templates,style)
|
||||
local new_templates = {}
|
||||
for name,_ in pairs(awmtk.proto_templates) do
|
||||
new_templates[name] = templates[name](style,vertical)
|
||||
for name,template in pairs(awmtk.proto_templates) do
|
||||
new_templates[name] = templates[name](style)
|
||||
end
|
||||
return new_templates
|
||||
end
|
||||
|
||||
awmtk.mask_object_call = function(obj,call)
|
||||
local new = {}
|
||||
for k,v in pairs(obj) do
|
||||
new[k] = v
|
||||
end
|
||||
return setmetatable(new,{
|
||||
__index = obj,
|
||||
__call = call
|
||||
})
|
||||
end
|
||||
|
||||
awmtk.wrap_hooks = function(w,callbacks)
|
||||
-- attach hooks to function
|
||||
local mcall = (getmetatable(w) and getmetatable(w).__call) or w
|
||||
local call_wrapper = function(...)
|
||||
if callbacks and callbacks.on_create_pre then
|
||||
callbacks.on_create_pre(...)
|
||||
end
|
||||
local widget = mcall(...)
|
||||
if callbacks and callbacks.on_create then
|
||||
callbacks.on_create(widget,...)
|
||||
end
|
||||
if callbacks and callbacks.on_ready then
|
||||
callbacks._on_ready_called = false
|
||||
local func = function()
|
||||
if not callbacks._on_ready_called then
|
||||
callbacks.on_ready(widget)
|
||||
callbacks._on_ready_called = true
|
||||
end
|
||||
end
|
||||
widget:connect_signal("widget::layout_changed",func)
|
||||
widget:connect_signal("widget::layout_changed",function()
|
||||
widget:disconnect_signal("widget::layout_changed",func)
|
||||
end)
|
||||
end
|
||||
return widget
|
||||
end
|
||||
return (getmetatable(w) and awmtk.mask_object_call(w,call_wrapper)) or
|
||||
call_wrapper
|
||||
end
|
||||
|
||||
awmtk.merge = gears.table.join
|
||||
-- }}}
|
||||
|
||||
|
||||
-- {{{ default style
|
||||
-- {{{ Default style
|
||||
|
||||
-- prototype style
|
||||
-- notice that it's not awmtk.default style - it's used as a base for default.
|
||||
-- Prototype style
|
||||
-- Notice that it's not awmtk.default style - it's used as a base for default.
|
||||
awmtk.proto_style = {
|
||||
base = setmetatable({
|
||||
-- { backgrounds
|
||||
-- { Backgrounds
|
||||
-- custom background color for highlighting elements
|
||||
bg_highlight = beautiful.bg_highlight or beautiful.bg_focus,
|
||||
-- allow more complex themes to define background images
|
||||
bgimage_focus = beautiful.bgimage_focus,
|
||||
bgimage_normal = beautiful.bgimage_normal,
|
||||
-- }
|
||||
-- { borders
|
||||
-- borders for popups
|
||||
-- { Borders
|
||||
-- Borders for popups
|
||||
shape_border_width = beautiful.shape_border_width or 0,
|
||||
shape_border_color = beautiful.shape_border_color or beautiful.bg_normal,
|
||||
-- }
|
||||
-- { callbacks
|
||||
-- { Callbacks
|
||||
-- a tiny bit more complex thing to account for more extensibility
|
||||
-- the stub functions do nothing - you should implement functionality inside the theme
|
||||
-- the stub functions do nothing - you should implement functionality inside theme
|
||||
onpress = function() end,
|
||||
onrelease = function() end,
|
||||
-- }
|
||||
-- { shapes
|
||||
-- { Shapes
|
||||
margins = 5,
|
||||
spacing = 5,
|
||||
shape = function(cr, width, height)
|
||||
|
@ -157,7 +109,7 @@ awmtk.proto_style = {
|
|||
},{__index = beautiful})
|
||||
}
|
||||
|
||||
-- subclasses
|
||||
-- Subclasses
|
||||
awmtk.proto_style.container = awmtk.create_delta("container",{
|
||||
},awmtk.proto_style,awmtk.proto_style.base)
|
||||
|
||||
|
@ -208,27 +160,15 @@ awmtk.proto_style.center = awmtk.create_delta("center", {
|
|||
awmtk.proto_style.slider = awmtk.create_delta("slider", {
|
||||
margins = 1
|
||||
}, awmtk.proto_style,awmtk.proto_style.base)
|
||||
|
||||
awmtk.proto_style.checkbox = awmtk.create_delta("checkbox", {
|
||||
}, awmtk.proto_style,awmtk.proto_style.base)
|
||||
|
||||
awmtk.proto_style.icon = awmtk.create_delta("icon", {
|
||||
constraint = "max",
|
||||
height = 100,
|
||||
width = 100,
|
||||
valign = "center",
|
||||
halign = "center",
|
||||
margins = 0
|
||||
}, awmtk.proto_style,awmtk.proto_style.base)
|
||||
-- }}}
|
||||
|
||||
-- {{{ generic templates
|
||||
-- {{{ Generic templates
|
||||
awmtk.proto_templates = {
|
||||
-- templates are built first using the given style, then applied to contents
|
||||
-- Templates are built first using the given style, then applied to contents
|
||||
-- through returned function
|
||||
container = function(style)
|
||||
-- container is practically any "box" that contains buttons, textboxes,
|
||||
-- and anything you want to put into the container. do not confuse with
|
||||
-- Container is practically any "box" that contains buttons, textboxes,
|
||||
-- and anything you want to put into the container. Do not confuse with
|
||||
-- popup - containers are designed to separate contents within a popup.
|
||||
return function(layout,options)
|
||||
return awmtk.merge({
|
||||
|
@ -237,20 +177,17 @@ awmtk.proto_templates = {
|
|||
margins = style.container.margins,
|
||||
layout = wibox.container.margin
|
||||
},
|
||||
bgimage = style.container.bgimage_normal,
|
||||
bgimage = style.container.bgimage,
|
||||
bg = style.container.bg_normal,
|
||||
fg = style.container.fg_normal,
|
||||
shape = style.container.shape,
|
||||
shape_border_color = style.container.shape_border_color,
|
||||
shape_border_width = style.container.shape_border_width,
|
||||
widget = awmtk.wrap_hooks(wibox.container.background,options)
|
||||
widget = wibox.container.background
|
||||
},options or {})
|
||||
end
|
||||
end,
|
||||
|
||||
button = function(style)
|
||||
-- self explanatory. notice that this does not bear any function -
|
||||
-- only the visual part of the button. by design, onpress and onrelease
|
||||
-- Self explanatory. Notice that this does not bear any function -
|
||||
-- only the visual part of the button. By design, onpress and onrelease
|
||||
-- callbacks should be connected to button events for animations
|
||||
return function(layout,options)
|
||||
return awmtk.merge({
|
||||
|
@ -259,32 +196,29 @@ awmtk.proto_templates = {
|
|||
margins = style.button.margins,
|
||||
layout = wibox.container.margin
|
||||
},
|
||||
bgimage = style.button.bgimage_normal,
|
||||
bgimage = style.button.bgimage,
|
||||
bg = style.button.bg_normal,
|
||||
fg = style.button.fg_normal,
|
||||
shape = style.button.shape,
|
||||
shape_border_color = style.button.shape_border_color,
|
||||
shape_border_width = style.button.shape_border_width,
|
||||
widget = awmtk.wrap_hooks(wibox.container.background,options)
|
||||
widget = wibox.container.background
|
||||
},options or {})
|
||||
end
|
||||
end,
|
||||
|
||||
textbox = function(style)
|
||||
-- nothing fancy here, but you can set per-widget fonts using beautiful.
|
||||
-- Nothing fancy here, but you can set per-widget fonts using beautiful.
|
||||
return function(options)
|
||||
return awmtk.merge({
|
||||
font = style.textbox.font,
|
||||
widget = awmtk.wrap_hooks(wibox.widget.textbox,options)
|
||||
widget = wibox.widget.textbox
|
||||
},options or {})
|
||||
end
|
||||
end,
|
||||
|
||||
hseparator = function(style)
|
||||
-- wow, i guess?
|
||||
-- Wow, i guess?
|
||||
return function(options)
|
||||
return awmtk.merge({
|
||||
widget = awmtk.wrap_hooks(wibox.widget.separator,options),
|
||||
widget = wibox.widget.separator,
|
||||
orientation = "horizontal",
|
||||
thickness = style.separator.thickness,
|
||||
color = style.separator.color,
|
||||
|
@ -293,45 +227,11 @@ awmtk.proto_templates = {
|
|||
end
|
||||
end,
|
||||
|
||||
icon = function(style)
|
||||
return function(args)
|
||||
args = args or {}
|
||||
return {
|
||||
{
|
||||
{
|
||||
{
|
||||
image = args.image,
|
||||
id = args.id,
|
||||
resize = args.resize,
|
||||
widget = wibox.widget.imagebox
|
||||
},
|
||||
widget = wibox.container.margin,
|
||||
margins = args.margins or style.icon.margins
|
||||
},
|
||||
strategy = args.constraint or style.icon.constraint,
|
||||
height = args.height or
|
||||
style.icon.height,
|
||||
width = args.width or
|
||||
style.icon.width,
|
||||
widget = awmtk.wrap_hooks(wibox.container.constraint,
|
||||
args),
|
||||
id = "constraint"
|
||||
},
|
||||
--widget = awmtk.wrap_hooks(
|
||||
widget = wibox.container.place,
|
||||
-- args
|
||||
--),
|
||||
valign = args.valign or style.icon.valign,
|
||||
halign = style.icon.halign or style.icon.halign
|
||||
}
|
||||
end
|
||||
end,
|
||||
|
||||
vseparator = function(style)
|
||||
-- i'm running out of comments
|
||||
-- I'm running out of comments
|
||||
return function(options)
|
||||
return awmtk.merge({
|
||||
widget = awmtk.wrap_hooks(wibox.widget.separator,options),
|
||||
widget = wibox.widget.separator,
|
||||
orientation = "vertical",
|
||||
thickness = style.separator.thickness,
|
||||
color = style.separator.color,
|
||||
|
@ -341,8 +241,8 @@ awmtk.proto_templates = {
|
|||
end,
|
||||
|
||||
article = function(style)
|
||||
-- article is a template that combines 3 common pieces of a full item:
|
||||
-- icon, name and description. designed to be placed within a container
|
||||
-- Article is a template that combines 3 common pieces of a full item:
|
||||
-- Icon, name and description. Designed to be placed within a container
|
||||
-- or a button.
|
||||
return function(options)
|
||||
return awmtk.merge({
|
||||
|
@ -371,6 +271,8 @@ awmtk.proto_templates = {
|
|||
id = options.title_id,
|
||||
widget = wibox.widget.textbox,
|
||||
font = style.article.font,
|
||||
align = options.font_align or
|
||||
style.article.font_align,
|
||||
valign = style.article.title_valign or "center",
|
||||
align = style.article.title_align or "left"
|
||||
},
|
||||
|
@ -379,6 +281,8 @@ awmtk.proto_templates = {
|
|||
id = options.desc_id,
|
||||
widget = wibox.widget.textbox,
|
||||
font = style.article.small_font,
|
||||
align = options.small_font_align or
|
||||
style.article.small_font_align,
|
||||
valign = style.article.desc_valign or "center",
|
||||
align = style.article.desc_align or "left"
|
||||
}),
|
||||
|
@ -386,10 +290,7 @@ awmtk.proto_templates = {
|
|||
layout = wibox.layout.flex.vertical
|
||||
},
|
||||
spacing = style.article.spacing,
|
||||
layout = awmtk.wrap_hooks(
|
||||
wibox.layout.fixed.horizontal,
|
||||
options
|
||||
)
|
||||
layout = wibox.layout.fixed.horizontal,
|
||||
}, options or {})
|
||||
end
|
||||
end,
|
||||
|
@ -407,10 +308,7 @@ awmtk.proto_templates = {
|
|||
style.center.width,
|
||||
widget = wibox.container.constraint
|
||||
},
|
||||
widget = awmtk.wrap_hooks(
|
||||
wibox.container.place,
|
||||
options
|
||||
),
|
||||
widget = wibox.container.place,
|
||||
valign = "center",
|
||||
halign = "center"
|
||||
},options or {})
|
||||
|
@ -418,16 +316,16 @@ awmtk.proto_templates = {
|
|||
end,
|
||||
|
||||
popup = function(style)
|
||||
-- popup is a distinct template designed to accomodate the "root" of
|
||||
-- Popup is a distinct template designed to accomodate the "root" of
|
||||
-- a popup, allowing one to add titlebars to popups, for example.
|
||||
return function(widget,options)
|
||||
return awmtk.merge({
|
||||
widget = {
|
||||
widget,
|
||||
margins = style.popup.margins,
|
||||
layout = awmtk.wrap_hooks(wibox.container.margin,options)
|
||||
layout = wibox.container.margin
|
||||
},
|
||||
bgimage = style.popup.bgimage_normal,
|
||||
bgimage = style.popup.bgimage,
|
||||
shape = style.popup.shape,
|
||||
visible = false,
|
||||
ontop = true
|
||||
|
@ -436,13 +334,13 @@ awmtk.proto_templates = {
|
|||
end,
|
||||
|
||||
titlebar = function(style)
|
||||
-- titlebar is a separate class specifically for window and popup
|
||||
-- titlebars. the decision to make it a separate class was due to
|
||||
-- Titlebar is a separate class specifically for window and popup
|
||||
-- titlebars. The decision to make it a separate class was due to
|
||||
-- the fact that much customization is done through default theme table
|
||||
return function(layout,options)
|
||||
-- if there's one thing that fascinates me, it's how much weird
|
||||
-- If there's one thing that fascinates me, it's how much weird
|
||||
-- bugs i manage to uncover by some sort of miraculous accident.
|
||||
-- this one fixes a race condition in margins+(left/right/bottom/top) configuration scenario
|
||||
-- This one fixes a race condition in margins+(left/right/bottom/top) configuration scenario
|
||||
local margins = style.titlebar.margins
|
||||
if (style.titlebar.left or
|
||||
style.titlebar.right or
|
||||
|
@ -453,7 +351,7 @@ awmtk.proto_templates = {
|
|||
return awmtk.merge({
|
||||
layout,
|
||||
margins = margins,
|
||||
layout = awmtk.wrap_hooks(wibox.container.margin,options),
|
||||
layout = wibox.container.margin,
|
||||
left = style.titlebar.left,
|
||||
right = style.titlebar.right,
|
||||
bottom = style.titlebar.bottom,
|
||||
|
@ -463,7 +361,7 @@ awmtk.proto_templates = {
|
|||
end,
|
||||
|
||||
wibar = function(style)
|
||||
-- just your regular old wibar, but as a style template.
|
||||
-- Just you regular old wibar, but as a style template.
|
||||
return function(layout,options)
|
||||
local margins = style.wibar.margins
|
||||
if (style.wibar.left or
|
||||
|
@ -475,7 +373,7 @@ awmtk.proto_templates = {
|
|||
return awmtk.merge({
|
||||
layout,
|
||||
margins = margins,
|
||||
layout = awmtk.wrap_hooks(wibox.container.margin,options),
|
||||
layout = wibox.container.margin,
|
||||
left = style.wibar.left,
|
||||
right = style.wibar.right,
|
||||
bottom = style.wibar.bottom,
|
||||
|
@ -484,10 +382,9 @@ awmtk.proto_templates = {
|
|||
end
|
||||
end,
|
||||
|
||||
slider = function(style,vertical)
|
||||
-- slider widget but wired to work with the awmtk2 namespace system
|
||||
slider = function(style)
|
||||
-- Slider widget but wired to work with the AWMTK2 namespace system
|
||||
return function(args)
|
||||
if not vertical then
|
||||
return awmtk.merge({
|
||||
handle_shape = style.slider.handle_shape or style.slider.shape,
|
||||
handle_color = style.slider.bg_normal,
|
||||
|
@ -503,64 +400,15 @@ awmtk.proto_templates = {
|
|||
bar_border_color = style.slider.bar_border_color,
|
||||
forced_width = style.slider.width,
|
||||
forced_height = style.slider.height,
|
||||
widget = awmtk.wrap_hooks(wibox.widget.slider,args)
|
||||
widget = wibox.widget.slider
|
||||
},args or {})
|
||||
else
|
||||
return {
|
||||
awmtk.merge({
|
||||
handle_shape = style.slider.handle_shape or style.slider.shape,
|
||||
handle_color = style.slider.bg_normal,
|
||||
handle_margins = style.slider.handle_margins,
|
||||
handle_width = style.slider.handle_width,
|
||||
handle_border_color = style.slider.handle_border_color,
|
||||
handle_border_width = style.slider.handle_border_width,
|
||||
bar_shape = style.slider.bar_shape or style.slider.shape,
|
||||
bar_height = style.slider.bar_height,
|
||||
bar_color = style.slider.bg_focus,
|
||||
bar_margins = style.slider.bar_margins,
|
||||
bar_border_width = style.slider.bar_border_width,
|
||||
bar_border_color = style.slider.bar_border_color,
|
||||
forced_width = style.slider.width,
|
||||
forced_height = style.slider.height,
|
||||
widget = awmtk.wrap_hooks(wibox.widget.slider,args)
|
||||
},args or {}),
|
||||
widget = wibox.container.rotate,
|
||||
direction = "west"
|
||||
}
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
checkbox = function(style)
|
||||
return function(args)
|
||||
return awmtk.merge({
|
||||
color = style.checkbox.color or style.checkbox.bg_normal,
|
||||
paddings = style.checkbox.paddings,
|
||||
shape = style.checkbox.shape,
|
||||
border_width = style.checkbox.border_width,
|
||||
border_color = style.checkbox.border_color or style.checkbox.bg_normal,
|
||||
bg = style.checkbox.bg or style.checkbox.bg_focus,
|
||||
check_color = style.checkbox.check_color or style.checkbox.bg_normal,
|
||||
check_shape = style.checkbox.check_shape,
|
||||
widget = awmtk.wrap_hooks(wibox.widget.checkbox,args)
|
||||
},args)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
-- last but not least - we export a default template lib and default style.
|
||||
-- this is done in order to allow overriding default style behaviour from theme
|
||||
-- Last but not least - we export a default template lib and default style.
|
||||
-- This is done in order to allow overriding default style behaviour from theme
|
||||
awmtk.default = awmtk.create_style("default",awmtk.proto_style,{})
|
||||
awmtk.templates = awmtk.create_template_lib("templates",awmtk.proto_templates,{})
|
||||
|
||||
-- generic styles for widgets that need them
|
||||
awmtk.generic = {}
|
||||
awmtk.generic.menu = awmtk.create_style("generic_menu",awmtk.default,{})
|
||||
awmtk.generic.button_list = awmtk.create_style("generic_button_list",awmtk.default,{})
|
||||
awmtk.generic.iconified_widget = awmtk.create_style("generic_iconified_widget",awmtk.default,{})
|
||||
awmtk.generic.status_widget = awmtk.create_style("generic_status_widget",awmtk.default,{})
|
||||
awmtk.generic.oneline_widget = awmtk.create_style("generic_oneline_widget",awmtk.default,{})
|
||||
awmtk.generic.composite_widget = awmtk.create_style("generic_composite_widget",awmtk.default,{})
|
||||
awmtk.generic.popup = awmtk.create_style("generic_popup",awmtk.default,{})
|
||||
-- }}}
|
||||
return awmtk
|
||||
|
|
|
@ -10,7 +10,6 @@ local json = require("dkjson")
|
|||
local gears = require("gears")
|
||||
local wibox = require("wibox")
|
||||
local awful = require("awful")
|
||||
local menu = require("context_menu")
|
||||
|
||||
local builtins = {
|
||||
h_spacer = function(o)
|
||||
|
@ -46,37 +45,30 @@ return function(description,opts)
|
|||
local buttons = opts.buttons
|
||||
-- Build a layout given a JSON description, a style and client
|
||||
-- (if applicable)
|
||||
local layout = {}
|
||||
local test,err = json.decode(description)
|
||||
if not test then
|
||||
error("Builder failure: "..err)
|
||||
end
|
||||
local function inner_builder(struct,options,vertical)
|
||||
local function inner_builder(struct)
|
||||
if struct.widget then -- External widget descriptions
|
||||
local args = gears.table.join({
|
||||
layout = (struct.layout and inner_builder(struct.layout,options)),
|
||||
client = (struct.client and c),
|
||||
screen = (struct.screen and s),
|
||||
vertical = (function()
|
||||
if type(struct.vertical) ~= "nil" then
|
||||
return struct.vertical
|
||||
end
|
||||
return vertical
|
||||
end)()
|
||||
},struct.options or {},options.passthrough or {})
|
||||
return require(struct.widget)(args)
|
||||
return require(struct.widget)(gears.table.join({
|
||||
layout = (struct.layout and inner_builder(struct.layout)), client = (struct.client and c),
|
||||
screen = (struct.screen and s)
|
||||
},struct.options or {},opts.passthrough or {}))
|
||||
elseif struct.list then -- List descriptions
|
||||
local list = {
|
||||
layout = wibox.layout.fixed[(
|
||||
(struct.vertical and "vertical") or
|
||||
"horizontal"
|
||||
)],
|
||||
spacing = style.spacing or struct.spacing
|
||||
spacing = style.spacing
|
||||
}
|
||||
for _,v in pairs(struct.list) do
|
||||
for k,v in pairs(struct.list) do
|
||||
if v.draggable then
|
||||
list.buttons = buttons
|
||||
else
|
||||
local new_obj = inner_builder(v,options,struct.vertical)
|
||||
local new_obj = inner_builder(v)
|
||||
if new_obj then
|
||||
table.insert(list,new_obj)
|
||||
end
|
||||
|
@ -91,25 +83,25 @@ return function(description,opts)
|
|||
local list = {
|
||||
{
|
||||
layout = wibox.layout.fixed[orient],
|
||||
spacing = style.spacing or struct.spacing
|
||||
spacing = style.spacing
|
||||
},{
|
||||
layout = wibox.layout.flex[orient],
|
||||
spacing = style.spacing or struct.spacing
|
||||
spacing = style.spacing
|
||||
},{
|
||||
-- Simulating "spacing" parameter
|
||||
widget = builtins[(struct.vertical and "v_spacer") or
|
||||
"h_spacer"]({size = style.spacing}),
|
||||
layout = wibox.layout.fixed[orient],
|
||||
spacing = style.spacing or struct.spacing
|
||||
spacing = style.spacing
|
||||
},
|
||||
layout = wibox.layout.align[orient],
|
||||
layout = wibox.layout.align[orient]
|
||||
}
|
||||
for k,v in pairs({"left","center","right"}) do
|
||||
for _,obj in pairs(struct.align[v]) do
|
||||
if obj.draggable then
|
||||
list[k].buttons = buttons
|
||||
else
|
||||
local new_obj = inner_builder(obj,options,struct.vertical)
|
||||
local new_obj = inner_builder(obj)
|
||||
if new_obj then
|
||||
table.insert(list[k],new_obj)
|
||||
end
|
||||
|
@ -128,25 +120,12 @@ return function(description,opts)
|
|||
return builtins[struct.builtin](gears.table.join({
|
||||
client = (struct.client and c)
|
||||
},struct.options or {}))
|
||||
elseif struct.multimenu then -- Multimenus, or otherwise "Context menu mergers".
|
||||
-- These merge multiple menus into one using the menu_parent argument.
|
||||
local multimenu = menu({items={}})
|
||||
local newopts = {}
|
||||
for k,v in pairs(options) do
|
||||
newopts[k] = v
|
||||
end
|
||||
newopts.passthrough = options.passthrough or {}
|
||||
newopts.passthrough.menu_parent = multimenu
|
||||
for _,v in pairs(struct.multimenu) do
|
||||
inner_builder(v,newopts,struct.vertical)
|
||||
end
|
||||
return multimenu
|
||||
end
|
||||
-- If this gets interpreted it's safe to say none of the constructions
|
||||
-- above got matched.
|
||||
print("Object dump: ")
|
||||
print("Object where the error occured: ")
|
||||
gears.debug.dump(struct)
|
||||
error("Builder error: invalid object description")
|
||||
end
|
||||
return inner_builder(test,opts),test.context_options
|
||||
return inner_builder(test,layout),test.context_options
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
-- POLYMENU - brutal deluxe
|
||||
-- This isn't in the widgets folder because it's too meta to be there
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local menubar_utils = require("menubar.utils")
|
||||
local wibox = require("wibox")
|
||||
local awmtk2 = require("awmtk2")
|
||||
|
||||
|
@ -17,7 +19,7 @@ local function position_popup(popup,widget,style)
|
|||
-- Figure out the geometry of the base widget
|
||||
local widget_geo
|
||||
for k,v in pairs(mouse.current_widgets or {}) do
|
||||
if (widget == v) and mouse.current_widget_geometries then
|
||||
if widget == v then
|
||||
widget_geo = mouse.current_widget_geometries[k]
|
||||
end
|
||||
end
|
||||
|
@ -37,8 +39,7 @@ end
|
|||
return function(args)
|
||||
-- A way to communicate that all widgets in menu got closed
|
||||
args.on_close = args.on_close or function() end
|
||||
local style = awmtk2.create_style("menu",
|
||||
awmtk2.generic.menu, args.style)
|
||||
local style = awmtk2.create_style("menu", awmtk2.default, args.style)
|
||||
local templates = awmtk2.create_template_lib("menu", awmtk2.templates, args.templates)
|
||||
local t = awmtk2.build_templates(templates,style)
|
||||
local function menu_builder(element,layer,root_layer)
|
||||
|
@ -52,15 +53,13 @@ return function(args)
|
|||
}))
|
||||
local onpress = function(widget)
|
||||
style.button.onrelease(widget)
|
||||
widget:emit_signal("cascade::kill")
|
||||
if root_layer._private.focused then
|
||||
root_layer._private.focused:emit_signal("cascade::kill")
|
||||
root_layer:emit_signal("cascade::kill")
|
||||
if root_layer.focused then
|
||||
root_layer.focused:emit_signal("cascade::kill")
|
||||
end
|
||||
end
|
||||
if type(element[2]) == "string" then
|
||||
new_element:connect_signal("button::press",style.button.onpress)
|
||||
new_element:connect_signal("button::press",function()
|
||||
new_element:connect_signal("button::press",function(widget)
|
||||
awful.spawn(element[2])
|
||||
end)
|
||||
new_element:connect_signal("button::release",onpress)
|
||||
|
@ -69,12 +68,12 @@ return function(args)
|
|||
new_element:connect_signal("button::press",element[2])
|
||||
new_element:connect_signal("button::release",onpress)
|
||||
elseif type(element[2]) == "table" then
|
||||
local layout = wibox.widget({
|
||||
local layout = {
|
||||
spacing = style.base.spacing,
|
||||
layout = wibox.layout.fixed.vertical
|
||||
})
|
||||
for _,v in pairs(element[2]) do
|
||||
layout:add(menu_builder(v,layout,root_layer))
|
||||
}
|
||||
for k,v in pairs(element[2]) do
|
||||
table.insert(layout,menu_builder(v,layout,root_layer))
|
||||
end
|
||||
local next_layer = awful.popup(t.popup(layout,{
|
||||
visible = false,
|
||||
|
@ -83,27 +82,25 @@ return function(args)
|
|||
preferred_anchors = {"front","back"},
|
||||
}))
|
||||
local function open_layer(widget)
|
||||
if layer._private.focused == widget and
|
||||
if layer.focused == widget and
|
||||
next_layer.visible then
|
||||
return
|
||||
end
|
||||
if layer._private.focused then
|
||||
layer._private.focused:emit_signal("cascade::close")
|
||||
if layer.focused then
|
||||
layer.focused:emit_signal("cascade::close")
|
||||
end
|
||||
layer._private.focused = widget
|
||||
layer.focused = widget
|
||||
position_popup(next_layer, new_element, style)
|
||||
end
|
||||
local onclose = function()
|
||||
style.button.onrelease(new_element)
|
||||
if layout._private.focused then
|
||||
layout._private.focused:emit_signal("cascade::close")
|
||||
if layout.focused then
|
||||
layout.focused:emit_signal("cascade::close")
|
||||
end
|
||||
next_layer.visible = false
|
||||
end
|
||||
new_element:connect_signal("cascade::close",onclose)
|
||||
new_element:connect_signal("cascade::kill",onclose)
|
||||
root_layer:connect_signal("cascade::kill",onclose)
|
||||
root_layer:connect_signal("cascade::close",onclose)
|
||||
-- that sweet "just move the mouse 4head" navigation
|
||||
if style.base.menu_slide then
|
||||
new_element:connect_signal("mouse::enter",open_layer)
|
||||
|
@ -115,13 +112,13 @@ return function(args)
|
|||
end
|
||||
return new_element
|
||||
end
|
||||
local root_layer = args.parent or wibox.widget {
|
||||
local root_layer = {
|
||||
layout = wibox.layout.fixed.vertical,
|
||||
id = "menu_root",
|
||||
spacing = style.base.spacing
|
||||
}
|
||||
for _,v in pairs(args.items) do
|
||||
root_layer:add(menu_builder(v,root_layer,root_layer))
|
||||
for k,v in pairs(args.items) do
|
||||
table.insert(root_layer,menu_builder(v,root_layer,root_layer))
|
||||
end
|
||||
return root_layer
|
||||
end
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Since it would seem that get_widget_by_id is still an open issue (https://github.com/awesomeWM/awesome/issues/2945, https://github.com/awesomeWM/awesome/issues/2181), this abomination is the nuclear solution to finding **ALL** widgets that don't get indexed because they are separated by an already instantiated widget. If you use this, please, use it carefully. Don't call it more than you really need, cache the results if you have to.
|
||||
-- Hypothetically, if there occurs such a thing as widget cycle, this will get stuck. Also it's very expensive.
|
||||
return function(widget, id)
|
||||
local results = {}
|
||||
local checked = {}
|
||||
local function walker(widget, id)
|
||||
if widget.children then
|
||||
for _,v in pairs(widget:get_children()) do
|
||||
if (v.id == id) and (not checked[v]) then
|
||||
table.insert(results, v)
|
||||
checked[v] = true
|
||||
end
|
||||
if (v._private.by_id and v._private.by_id[id]) then
|
||||
for _,v2 in pairs(v._private.by_id[id]) do
|
||||
if not checked[v2] then
|
||||
table.insert(results,v2)
|
||||
checked[v2] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
walker(v, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
walker(widget, id)
|
||||
return results
|
||||
end
|
||||
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- If possible, try to blur the image and return its path via callback. Relies on ImageMagick
|
||||
local awful = require("awful")
|
||||
return function(image_path,callback)
|
||||
local blurred_wallpaper_path = "/tmp/.blur_"..image_path:match("[^/]$")
|
||||
if select(3,os.execute("convert -version")) == 0 then
|
||||
awful.spawn.easy_async("convert "..image_path.." -blur 0x10 "..blurred_wallpaper_path,function()
|
||||
callback(blurred_wallpaper_path)
|
||||
end)
|
||||
end
|
||||
return image_path
|
||||
end
|
|
@ -5,6 +5,9 @@
|
|||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
local awful = require("awful")
|
||||
local wibox = require("wibox")
|
||||
|
||||
return function(widget,list,max_elements)
|
||||
if not tostring(widget):match("wibox.layout") then
|
||||
error("Cannot attach pager to widget that isn't a layout")
|
||||
|
|
|
@ -1,215 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
local parsers = require("parsers")
|
||||
function rprint(t,ident)
|
||||
ident = ident or 0
|
||||
for k,v in pairs(t) do
|
||||
print((" "):rep(ident)..k,v)
|
||||
if type(v) == "table" then
|
||||
rprint(v,ident+4)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Conf parser
|
||||
local data = [[
|
||||
# Global variables
|
||||
[global]
|
||||
terminal = "$HOME/.local/bin/st"
|
||||
browser = "prime-run librewolf"
|
||||
modkey = "Mod4"
|
||||
theme = "reno98"
|
||||
shell = "zsh"
|
||||
|
||||
# Keybindings
|
||||
# Format: <modifier>(+<modifier>+...)+<key> = "<command or :internal.function>"
|
||||
# "modkey" as modifier will be substituted for modkey variable in [global]
|
||||
[keys]
|
||||
"modkey+Up" = ":root.client_next"
|
||||
"modkey+Down" = ":root.client_previous"
|
||||
"modkey+Control+Up" = ":root.screen_next"
|
||||
"modkey+Control+Down" = ":root.screen_previous"
|
||||
"modkey+Tab" = ":root.client_swap"
|
||||
"modkey+Return" = ":root.spawn_terminal"
|
||||
"modkey+Shift+Return" = ":root.spawn_browser"
|
||||
|
||||
# Client keys only work if a focused client exists
|
||||
"modkey+Shift+c" = ":client.kill"
|
||||
"modkey+t" = ":client.cycle_screen"
|
||||
"modkey+o" = ":client.ontop"
|
||||
"modkey+b" = ":client.below"
|
||||
"modkey+f" = ":client.fullscreen"
|
||||
"modkey+n" = ":client.minimize"
|
||||
"modkey+m" = ":client.maximize"
|
||||
]]
|
||||
for k,v in pairs(parsers.conf(data)) do
|
||||
print("Block: ["..k.."]")
|
||||
for kk,vv in pairs(v) do
|
||||
print(kk,vv)
|
||||
end
|
||||
end
|
||||
|
||||
-- yaml-like (pactl) parser
|
||||
data = [[
|
||||
Sink Input #75
|
||||
Driver: PipeWire
|
||||
Owner Module: n/a
|
||||
Client: 58
|
||||
Sink: 46
|
||||
Sample Specification: float32le 2ch 44100Hz
|
||||
Channel Map: front-left,front-right
|
||||
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
|
||||
Corked: no
|
||||
Mute: no
|
||||
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
|
||||
balance 0.00
|
||||
Buffer Latency: 0 usec
|
||||
Sink Latency: 0 usec
|
||||
Resample method: PipeWire
|
||||
Properties:
|
||||
client.api = "pipewire-pulse"
|
||||
pulse.server.type = "unix"
|
||||
application.name = "LibreWolf"
|
||||
application.process.id = "1756"
|
||||
application.process.user = "yessiest"
|
||||
application.process.host = "architect"
|
||||
application.process.binary = "librewolf"
|
||||
application.language = "C.UTF-8"
|
||||
window.x11.display = ":0"
|
||||
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
|
||||
application.process.session_id = "1"
|
||||
media.name = "AudioStream"
|
||||
node.rate = "1/44100"
|
||||
node.latency = "3307/44100"
|
||||
stream.is-live = "true"
|
||||
node.name = "LibreWolf"
|
||||
node.autoconnect = "true"
|
||||
node.want-driver = "true"
|
||||
media.class = "Stream/Output/Audio"
|
||||
adapt.follower.spa-node = ""
|
||||
object.register = "false"
|
||||
factory.id = "6"
|
||||
clock.quantum-limit = "8192"
|
||||
factory.mode = "split"
|
||||
audio.adapt.follower = ""
|
||||
library.name = "audioconvert/libspa-audioconvert"
|
||||
client.id = "56"
|
||||
object.id = "57"
|
||||
object.serial = "75"
|
||||
pulse.attr.maxlength = "4194304"
|
||||
pulse.attr.tlength = "44104"
|
||||
pulse.attr.prebuf = "35296"
|
||||
pulse.attr.minreq = "8816"
|
||||
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
|
||||
]]
|
||||
rprint(parsers.yaml_pseudo(data))
|
||||
-- Fast incomplete yaml parser
|
||||
data = [[
|
||||
Sink Input #73
|
||||
Driver: PipeWire
|
||||
Owner Module: n/a
|
||||
Client: 58
|
||||
Sink: 46
|
||||
Sample Specification: float32le 2ch 44100Hz
|
||||
Channel Map: front-left,front-right
|
||||
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
|
||||
Corked: no
|
||||
Mute: no
|
||||
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
|
||||
balance 0.00
|
||||
Buffer Latency: 0 usec
|
||||
Sink Latency: 0 usec
|
||||
Resample method: PipeWire
|
||||
Properties:
|
||||
client.api = "pipewire-pulse"
|
||||
pulse.server.type = "unix"
|
||||
application.name = "LibreWolf"
|
||||
application.process.id = "1756"
|
||||
application.process.user = "yessiest"
|
||||
application.process.host = "architect"
|
||||
application.process.binary = "librewolf"
|
||||
application.language = "C.UTF-8"
|
||||
window.x11.display = ":0"
|
||||
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
|
||||
application.process.session_id = "1"
|
||||
media.name = "AudioStream"
|
||||
node.rate = "1/44100"
|
||||
node.latency = "3307/44100"
|
||||
stream.is-live = "true"
|
||||
node.name = "LibreWolf"
|
||||
node.autoconnect = "true"
|
||||
node.want-driver = "true"
|
||||
media.class = "Stream/Output/Audio"
|
||||
adapt.follower.spa-node = ""
|
||||
object.register = "false"
|
||||
factory.id = "6"
|
||||
clock.quantum-limit = "8192"
|
||||
factory.mode = "split"
|
||||
audio.adapt.follower = ""
|
||||
library.name = "audioconvert/libspa-audioconvert"
|
||||
client.id = "56"
|
||||
object.id = "57"
|
||||
object.serial = "75"
|
||||
pulse.attr.maxlength = "4194304"
|
||||
pulse.attr.tlength = "44104"
|
||||
pulse.attr.prebuf = "35296"
|
||||
pulse.attr.minreq = "8816"
|
||||
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
|
||||
|
||||
Sink Input #75
|
||||
Driver: PipeWire
|
||||
Owner Module: n/a
|
||||
Client: 58
|
||||
Sink: 46
|
||||
Sample Specification: float32le 2ch 44100Hz
|
||||
Channel Map: front-left,front-right
|
||||
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
|
||||
Corked: no
|
||||
Mute: no
|
||||
Volume: front-left: 38326 / 58% / -13.98 dB, front-right: 38326 / 58% / -13.98 dB
|
||||
balance 0.00
|
||||
Buffer Latency: 0 usec
|
||||
Sink Latency: 0 usec
|
||||
Resample method: PipeWire
|
||||
Properties:
|
||||
client.api = "pipewire-pulse"
|
||||
pulse.server.type = "unix"
|
||||
application.name = "LibreWolf"
|
||||
application.process.id = "1756"
|
||||
application.process.user = "yessiest"
|
||||
application.process.host = "architect"
|
||||
application.process.binary = "librewolf"
|
||||
application.language = "C.UTF-8"
|
||||
window.x11.display = ":0"
|
||||
application.process.machine_id = "a8099c2d12fe88c3df940ed562adbe8c"
|
||||
application.process.session_id = "1"
|
||||
media.name = "AudioStream"
|
||||
node.rate = "1/44100"
|
||||
node.latency = "3307/44100"
|
||||
stream.is-live = "true"
|
||||
node.name = "LibreWolf"
|
||||
node.autoconnect = "true"
|
||||
node.want-driver = "true"
|
||||
media.class = "Stream/Output/Audio"
|
||||
adapt.follower.spa-node = ""
|
||||
object.register = "false"
|
||||
factory.id = "6"
|
||||
clock.quantum-limit = "8192"
|
||||
factory.mode = "split"
|
||||
audio.adapt.follower = ""
|
||||
library.name = "audioconvert/libspa-audioconvert"
|
||||
client.id = "56"
|
||||
object.id = "57"
|
||||
object.serial = "75"
|
||||
pulse.attr.maxlength = "4194304"
|
||||
pulse.attr.tlength = "44104"
|
||||
pulse.attr.prebuf = "35296"
|
||||
pulse.attr.minreq = "8816"
|
||||
module-stream-restore.id = "sink-input-by-application-name:LibreWolf"
|
||||
]]
|
||||
rprint(parsers.fast_split_yaml(data))
|
161
libs/parsers.lua
|
@ -1,161 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Various utilitiy parsers
|
||||
local parsers = {}
|
||||
|
||||
local function split_strings(text)
|
||||
-- probably the cleanest function to split by strings i've written to date
|
||||
local split = {}
|
||||
while text:find("\"") do
|
||||
local strstart = text:find("\"")
|
||||
while text:sub(strstart-1,strstart-1) == "\\" do
|
||||
strstart = text:find("\"",strstart+1)
|
||||
end
|
||||
local strend = text:find("\"",strstart+1)
|
||||
while text:sub(strend-1,strend-1) == "\\" do
|
||||
strend = text:find("\"",strend+1)
|
||||
end
|
||||
if not strend then
|
||||
return nil, "String not closed at "..strstart
|
||||
end
|
||||
local before_string = text:sub(1,strstart-1)
|
||||
local string = text:sub(strstart,strend):gsub("\\\"","\"")
|
||||
text = text:sub(strend+1,-1)
|
||||
table.insert(split,before_string)
|
||||
table.insert(split,string)
|
||||
end
|
||||
table.insert(split,text)
|
||||
return split
|
||||
end
|
||||
|
||||
parsers.fast_split_yaml = function(cfgtext)
|
||||
-- Fast yaml splitter - incomplete parsing, only first layer is parsed
|
||||
-- Used within timers to find objects while decreasing CPU usage
|
||||
local items = {}
|
||||
local replacements = 1
|
||||
cfgtext = cfgtext:gsub("^%s*","")
|
||||
while replacements > 0 do
|
||||
cfgtext,replacements = cfgtext:gsub("^(.-\n)(%S+)",function(struct,n)
|
||||
table.insert(items,struct)
|
||||
return ""..n
|
||||
end)
|
||||
end
|
||||
table.insert(items,cfgtext)
|
||||
return items
|
||||
end
|
||||
parsers.yaml_pseudo = function(cfgtext)
|
||||
-- Somewhat yaml-like structure used by pactl
|
||||
local struct = {}
|
||||
local lines = {}
|
||||
cfgtext:gsub("(%s*)([^\n]*)",function(spacing,line)
|
||||
table.insert(lines,
|
||||
{
|
||||
spacing:len(),
|
||||
-- key
|
||||
line:match("^([^:=]-)%s*[:=]") or line,
|
||||
-- value
|
||||
line:match(":%s*(.-)%s*$") or
|
||||
line:match("=%s*(.-)%s*$")
|
||||
}
|
||||
)
|
||||
end)
|
||||
local history = {struct}
|
||||
local spacing_width = 0
|
||||
for k,v in pairs(lines) do
|
||||
if v[1] > spacing_width then
|
||||
history[#history][lines[k-1][2]] = {
|
||||
[lines[k-1][2]] = lines[k-1][3]
|
||||
}
|
||||
history[#history+1] = history[#history][lines[k-1][2]]
|
||||
elseif v[1] < spacing_width then
|
||||
history[#history] = nil
|
||||
end
|
||||
if v[3] and v[3]:match("^%s*\".*\"%s*$") then
|
||||
history[#history][v[2]] = v[3]:match("^%s*\"(.*)\"%s*$")
|
||||
else
|
||||
history[#history][v[2]] = v[3]
|
||||
end
|
||||
spacing_width = v[1]
|
||||
end
|
||||
return struct
|
||||
end
|
||||
|
||||
local function quotestrip(txt)
|
||||
if (txt:sub(1,1):match("['\"]"))
|
||||
and (txt:sub(-1,-1) == txt:sub(1,1)) then
|
||||
return txt:sub(2,-2)
|
||||
else
|
||||
return txt
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
parsers.conf = function(cfgtext)
|
||||
-- Conf style parser (not exactly TOML)
|
||||
cfgtext = cfgtext:gsub("#[^\n]*","")
|
||||
local split_by_strings,err = split_strings(cfgtext)
|
||||
if not split_by_strings then error(err) end
|
||||
local full_split = {{}}
|
||||
local current_line = full_split[1]
|
||||
-- tokenizer
|
||||
for _,v in pairs(split_by_strings) do
|
||||
v = v:match("^[ \t]*(.*)[ \t]*$")
|
||||
if (not (v == "")) then
|
||||
if not v:match("^\".*\"$") then
|
||||
v:gsub("[^ \t]+",function(text)
|
||||
while text:match("\n") do
|
||||
local before,after = text:match("([^\n]*)\n(.*)")
|
||||
if before ~= "" then
|
||||
table.insert(current_line,before)
|
||||
end
|
||||
if #current_line > 0 then
|
||||
table.insert(full_split,{})
|
||||
current_line = full_split[#full_split]
|
||||
end
|
||||
text = after
|
||||
end
|
||||
if text ~= "" then
|
||||
table.insert(current_line,text)
|
||||
end
|
||||
end)
|
||||
else
|
||||
table.insert(current_line,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.remove(full_split,#full_split)
|
||||
local struct = {global = {}}
|
||||
local block = "global"
|
||||
-- parser
|
||||
for _,line in pairs(full_split) do
|
||||
if line[1] and line[1]:match("^%[[^%]]+%]$") then -- block
|
||||
block = line[1]:match("^%[([^%]]+)%]$")
|
||||
struct[block] = {}
|
||||
elseif #line == 3 then -- assignment
|
||||
if (line[3]:sub(1,1):match("['\"]")) -- string
|
||||
and (line[3]:sub(-1,-1) == line[3]:sub(1,1)) then
|
||||
struct[block][quotestrip(line[1])] = quotestrip(line[3])
|
||||
elseif line[3]:match("^%d+$") then -- number
|
||||
struct[block][quotestrip(line[1])] = tonumber(line[3])
|
||||
elseif (line[3] == "true") or (line[3] == "false") then -- boolean
|
||||
struct[block][quotestrip(line[1])] = (line[3] == "true")
|
||||
else
|
||||
error("Invalid assignment expression: "..line[3])
|
||||
end
|
||||
else -- invalid
|
||||
local textline = ""
|
||||
for _,v in pairs(line) do
|
||||
textline = textline..v.." "
|
||||
end
|
||||
error("Invalid config expression: "..textline:sub(1,-2))
|
||||
end
|
||||
end
|
||||
return struct
|
||||
end
|
||||
return parsers
|
|
@ -10,8 +10,7 @@
|
|||
local syscontrol = {
|
||||
power_supply = {},
|
||||
backlight = {},
|
||||
hwmon = {},
|
||||
pulse = {}
|
||||
hwmon = {}
|
||||
}
|
||||
syscontrol.backlight.enumerate = function()
|
||||
local lshandler = io.popen("ls -1 /sys/class/backlight","r")
|
||||
|
@ -39,7 +38,6 @@ syscontrol.backlight.read_attribs = function(dev_name)
|
|||
writable = false
|
||||
}
|
||||
fhandler:close()
|
||||
bhandler:close()
|
||||
local wfhandler = io.open("/sys/class/backlight/"..dev_name.."/brightness","w")
|
||||
if wfhandler then
|
||||
wfhandler:close()
|
||||
|
@ -85,38 +83,34 @@ end
|
|||
syscontrol.power_supply.read_attribs = function(dev_name)
|
||||
local fhandler,err = io.open("/sys/class/power_supply/"..dev_name.."/type","r")
|
||||
if fhandler then
|
||||
local uevent_f = io.open("/sys/class/power_supply/"..dev_name.."/uevent","r")
|
||||
if not uevent_f then
|
||||
return nil, "Unable to open uevent file"
|
||||
end
|
||||
local uevent_data = {}
|
||||
local dtype = fhandler:read("*a"):match("%S*")
|
||||
fhandler:close()
|
||||
local full_path = "/sys/class/power_supply/"..dev_name
|
||||
local device = {
|
||||
type = fhandler:read("*a"):match("%S*"),
|
||||
full_path = "/sys/class/power_supply/"..dev_name,
|
||||
type = dtype,
|
||||
full_path = full_path,
|
||||
name = dev_name
|
||||
}
|
||||
fhandler:close()
|
||||
uevent_f:read("*a"):gsub("[^\n]+",function(line)
|
||||
local key,value = line:match("^([^=]+)=([^=]*)")
|
||||
if value:match("^%d+$") then
|
||||
value = tonumber(value)
|
||||
end
|
||||
uevent_data[key] = value
|
||||
end)
|
||||
uevent_f:close()
|
||||
if device.type == "Battery" then
|
||||
device.model = uevent_data.POWER_SUPPLY_MODEL_NAME or "N/A"
|
||||
device.energy_full_design = uevent_data.POWER_SUPPLY_ENERGY_FULL_DESIGN
|
||||
device.energy_full = uevent_data.POWER_SUPPLY_ENERGY_FULL
|
||||
if not device.energy_full_design then
|
||||
device.energy_full_design = uevent_data.POWER_SUPPLY_CHARGE_FULL_DESIGN or -1
|
||||
device.energy_full = uevent_data.POWER_SUPPLY_CHARGE_FULL or -1
|
||||
end
|
||||
if dtype == "Mains" then
|
||||
local online_f = io.open(full_path.."/online","r")
|
||||
device.online = (online_f:read("*a"):match("%S*") == "1")
|
||||
online_f:close()
|
||||
elseif dtype == "Battery" then
|
||||
local capacity_f = io.open(full_path.."/capacity","r")
|
||||
local model_f = io.open(full_path.."/model_name","r")
|
||||
local energy_full_design_f = io.open(full_path.."/energy_full_design","r")
|
||||
local energy_full_f = io.open(full_path.."/energy_full","r")
|
||||
local charging_f = io.open(full_path.."/status","r")
|
||||
device.model = model_f:read("*a"):match("[^\n]*")
|
||||
device.energy_full_design = tonumber(energy_full_design_f:read("*a"))
|
||||
device.energy_full = tonumber(energy_full_f:read("*a"))
|
||||
device.quality = (device.energy_full/device.energy_full_design)*100
|
||||
device.capacity = uevent_data.POWER_SUPPLY_CAPACITY or 0
|
||||
device.charging = (uevent_data.POWER_SUPPLY_STATUS == "Charging")
|
||||
elseif device.type == "Mains" then
|
||||
device.online = (uevent_data.POWER_SUPPLY_ONLINE == 1)
|
||||
device.capacity = tonumber(capacity_f:read("*a"))
|
||||
device.charging = (charging_f:read("*a"):match("%S*") == "Charging")
|
||||
capacity_f:close()
|
||||
model_f:close()
|
||||
energy_full_design_f:close()
|
||||
energy_full_f:close()
|
||||
end
|
||||
return device
|
||||
else
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
-- Various utilities that are not included into awesomewm
|
||||
local utils = {}
|
||||
utils.substitute_env = function(str)
|
||||
return str:gsub("$[%w_]+",function(var)
|
||||
return os.getenv(var:sub(2,-1)) or ""
|
||||
end)
|
||||
end
|
||||
return utils
|
|
@ -1,137 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- Asynchronous XDG data aggregator (library)
|
||||
local menu_utils = require("menubar.utils")
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local json = require("dkjson")
|
||||
local lib = {}
|
||||
|
||||
function lib.init_xdg_struct()
|
||||
-- Global xdg data struct
|
||||
local xdg = {
|
||||
directory_integrity = {},
|
||||
directory_listings = {},
|
||||
apps = {},
|
||||
categories = {
|
||||
Other = {
|
||||
icon = "applications-other",
|
||||
apps = {}
|
||||
},
|
||||
Wine = {
|
||||
icon = "wine",
|
||||
apps = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return xdg
|
||||
end
|
||||
|
||||
function lib.load_xdg_cache()
|
||||
-- Load cached applications
|
||||
local cache_file = io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","r")
|
||||
local cache
|
||||
if cache_file then
|
||||
cache = json.decode(cache_file:read("*a"))
|
||||
cache_file:close()
|
||||
end
|
||||
return cache
|
||||
end
|
||||
|
||||
function lib.add_categories(xdg, categories)
|
||||
-- Add missing category entries as defined by awesome
|
||||
for _,v in pairs(categories) do
|
||||
xdg.categories[v.app_type] = {
|
||||
name = v.name,
|
||||
icon = v.icon_name,
|
||||
apps = {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function lib.async_process_dirs(xdg, dirs, cache, on_dir_done)
|
||||
-- Asynchronous scanning process
|
||||
for _,v in pairs(dirs) do
|
||||
xdg.directory_listings[v] = {}
|
||||
awful.spawn.with_line_callback("find "..tostring(v).." -maxdepth 1 -name *.desktop",{
|
||||
stdout = function(line)
|
||||
-- Assume the cache is valid for a listed file
|
||||
if cache and cache.directory_listings[v][line] then
|
||||
xdg.directory_listings[v][line] = true
|
||||
xdg.apps[line] = cache.apps[line]
|
||||
xdg.categories[cache.apps[line].category].apps[line] = cache.apps[line]
|
||||
return
|
||||
end
|
||||
local data = menu_utils.parse_desktop_file(line)
|
||||
if data.NoDisplay then
|
||||
return
|
||||
end
|
||||
local appdata = {
|
||||
name = data.Name,
|
||||
category = "Other",
|
||||
exec = data.Exec,
|
||||
icon = (data.Icon and menu_utils.lookup_icon(data.Icon)),
|
||||
description = data.Comment
|
||||
}
|
||||
-- Match first available cateogry for sorting
|
||||
for _,vv in pairs(data.Categories or {"Other"}) do
|
||||
if xdg.categories[vv] then
|
||||
appdata.category = vv
|
||||
break
|
||||
end
|
||||
-- Oh how do I love those Wine applications and their categories
|
||||
if vv:match("^Wine") then
|
||||
appdata.category = "Wine"
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Open terminal apps in the terminal (duh)
|
||||
if data.Terminal then
|
||||
appdata.exec = global.terminal.." -e "..appdata.exec
|
||||
end
|
||||
-- Just for you, Wine - special case because you're being a shit
|
||||
if (appdata.exec and appdata.exec:find("%W?wine ")) then
|
||||
appdata.category = "Wine"
|
||||
end
|
||||
xdg.apps[line] = appdata
|
||||
xdg.categories[appdata.category].apps[line] = appdata
|
||||
-- Add the file to the listing of cached ones
|
||||
xdg.directory_listings[v][line] = true
|
||||
end,
|
||||
output_done = function(...) on_dir_done(v,...) end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function lib.generate_meta_patch(xdg)
|
||||
local patch = {apps = {}}
|
||||
for k,v in pairs(xdg.apps) do
|
||||
patch.apps[k] = {}
|
||||
for kk,vv in pairs(v) do
|
||||
patch.apps[k][kk] = vv
|
||||
end
|
||||
patch.apps[k].name = nil
|
||||
patch.apps[k].category = nil
|
||||
patch.apps[k].exec = nil
|
||||
patch.apps[k].icon = nil
|
||||
patch.apps[k].description = nil
|
||||
end
|
||||
return patch
|
||||
end
|
||||
|
||||
function lib.apply_meta_patch(xdg,patch)
|
||||
for k,v in pairs(patch.apps) do
|
||||
if xdg.apps[k] then
|
||||
for kk,vv in pairs(v) do
|
||||
xdg.apps[k][kk] = vv
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return lib
|
|
@ -5,99 +5,20 @@
|
|||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- XFCE style autostart system with a system to automatically kill children just to fuck over Steam. (now with SMЯT targeting software package!)
|
||||
-- XFCE style autostart system
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local gfs = gears.filesystem
|
||||
local menu_utils = require("menubar.utils")
|
||||
local hide_ids = {}
|
||||
local related_ids = {}
|
||||
local settings = config.autostart
|
||||
local stop_checking = false
|
||||
-- I know this is linux specific, blame Steam for creating a triple-forking launcher with no startup id.
|
||||
-- I love the fact that valve is supportive of linux and thinks it's the future of gaming and all that
|
||||
-- but you could've just done the due diligence and, yk, maybe research how things work with XDG?
|
||||
-- P.S. if you know how to make this function work in similar vein on BSD, feel free to contribute
|
||||
local function is_child_of(pid,related)
|
||||
related = related or pid
|
||||
local ppidfile = io.open("/proc/"..tostring(pid).."/status","rb")
|
||||
if not ppidfile then return false end
|
||||
local ppid = ppidfile:read("*a"):match("PPid:%s*(%d+)")
|
||||
ppidfile:close()
|
||||
if (not ppid) or (ppid == "1") then return false end
|
||||
if hide_ids[tonumber(ppid)] then
|
||||
related_ids[related] = tonumber(ppid)
|
||||
return true
|
||||
else
|
||||
return is_child_of(ppid,related)
|
||||
end
|
||||
end
|
||||
-- Play whack-a-mole with the clients that match ids to hide
|
||||
-- NO MORE MR NICE GUY, until the user EXPLICITLY activates the client,
|
||||
-- it's being hidden.
|
||||
local callback = function(c)
|
||||
if not settings.minimize_enable then return end
|
||||
if stop_checking then return end
|
||||
gears.timer.delayed_call(function()
|
||||
local kill_later = false
|
||||
if c.pid and hide_ids[c.pid] then
|
||||
kill_later = true
|
||||
end
|
||||
if c.startup_id and hide_ids[c.startup_id] then
|
||||
kill_later = true
|
||||
end
|
||||
if c.pid and is_child_of(c.pid) then
|
||||
kill_later = true
|
||||
end
|
||||
if kill_later then
|
||||
c.minimized = true
|
||||
end
|
||||
end)
|
||||
end
|
||||
client.connect_signal("focus",callback)
|
||||
client.connect_signal("manage",callback)
|
||||
-- if the client has been mouse pressed we no longer hide it or any of its siblings - user needs the client to be active.
|
||||
client.connect_signal("request::activate",function(c,reason)
|
||||
if (reason ~= "mouse_click") and (reason ~= "tasklist") then
|
||||
return
|
||||
end
|
||||
if c.pid then
|
||||
hide_ids[c.pid] = nil
|
||||
if related_ids[c.pid] then
|
||||
hide_ids[related_ids[c.pid]] = nil
|
||||
end
|
||||
end
|
||||
if c.startup_id then
|
||||
hide_ids[c.startup_id] = nil
|
||||
end
|
||||
end)
|
||||
-- this ain't happy hour - stop hitting everything in sight.
|
||||
gears.timer {
|
||||
timeout = settings.minimize_timeout or 30,
|
||||
autostart = true,
|
||||
single_shot = true,
|
||||
callback = function()
|
||||
stop_checking = true
|
||||
hide_ids = {}
|
||||
end
|
||||
}
|
||||
local stdir = os.getenv("XDG_RUNTIME_DIR").."/.awesome_startup/"
|
||||
local stdir = "/tmp/.awesome_startup/"
|
||||
gfs.make_directories(stdir)
|
||||
awful.spawn.with_line_callback("find "..gfs.get_xdg_config_home().."autostart/ -name *.desktop",{
|
||||
stdout = function(line)
|
||||
local data = menu_utils.parse_desktop_file(line)
|
||||
if (data.RunHook == "0") or (data.RunHook == nil) then
|
||||
if not gfs.file_readable(stdir..line:match("[^/]*$")) then
|
||||
local npid,nsnid = awful.spawn(data.Exec:gsub("%%%w",""))
|
||||
io.open(stdir..line:match("[^/]*$"),"w"):write(npid):close()
|
||||
if data.Hidden then
|
||||
if npid then
|
||||
hide_ids[npid] = true
|
||||
end
|
||||
if nsnid then
|
||||
hide_ids[nsnid] = true
|
||||
end
|
||||
end
|
||||
io.open(stdir..line:match("[^/]*$"),"w"):close()
|
||||
awful.spawn(data.Exec)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,11 +69,6 @@ client.connect_signal("manage", function(c)
|
|||
end
|
||||
end)
|
||||
|
||||
--available layouts
|
||||
awful.layout.layouts = {
|
||||
awful.layout.suit.floating
|
||||
}
|
||||
|
||||
client.connect_signal("focus", function(c)
|
||||
c.border_color = beautiful.border_focus
|
||||
end)
|
||||
|
|
|
@ -8,41 +8,30 @@
|
|||
-- Module that adds default keybindings
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local ask = require("asckey")
|
||||
|
||||
global.modkey = global.modkey or "Mod4"
|
||||
|
||||
ask.set_keymap(config.keys)
|
||||
local custom_keys = ask.custom_binds()
|
||||
local k = ask.k
|
||||
|
||||
local keys = gears.table.join(
|
||||
k(':root.tag_next',
|
||||
awful.tag.viewnext,
|
||||
{description = "switch to next tag", group = "client"}),
|
||||
k(':root.tag_prev',
|
||||
awful.tag.viewprev,
|
||||
{description = "switch to previous tag", group = "client"}),
|
||||
k(':root.client_next',
|
||||
awful.key({global.modkey}, "Up",
|
||||
function()
|
||||
awful.client.focus.byidx(1)
|
||||
end,
|
||||
{description = "switch to next client", group = "client"}),
|
||||
k(":root.client_previous",
|
||||
awful.key({global.modkey}, "Down",
|
||||
function()
|
||||
awful.client.focus.byidx(-1)
|
||||
end,
|
||||
{description = "switch to previous client", group = "client"}),
|
||||
k(":root.screen_next",
|
||||
awful.key({global.modkey, "Control"}, "Up",
|
||||
function()
|
||||
awful.screen.focus_relative(1)
|
||||
end,
|
||||
{description = "switch to next screen", group = "screen"}),
|
||||
k(":root.screen_previous",
|
||||
awful.key({global.modkey, "Control"}, "Down",
|
||||
function()
|
||||
awful.screen.focus_relative(-1)
|
||||
end,
|
||||
{description = "switch to previous screen", group = "screen"}),
|
||||
k(":root.client_swap",
|
||||
awful.key({global.modkey}, "Tab",
|
||||
function()
|
||||
awful.client.focus.history.previous()
|
||||
if client.focus then
|
||||
|
@ -50,52 +39,16 @@ local keys = gears.table.join(
|
|||
end
|
||||
end,
|
||||
{description = "go back", group = "client"}),
|
||||
k(":root.spawn_terminal",
|
||||
awful.key({global.modkey}, "Return",
|
||||
function()
|
||||
awful.spawn(global.terminal)
|
||||
end,
|
||||
{description = "open terminal", group = "launcher"}),
|
||||
k(":root.spawn_browser",
|
||||
{description = "Open terminal", group = "launcher"}),
|
||||
awful.key({global.modkey, "Shift"}, "Return",
|
||||
function()
|
||||
awful.spawn(global.browser)
|
||||
end,
|
||||
{description = "open browser", group = "launcher"}),
|
||||
k(":root.toggle_titlebars",
|
||||
function (c)
|
||||
awesome.emit_signal("titlebar::toggle")
|
||||
end ,
|
||||
{description = "(un)hide all titlebars", group = "client"}),
|
||||
k(":layout.increase_master",
|
||||
function() awful.tag.incmwfact(0.05) end,
|
||||
{description = "increase master width factor", group = "layout"}),
|
||||
k(":layout.decrease_master",
|
||||
function() awful.tag.incmwfact(-0.05) end,
|
||||
{description = "decrease master width factor", group = "layout"}),
|
||||
k(":layout.increase_master_count",
|
||||
function() awful.tag.incnmaster(1, nil, true) end,
|
||||
{description = "increase the number of master clients", group = "layout"}),
|
||||
k(":layout.decrease_master_count",
|
||||
function() awful.tag.incnmaster(-1, nil, true) end,
|
||||
{description = "decrease the number of master clients", group = "layout"}),
|
||||
k(":layout.increase_column_count",
|
||||
function() awful.tag.incncol(1, nil, true) end,
|
||||
{description = "increase the number of columns", group = "layout"}),
|
||||
k(":layout.decrease_column_count",
|
||||
function() awful.tag.incncol(-1, nil, true) end,
|
||||
{description = "decrease the number of columns", group = "layout"}),
|
||||
k(":layout.next_layout",
|
||||
function() awful.layout.inc(1) end,
|
||||
{description = "next layout", group = "layout"}),
|
||||
k(":layout.prev_layout",
|
||||
function() awful.layout.inc(-1) end,
|
||||
{description = "previous layout", group = "layout"}),
|
||||
k(":layout.swap_next_client",
|
||||
function() awful.client.swap.byidx(1) end,
|
||||
{description = "swap with next client by index", group = "client"}),
|
||||
k(":layout.swap_next_client",
|
||||
function() awful.client.swap.byidx(-1) end,
|
||||
{description = "swap with previous client by index", group = "client"}),
|
||||
table.unpack(custom_keys))
|
||||
{description = "Open browser", group = "launcher"}))
|
||||
root.keys(keys)
|
||||
|
||||
local buttons = gears.table.join(
|
||||
|
@ -106,63 +59,43 @@ local buttons = gears.table.join(
|
|||
root.buttons(buttons)
|
||||
|
||||
local clientkeys = gears.table.join(
|
||||
k(":client.kill",
|
||||
awful.key({global.modkey, "Shift"},"c",
|
||||
function(c)
|
||||
c:kill()
|
||||
end,
|
||||
{description = "close client", group = "client"}),
|
||||
k(":client.cycle_screen",
|
||||
awful.key({global.modkey}, "o",
|
||||
function(c)
|
||||
c:move_to_screen()
|
||||
end,
|
||||
{description = "move to screen", group = "client"}),
|
||||
k(":client.ontop",
|
||||
awful.key({global.modkey}, "t",
|
||||
function(c)
|
||||
c.ontop = not c.ontop
|
||||
end,
|
||||
{description = "toggle ontop", group = "client"}),
|
||||
k(":client.below",
|
||||
awful.key({global.modkey}, "b",
|
||||
function(c)
|
||||
c.below = not c.below
|
||||
end,
|
||||
{description = "toggle below", group = "client"}),
|
||||
k(":client.fullscreen",
|
||||
awful.key({global.modkey}, "f",
|
||||
function(c)
|
||||
c.fullscreen = not c.fullscreen
|
||||
c:raise()
|
||||
end,
|
||||
{description = "toggle fullscreen", group = "client"}),
|
||||
k(":client.minimize",
|
||||
awful.key({ global.modkey }, "n",
|
||||
function (c)
|
||||
c.minimized = true
|
||||
end ,
|
||||
{description = "minimize", group = "client"}),
|
||||
k(":client.maximize",
|
||||
awful.key({ global.modkey }, "m",
|
||||
function (c)
|
||||
c.maximized = not c.maximized
|
||||
c:raise()
|
||||
end ,
|
||||
{description = "(un)maximize", group = "client"}),
|
||||
k(":client.pin",
|
||||
function (c)
|
||||
c.sticky = not c.sticky
|
||||
end ,
|
||||
{description = "(un)pin", group = "client"}),
|
||||
k(":client.toggle_titlebars",
|
||||
function (c)
|
||||
c:emit_signal("titlebar::toggle")
|
||||
end ,
|
||||
{description = "(un)hide titlebars", group = "client"}),
|
||||
k(":client.swap_to_master",
|
||||
function (c)
|
||||
c:swap(awful.client.getmaster())
|
||||
end,
|
||||
{description = "swap with master", group = "client"}),
|
||||
k(":client.move_to_screen",
|
||||
function (c)
|
||||
c:move_to_screen()
|
||||
end,
|
||||
{description = "move to screen", group = "client"}))
|
||||
{description = "(un)maximize", group = "client"}))
|
||||
awful.rules.rules[1].properties.keys = clientkeys
|
||||
|
||||
local clientbuttons = gears.table.join(
|
||||
|
@ -176,7 +109,6 @@ local clientbuttons = gears.table.join(
|
|||
awful.button({ global.modkey }, 3, function (c)
|
||||
c:emit_signal("request::activate", "mouse_click", {raise = true})
|
||||
awful.mouse.client.resize(c)
|
||||
end)
|
||||
)
|
||||
end))
|
||||
awful.rules.rules[1].properties.buttons = clientbuttons
|
||||
|
||||
|
|
|
@ -5,18 +5,12 @@
|
|||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- garbage collection timer
|
||||
local gears = require("gears")
|
||||
gears.timer {
|
||||
timeout = 30,
|
||||
timeout = 10,
|
||||
autostart = true,
|
||||
callback = function()
|
||||
local out_string = tostring(os.date()) .. "\nLua memory usage:"..tostring(collectgarbage("count")).."\n"
|
||||
collectgarbage()
|
||||
out_string = out_string .. "\nLua memory usage after GC: "..tostring(collectgarbage("count")).."\n"
|
||||
out_string = out_string.."Objects alive:"
|
||||
for name, obj in pairs{button = button, client = client, drawable = drawable, drawin = drawin, key = key, screen = screen, tag = tag } do
|
||||
out_string = out_string .. "\n" .. tostring(name) .. " = "..tostring(obj.instances())
|
||||
end
|
||||
collectgarbage("step", 20000)
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- Compositor manager with inhibitor
|
||||
local awful = require("awful")
|
||||
-- Configuration variables
|
||||
local cfg = config.compositor or {}
|
||||
local compositor_enabled = false
|
||||
local inhibitors = {}
|
||||
local compositor_pid = nil
|
||||
local function count(t)
|
||||
local c = 0
|
||||
for _,_ in pairs(t) do
|
||||
c = c + 1
|
||||
end
|
||||
return c
|
||||
end
|
||||
if cfg.exec then
|
||||
compositor_pid = awful.spawn(cfg.exec)
|
||||
compositor_enabled = true
|
||||
client.connect_signal("manage",function(c)
|
||||
if c.inhibit_compositor then
|
||||
inhibitors[c] = true
|
||||
if compositor_enabled then
|
||||
awful.spawn("kill "..tostring(compositor_pid))
|
||||
compositor_enabled = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
client.connect_signal("unmanage",function(c)
|
||||
if c.inhibit_compositor then
|
||||
inhibitors[c] = nil
|
||||
if count(inhibitors) == 0 then
|
||||
if compositor_enabled == false then
|
||||
compositor_pid = awful.spawn(cfg.exec)
|
||||
compositor_enabled = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
awesome.connect_signal("exit",function()
|
||||
if compositor_enabled and compositor_pid then
|
||||
awful.spawn("kill "..tostring(compositor_pid))
|
||||
end
|
||||
end)
|
||||
end
|
|
@ -11,6 +11,8 @@ local wibox = require("wibox")
|
|||
local awful = require("awful")
|
||||
local beautiful = require("beautiful")
|
||||
local builder = require("builder")
|
||||
local mbarutils = require("menubar").utils
|
||||
local menugen = require("context_menu")
|
||||
local json = require("dkjson")
|
||||
|
||||
local function read_file(fname)
|
||||
|
@ -44,10 +46,66 @@ for k,v in pairs(json.decode(config) or {}) do
|
|||
end
|
||||
end -- }}}
|
||||
|
||||
|
||||
do -- {{{ Titlebars
|
||||
local titlebar_config = {}
|
||||
local style = awmtk2.create_style("titlebar",awmtk2.default,{})
|
||||
for k,v in pairs({"titlebar_top","titlebar_left","titlebar_right","titlebar_bottom"}) do
|
||||
-- Create styles for each titlebar
|
||||
style[v] = awmtk2.create_delta(v, {},
|
||||
(beautiful.widgets and beautiful.widgets.titlebar) or {},
|
||||
style.titlebar)
|
||||
-- Load in json layouts for titlebars
|
||||
titlebar_config[v] = read_file(root_path.."/themes/"..global.theme.."/config/"..v..".json")
|
||||
end
|
||||
local templates = awmtk2.create_template_lib("titlebar",awmtk2.templates,{})
|
||||
local t = awmtk2.build_templates(templates,style)
|
||||
-- Add titlebars to normal windows
|
||||
table.insert(awful.rules.rules,
|
||||
{ rule_any = {type = { "normal", "dialog" }
|
||||
}, properties = { titlebars_enabled = true }
|
||||
}
|
||||
)
|
||||
client.connect_signal("request::titlebars",function(c)
|
||||
local buttons = gears.table.join(
|
||||
awful.button({}, 1, function()
|
||||
c:emit_signal("request::activate","titlebar",{raise=true})
|
||||
awful.mouse.client.move(c)
|
||||
end),
|
||||
awful.button({}, 3, function()
|
||||
c:emit_signal("request::activate","titlebar",{raise=true})
|
||||
awful.mouse.client.resize(c)
|
||||
end)
|
||||
)
|
||||
for k,v in pairs({"titlebar_top","titlebar_bottom","titlebar_left","titlebar_right"}) do
|
||||
local contents = { widget = wibox.container.background }
|
||||
if titlebar_config[v] then
|
||||
contents = builder(titlebar_config[v],{
|
||||
client = c,
|
||||
screen = c.screen,
|
||||
style = style[v],
|
||||
buttons = buttons
|
||||
})
|
||||
end
|
||||
awful.titlebar(c,{
|
||||
size = style[v].size or 0,
|
||||
position = v:gsub("titlebar_",""),
|
||||
bg_normal = style[v].bg_normal,
|
||||
bg_focus = style[v].bg_focus,
|
||||
bgimage_normal = style[v].bgimage_normal,
|
||||
bgimage_focus = style[v].bgimage_focus,
|
||||
fg_normal = style[v].fg_normal,
|
||||
fg_focus = style[v].fg_focus,
|
||||
font = style[v].font
|
||||
}):setup(t.titlebar(contents))
|
||||
end
|
||||
end)
|
||||
end --}}}
|
||||
|
||||
do --{{{ Screen
|
||||
local wibar_config = {}
|
||||
local style = awmtk2.create_style("wibar",awmtk2.generic.composite_widget,{})
|
||||
for _,v in pairs({"wibar_top","wibar_bottom","wibar_left","wibar_right"}) do
|
||||
local style = awmtk2.create_style("wibar",awmtk2.default,{})
|
||||
for k,v in pairs({"wibar_top","wibar_bottom","wibar_left","wibar_right"}) do
|
||||
style[v] = awmtk2.create_delta(v, {},
|
||||
(beautiful.widgets and beautiful.widgets.wibar) or {},
|
||||
style.wibar)
|
||||
|
@ -57,7 +115,7 @@ local templates = awmtk2.create_template_lib("wibar",awmtk2.templates,{})
|
|||
local t = awmtk2.build_templates(templates,style)
|
||||
|
||||
awful.screen.connect_for_each_screen(function(s)
|
||||
for _,v in pairs({"wibar_top","wibar_bottom","wibar_left","wibar_right"}) do
|
||||
for k,v in pairs({"wibar_top","wibar_bottom","wibar_left","wibar_right"}) do
|
||||
local contents = { widget = wibox.container.background }
|
||||
if wibar_config[v] then
|
||||
contents = builder(wibar_config[v],{
|
||||
|
@ -79,8 +137,8 @@ awful.screen.connect_for_each_screen(function(s)
|
|||
border_color = style[v].border_color,
|
||||
opacity = style[v].opacity or 1,
|
||||
shape = style[v].shape,
|
||||
bg = style[v].bg_normal,
|
||||
bgimage = style[v].bgimage_normal,
|
||||
bg = style[v].bg,
|
||||
bgimage = style[v].bgimage,
|
||||
fg = style[v].fg,
|
||||
input_passthrough = style[v].input_passthrough
|
||||
})
|
||||
|
@ -89,170 +147,3 @@ awful.screen.connect_for_each_screen(function(s)
|
|||
end
|
||||
end)
|
||||
end -- }}}
|
||||
|
||||
do -- {{{ Titlebars
|
||||
local titlebar_config = {}
|
||||
local style = awmtk2.create_style("titlebar",awmtk2.generic.composite_widget,{})
|
||||
for _,v in pairs({"titlebar_top","titlebar_left","titlebar_right","titlebar_bottom"}) do
|
||||
-- Create styles for each titlebar
|
||||
style[v] = awmtk2.create_delta(v, {},
|
||||
(beautiful.widgets and beautiful.widgets.titlebar) or {},
|
||||
style.titlebar)
|
||||
-- Load in json layouts for titlebars
|
||||
titlebar_config[v] = read_file(root_path.."/themes/"..global.theme.."/config/"..v..".json")
|
||||
end
|
||||
local templates = awmtk2.create_template_lib("titlebar",awmtk2.templates,{})
|
||||
local t = awmtk2.build_templates(templates,style)
|
||||
-- Add titlebars to normal windows
|
||||
table.insert(awful.rules.rules,
|
||||
{ rule_any = {type = { "normal", "dialog" }
|
||||
}, properties = { titlebars_enabled = true }
|
||||
}
|
||||
)
|
||||
|
||||
local window_shape_hide = function(cr, width, height)
|
||||
return gears.shape.partially_rounded_rect(cr,width,height,
|
||||
false,false,false,false,0)
|
||||
end
|
||||
|
||||
local window_shape = beautiful.window_shape or function(cr, width, height)
|
||||
return gears.shape.partially_rounded_rect(cr,width,height,
|
||||
true,true,false,false,beautiful.window_rounding)
|
||||
end
|
||||
|
||||
client.connect_signal("manage", function(c)
|
||||
c.shape = window_shape
|
||||
end)
|
||||
|
||||
local titlebars_on = true
|
||||
awesome.connect_signal("titlebar::toggle",function()
|
||||
titlebars_on = not titlebars_on
|
||||
for _,c in ipairs(client.get()) do
|
||||
if titlebars_on then
|
||||
for _, pos in ipairs({"top","bottom","left","right"}) do
|
||||
awful.titlebar.show(c,pos)
|
||||
end
|
||||
c.shape = window_shape
|
||||
c:emit_signal("titlebar::perform_action",function(titlebar)
|
||||
titlebar.widget.visible = true
|
||||
end)
|
||||
else
|
||||
for _, pos in ipairs({"top","bottom","left","right"}) do
|
||||
awful.titlebar.hide(c,pos)
|
||||
end
|
||||
c.shape = window_shape_hide
|
||||
c:emit_signal("titlebar::perform_action",function(titlebar)
|
||||
titlebar.widget.visible = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Second manage call to create hidden titlebars
|
||||
client.connect_signal("manage",function(c)
|
||||
-- Drag and resize buttons
|
||||
local buttons = gears.table.join(
|
||||
awful.button({}, 1, function()
|
||||
c:emit_signal("request::activate","titlebar",{raise=true})
|
||||
awful.mouse.client.move(c)
|
||||
end),
|
||||
awful.button({}, 3, function()
|
||||
c:emit_signal("request::activate","titlebar",{raise=true})
|
||||
awful.mouse.client.resize(c)
|
||||
end)
|
||||
)
|
||||
-- Building the titlebars
|
||||
for k,v in pairs({"titlebar_top","titlebar_bottom","titlebar_left","titlebar_right"}) do
|
||||
-- Build content of the titlebar
|
||||
local contents = { widget = wibox.widget.textbox, text = "" }
|
||||
if titlebar_config[v] then
|
||||
contents = builder(titlebar_config[v],{
|
||||
client = c,
|
||||
screen = c.screen,
|
||||
style = style[v],
|
||||
buttons = buttons
|
||||
})
|
||||
end
|
||||
-- Create the base
|
||||
local titlebar = awful.titlebar(c,{
|
||||
size = style[v].size or 0,
|
||||
position = v:gsub("titlebar_",""),
|
||||
bg_normal = style[v].bg_normal,
|
||||
bg_focus = style[v].bg_focus,
|
||||
bgimage_normal = style[v].bgimage_normal,
|
||||
bgimage_focus = style[v].bgimage_focus,
|
||||
fg_normal = style[v].fg_normal,
|
||||
fg_focus = style[v].fg_focus,
|
||||
font = style[v].font
|
||||
})
|
||||
-- Compile and build titlebar
|
||||
titlebar:setup(t.titlebar({
|
||||
contents,
|
||||
widget = wibox.container.background
|
||||
}))
|
||||
-- Since new clients will be placed without titlebars, we need to apply placement rules again
|
||||
awful.rules.rules[1].properties.placement(c)
|
||||
-- Callbacks for focus/unfocus of titlebars
|
||||
if style[v].onfocus then
|
||||
c:connect_signal("focus",function()
|
||||
style[v].onfocus(titlebar)
|
||||
end)
|
||||
end
|
||||
if style[v].onunfocus then
|
||||
c:connect_signal("unfocus",function()
|
||||
style[v].onunfocus(titlebar)
|
||||
end)
|
||||
end
|
||||
-- Activate focus callback if our client is focused
|
||||
if (c == client.focus) and (style[v].onfocus) then
|
||||
style[v].onfocus(titlebar)
|
||||
end
|
||||
-- Add a titlebar toggle signal
|
||||
c:connect_signal("titlebar::toggle",function(c)
|
||||
titlebar.widget.visible = not titlebar.widget.visible
|
||||
if titlebar.widget.visible then
|
||||
awful.titlebar.show(c,v:gsub("titlebar_",""))
|
||||
c.shape = window_shape
|
||||
else
|
||||
awful.titlebar.hide(c,v:gsub("titlebar_",""))
|
||||
c.shape = window_shape_hide
|
||||
end
|
||||
end)
|
||||
c:connect_signal("titlebar::perform_action",function(c,f)
|
||||
f(titlebar)
|
||||
end)
|
||||
-- Add rules for hiding titlebars on creation
|
||||
if (not titlebars_on) or
|
||||
(c.titlebars_enabled == false) or
|
||||
(c.requests_no_titlebar == true) then
|
||||
titlebar.widget.visible = false
|
||||
c.shape = window_shape_hide
|
||||
awful.titlebar.hide(c,v:gsub("titlebar_",""))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end --}}}
|
||||
|
||||
-- Find a client to focus on when switching tags
|
||||
for s in screen do
|
||||
for _,t in pairs(awful.tag.gettags(s)) do
|
||||
t:connect_signal("property::selected", function(t)
|
||||
if not client.focus then
|
||||
local c = t:clients()[1]
|
||||
if c then
|
||||
client.focus = c
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
-- Focus on the previous client once the newer client is closed
|
||||
client.connect_signal("unmanage",function(c)
|
||||
local focused_clients = screen.primary.clients
|
||||
local prev
|
||||
for _,v in pairs(focused_clients) do
|
||||
if v == c then break end
|
||||
prev = v
|
||||
end
|
||||
client.focus = prev
|
||||
end)
|
||||
|
|
|
@ -6,14 +6,10 @@
|
|||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- Global settings
|
||||
local conf = require("parsers").conf
|
||||
local envsub = require("utils").substitute_env
|
||||
local conf_file = io.open(root_path.."/desktop.conf","r")
|
||||
if not conf_file then
|
||||
error("desktop.conf is missing or not readable")
|
||||
end
|
||||
config = conf(conf_file:read("*a"))
|
||||
conf_file:close()
|
||||
global = config.global
|
||||
global.terminal = envsub(global.terminal)
|
||||
global.browser = envsub(global.browser)
|
||||
global = {}
|
||||
global.terminal = os.getenv("HOME").."/.local/bin/st" --Mod+Enter (spawn terminal)
|
||||
global.browser = "prime-run librewolf" --Mod+Shift+Enter (spawn browser)
|
||||
global.modkey = "Mod4" -- Default modifier key
|
||||
global.theme = "reno98"
|
||||
global.shell = "zsh"
|
||||
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local ask = require("asckey")
|
||||
local fs = gears.filesystem
|
||||
|
||||
_G.MacroTimer = {}
|
||||
_G.MacroActive = {}
|
||||
|
||||
local mconf = config.macros
|
||||
|
||||
local macro_loop = false
|
||||
local macro_delay = mconf.step or 10
|
||||
local macro_base_dir = mconf.base_dir or (root_path.."/macros/")
|
||||
|
||||
if not fs.dir_readable(root_path.."/macros/") then
|
||||
fs.make_directories(root_path.."/macros/")
|
||||
end
|
||||
|
||||
awesome.connect_signal("macro::record",function(filename)
|
||||
if not filename then return end
|
||||
local timeline = "loop="..tostring(macro_loop).."\n"..
|
||||
"delay="..tostring(macro_delay).."\n"
|
||||
require("naughty").notify({title =
|
||||
"Recording macro "..
|
||||
tostring(filename)..
|
||||
" (Escape to stop)"
|
||||
})
|
||||
awful.keygrabber {
|
||||
autostart = true,
|
||||
stop_key = "Escape",
|
||||
keypressed_callback = function(self, mod, key, event)
|
||||
if key == " " then
|
||||
key = "space"
|
||||
end
|
||||
timeline = timeline.."+"..key.."\n"
|
||||
end,
|
||||
keyreleased_callback = function(self, mod, key, event)
|
||||
if key == " " then
|
||||
key = "space"
|
||||
end
|
||||
timeline = timeline.."-"..key.."\n"
|
||||
end,
|
||||
stop_callback = function()
|
||||
local macrofile = io.open(filename,"w")
|
||||
if macrofile then
|
||||
macrofile:write(timeline)
|
||||
macrofile:close()
|
||||
require("naughty").notify({title="Macro saved as "..tostring(filename)})
|
||||
else
|
||||
require("naughty").notify({title="Failed to save macro"})
|
||||
end
|
||||
-- For some reason after keygrabber is done input doesn't work
|
||||
gears.timer.delayed_call(function()
|
||||
awful.keygrabber:stop()
|
||||
end)
|
||||
end
|
||||
}
|
||||
end)
|
||||
|
||||
awesome.connect_signal("macro::play",function(filename)
|
||||
if (not filename) or (not fs.file_readable(filename)) then
|
||||
return
|
||||
end
|
||||
local macrofile = io.open(filename,"r")
|
||||
local macrodata = macrofile:read("*a")
|
||||
macrofile:close()
|
||||
local macro = {}
|
||||
local step = 0
|
||||
macrodata:gsub("([^\n]+)",function(line)
|
||||
table.insert(macro,line)
|
||||
end)
|
||||
local delay = tonumber(macro[2]:match("delay=(%d+)"))
|
||||
_G.MacroActive[filename] = (macro[1]:match("loop=(.+)") == "true")
|
||||
table.remove(macro,2)
|
||||
table.remove(macro,1)
|
||||
local macro_length = #macro
|
||||
_G.MacroTimer[filename] = gears.timer.start_new((1/1000)*delay,function()
|
||||
step = step + 1
|
||||
local instruction = macro[step]
|
||||
local event = (instruction:match("^%+") and "key_press") or "key_release"
|
||||
root.fake_input(event,instruction:match("^[%+%-](.*)"))
|
||||
if (step == macro_length) and (_G.MacroActive[filename]) then
|
||||
step = 0
|
||||
return true
|
||||
elseif (step < macro_length) then
|
||||
return true
|
||||
else
|
||||
_G.MacroTimer[filename] = nil
|
||||
_G.MacroActive[filename] = nil
|
||||
return false
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
root.keys(gears.table.join(
|
||||
root.keys(),
|
||||
ask.k(":macro.record_1", function()
|
||||
awesome.emit_signal("macro::record",macro_base_dir.."macro1")
|
||||
end,{group="macros",description="Record macro 1"}),
|
||||
ask.k(":macro.record_2", function()
|
||||
awesome.emit_signal("macro::record",macro_base_dir.."macro2")
|
||||
end,{group="macros",description="Record macro 2"}),
|
||||
ask.k(":macro.record_3", function()
|
||||
awesome.emit_signal("macro::record",macro_base_dir.."macro3")
|
||||
end,{group="macros",description="Record macro 3"}),
|
||||
ask.k(":macro.record_4", function()
|
||||
awesome.emit_signal("macro::record",macro_base_dir.."macro4")
|
||||
end,{group="macros",description="Record macro 4"}),
|
||||
ask.k(":macro.record_5", function()
|
||||
awesome.emit_signal("macro::record",macro_base_dir.."macro5")
|
||||
end,{group="macros",description="Record macro 5"}),
|
||||
ask.k(":macro.play_1", function()
|
||||
if not _G.MacroActive[macro_base_dir.."macro1"] then
|
||||
awesome.emit_signal("macro::play",macro_base_dir.."macro1")
|
||||
else
|
||||
_G.MacroActive[macro_base_dir.."macro1"] = false
|
||||
end
|
||||
end,{group="macros",description="Play macro 1",release_pre=true}),
|
||||
ask.k(":macro.play_2", function()
|
||||
if not _G.MacroActive[macro_base_dir.."macro2"] then
|
||||
awesome.emit_signal("macro::play",macro_base_dir.."macro2")
|
||||
else
|
||||
_G.MacroActive[macro_base_dir.."macro2"] = false
|
||||
end
|
||||
end,{group="macros",description="Play macro 2",release_pre=true}),
|
||||
ask.k(":macro.play_3", function()
|
||||
if not _G.MacroActive[macro_base_dir.."macro3"] then
|
||||
awesome.emit_signal("macro::play",macro_base_dir.."macro3")
|
||||
else
|
||||
_G.MacroActive[macro_base_dir.."macro3"] = false
|
||||
end
|
||||
end,{group="macros",description="Play macro 3",release_pre=true}),
|
||||
ask.k(":macro.play_4", function()
|
||||
if not _G.MacroActive[macro_base_dir.."macro4"] then
|
||||
awesome.emit_signal("macro::play",macro_base_dir.."macro4")
|
||||
else
|
||||
_G.MacroActive[macro_base_dir.."macro4"] = false
|
||||
end
|
||||
end,{group="macros",description="Play macro 4",release_pre=true}),
|
||||
ask.k(":macro.play_5", function()
|
||||
if not _G.MacroActive[macro_base_dir.."macro5"] then
|
||||
awesome.emit_signal("macro::play",macro_base_dir.."macro5")
|
||||
else
|
||||
_G.MacroActive[macro_base_dir.."macro5"] = false
|
||||
end
|
||||
end,{group="macros",description="Play macro 5",release_pre=true}),
|
||||
ask.k(":macro.loop",function()
|
||||
macro_loop = not macro_loop
|
||||
if macro_loop then
|
||||
require("naughty").notify({title="Macro looping turned on"})
|
||||
else
|
||||
require("naughty").notify({title="Macro looping turned off"})
|
||||
end
|
||||
end,{group="macros",description="turn looping on/off"})
|
||||
))
|
|
@ -1,75 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
-- Powerman X - second generation of the power management module
|
||||
local awful = require("awful")
|
||||
local sysctl = require("syscontrol")
|
||||
local naughty = require("naughty")
|
||||
local gears = require("gears")
|
||||
local batteries = sysctl.power_supply.enumerate()
|
||||
local state_tracking = {}
|
||||
-- Configuration variables
|
||||
local cfg = config.powerman or {}
|
||||
local quality_min = cfg.battery_quality_min or 33
|
||||
local capacity_min = cfg.battery_capacity_min or 15
|
||||
local on_low_battery = cfg.on_low_battery or ""
|
||||
local on_charged_battery = cfg.on_charged_battery
|
||||
local on_critical_condition = cfg.on_critical_condition
|
||||
-- Main loop
|
||||
gears.timer({
|
||||
timeout = 2,
|
||||
autostart = true,
|
||||
callback = function()
|
||||
for _,v in pairs(batteries) do
|
||||
local data,_ = sysctl.power_supply.read_attribs(v)
|
||||
state_tracking[v] = state_tracking[v] or {}
|
||||
if data.type == "Battery" then
|
||||
if (tonumber(data.quality) < quality_min) and
|
||||
(not state_tracking[v].quality_notification) then
|
||||
naughty.notify({
|
||||
title = "Critical battery condition",
|
||||
text = "Battery "..data.name.." has reached critically low condition, seek a suitable replacement"
|
||||
})
|
||||
state_tracking[v].quality_notification = true
|
||||
if on_critical_condition then
|
||||
awful.spawn(on_critical_condition)
|
||||
end
|
||||
end
|
||||
if (tonumber(data.capacity) <= capacity_min) and
|
||||
(not data.charging) and
|
||||
(not state_tracking[v].capacity_notification) then
|
||||
naughty.notify({
|
||||
title = "Battery capacity low",
|
||||
text = "Battery "..data.name.." capacity is at "..tostring(data.capacity).."%"
|
||||
})
|
||||
state_tracking[v].capacity_notification = true
|
||||
if on_low_battery then
|
||||
awful.spawn(on_low_battery)
|
||||
end
|
||||
end
|
||||
if (tonumber(data.capacity) > capacity_min) then
|
||||
state_tracking[v].capacity_notification = false
|
||||
end
|
||||
if (data.capacity == "100") and
|
||||
(data.charging) and
|
||||
(not state_tracking[v].charged_notification) then
|
||||
naughty.notify({
|
||||
title = "Battery is completely charged",
|
||||
text = "Disconnect the charger from the power grid to avoid passive electricity usage."
|
||||
})
|
||||
if on_charged_battery then
|
||||
awful.spawn(on_charged_battery)
|
||||
end
|
||||
end
|
||||
if (not data.charging) then
|
||||
state_tracking[v].charged_notification = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
awful.rules.rules = gears.table.join(awful.rules.rules, {
|
||||
{ rule_any = { class = {
|
||||
"steam_app_548430",
|
||||
"steam_app_%d*",
|
||||
"love",
|
||||
"^Minecraft.*"
|
||||
}},
|
||||
properties = {inhibit_compositor = true},
|
||||
},
|
||||
{ rule = { name = "notificationtoasts.*" },
|
||||
properties = {
|
||||
focusable = false
|
||||
}
|
||||
},
|
||||
{ rule = { fullscreen = true },
|
||||
properties = {
|
||||
titlebars_enabled = false,
|
||||
floating = true,
|
||||
border_width = 0,
|
||||
border_color = 0,
|
||||
size_hints_honor = false,
|
||||
placement=awful.placement.no_offscreen
|
||||
},
|
||||
callback = function(c)
|
||||
gears.timer.delayed_call(function()
|
||||
if c.valid then
|
||||
c:geometry(c.screen.geometry)
|
||||
end
|
||||
end)
|
||||
end
|
||||
}
|
||||
})
|
|
@ -19,6 +19,14 @@ awful.screen.connect_for_each_screen(function(s)
|
|||
end)
|
||||
-- keybindings for tags
|
||||
local keys = root.keys()
|
||||
keys = gears.table.join(keys,
|
||||
awful.key({global.modkey}, "Left",
|
||||
awful.tag.viewprev,
|
||||
{description = "view next tag", group = "tag"}),
|
||||
awful.key({global.modkey}, "Right",
|
||||
awful.tag.viewnext,
|
||||
{description = "view previous tag", group = "tag"}))
|
||||
|
||||
for i = 1,9 do
|
||||
keys = gears.table.join(keys,
|
||||
awful.key({global.modkey}, "#"..i+9,
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
-- This file is part of Reno desktop.
|
||||
--
|
||||
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
--
|
||||
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
--
|
||||
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
|
||||
local awful = require("awful")
|
||||
|
||||
awful.layout.layouts = {
|
||||
awful.layout.suit.floating,
|
||||
awful.layout.suit.tile,
|
||||
awful.layout.suit.tile.left,
|
||||
awful.layout.suit.tile.bottom,
|
||||
awful.layout.suit.tile.top,
|
||||
awful.layout.suit.fair,
|
||||
}
|
||||
|
||||
|
|
@ -8,61 +8,100 @@
|
|||
-- Asynchronous XDG data aggregator
|
||||
local menu_utils = require("menubar.utils")
|
||||
local menu_gen = require("menubar.menu_gen")
|
||||
local awful = require("awful")
|
||||
local gears = require("gears")
|
||||
local json = require("dkjson")
|
||||
local lib = require("xdg_data")
|
||||
menu_utils.wm_name = ""
|
||||
|
||||
-- Directories to scan for .desktop files
|
||||
local desktop_dirs = {os.getenv("HOME").."/Desktop"}
|
||||
local desktop_dirs = {}
|
||||
local desktop_dirs_complete = 0
|
||||
local _ = ((table.concat(gears.filesystem.get_xdg_data_dirs(),":") or
|
||||
"/usr/share:/usr/local/share")..":"..os.getenv("HOME").."/.local/share"):gsub("[^:]*",function(path)
|
||||
local icon_dirs = {}
|
||||
(os.getenv("XDG_DATA_DIRS")..":/home/yessiest/.local/share"):gsub("[^:]*",function(path)
|
||||
if gears.filesystem.dir_readable(path.."/applications") then
|
||||
table.insert(desktop_dirs, path.."/applications")
|
||||
end
|
||||
if gears.filesystem.dir_readable(path.."/icons") then
|
||||
table.insert(icon_dirs, path.."/icons")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
-- Global xdg data struct
|
||||
_G.xdg = lib.init_xdg_struct()
|
||||
-- Load cached applications
|
||||
local cache = lib.load_xdg_cache()
|
||||
-- Add missing category entries as defined by awesome
|
||||
lib.add_categories(xdg,menu_gen.all_categories)
|
||||
-- Global xdg data cache
|
||||
xdg = {
|
||||
apps = {},
|
||||
categories = {
|
||||
Other = {
|
||||
icon = "applications-other",
|
||||
apps = {}
|
||||
},
|
||||
Wine = {
|
||||
icon = "wine",
|
||||
apps = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k,v in pairs(menu_gen.all_categories) do
|
||||
xdg.categories[v.app_type] = {
|
||||
name = v.name,
|
||||
icon = v.icon_name,
|
||||
apps = {}
|
||||
}
|
||||
end
|
||||
|
||||
-- Asynchronous scanning process
|
||||
lib.async_process_dirs(xdg,desktop_dirs,cache,function(v)
|
||||
-- Count completed directory
|
||||
desktop_dirs_complete = desktop_dirs_complete + 1
|
||||
for k,v in pairs(desktop_dirs) do
|
||||
awful.spawn.with_line_callback("find "..tostring(v).." -name *.desktop",{
|
||||
stdout = function(line)
|
||||
local data = menu_utils.parse_desktop_file(line)
|
||||
if data.NoDisplay then
|
||||
return
|
||||
end
|
||||
local appdata = {
|
||||
name = data.Name,
|
||||
category = "Other",
|
||||
exec = data.Exec,
|
||||
icon = (data.Icon and menu_utils.lookup_icon(data.Icon)),
|
||||
description = data.Comment
|
||||
}
|
||||
-- Match first available cateogry for sorting
|
||||
for k,v in pairs(data.Categories or {"Other"}) do
|
||||
if xdg.categories[v] then
|
||||
appdata.category = v
|
||||
break
|
||||
end
|
||||
-- Oh how do I love those Wine applications and their categories
|
||||
if v:match("^Wine") then
|
||||
appdata.category = "Wine"
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Open terminal apps in the terminal (duh)
|
||||
if data.Terminal then
|
||||
appdata.exec = global.terminal.." -e "..appdata.exec
|
||||
end
|
||||
-- Just for you, Wine - special case because you're being a shit
|
||||
if (exec and exec:find("%W?wine ")) then
|
||||
appdata.category = "Wine"
|
||||
end
|
||||
table.insert(xdg.apps,appdata)
|
||||
table.insert(xdg.categories[appdata.category].apps,appdata)
|
||||
end,
|
||||
output_done = function()
|
||||
-- Call a global signal
|
||||
desktop_dirs_complete = desktop_dirs_complete + 1
|
||||
awesome.emit_signal("xdg::dir_finished",v)
|
||||
end)
|
||||
|
||||
local count = function(t)
|
||||
local n = 0
|
||||
for _,_ in pairs(t) do
|
||||
n = n + 1
|
||||
end
|
||||
return n
|
||||
})
|
||||
end
|
||||
|
||||
awesome.connect_signal("xdg::dir_finished",function(dir)
|
||||
-- We only send the all_finished signal when all directories finished processing
|
||||
if desktop_dirs_complete == #desktop_dirs then
|
||||
awesome.emit_signal("xdg::all_finished")
|
||||
-- Clean up empty categories
|
||||
for k,v in pairs(xdg.categories) do
|
||||
if count(v.apps) == 0 then
|
||||
if #v.apps == 0 then
|
||||
xdg.categories[k] = nil
|
||||
end
|
||||
end
|
||||
-- Save the cache if it doesn't exist yet
|
||||
io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","w"):write(json.encode(xdg)):close()
|
||||
-- Finally, call the all_finished signal
|
||||
awesome.emit_signal("xdg::all_finished")
|
||||
end
|
||||
end)
|
||||
|
||||
-- Before exiting, save all xdg cache
|
||||
awesome.connect_signal("exit",function()
|
||||
io.open(gears.filesystem.get_xdg_cache_home()..".reno_xdg_cache.json","w"):write(json.encode(xdg)):close()
|
||||
end)
|
||||
|
|
8
rc.lua
|
@ -16,14 +16,10 @@ package.cpath = package.cpath
|
|||
-- Modules list
|
||||
require("modules.collect_garbage")
|
||||
require("modules.global")
|
||||
require("modules.powermanX")
|
||||
require("modules.errorlog")
|
||||
require("modules.base")
|
||||
require("modules.autostart")
|
||||
require("modules.rules_stub")
|
||||
require("modules.compositor")
|
||||
require("modules.binds")
|
||||
require("modules.xdg_data")
|
||||
require("modules.autostart")
|
||||
require("modules.static_tags")
|
||||
require("modules.tiling")
|
||||
require("modules.desktop")
|
||||
require("modules.macros")
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
Notice: all assets in the layouts directory and taglist directory, as well as submenu.png, belong to the AwesomeWM team and respective authors of these assets.
|
||||
|
||||
The following assets in the titlebar directory are made specifically for reno98 and are licensed under CC0:
|
||||
close_focus.png
|
||||
close_normal.png
|
||||
maximized_focus_inactive.png
|
||||
maximized_normal_inactive.png
|
||||
maximized_normal_active.png
|
||||
minimize_focus.png
|
||||
minimize_normal.png
|
||||
|
||||
Other assets in the directory are kept from the default theme of AwesomeWM and belong to the respecitve authors.
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
"list": [
|
||||
{"widget": "widgets.clientmenu.volume","vertical":false},
|
||||
{"widget": "widgets.clientmenu.controls"},
|
||||
{"widget": "widgets.clientmenu.buttons"}
|
||||
{"widget": "widgets.clientcontrols"},
|
||||
{"widget": "widgets.clientbuttons"}
|
||||
],
|
||||
"vertical":true
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"widgets.dismal":{
|
||||
"pos":"top_left",
|
||||
"ignore_wibars":false
|
||||
"x":0,
|
||||
"y":26
|
||||
},
|
||||
"widgets.rootmenu":{},
|
||||
"widgets.lockscreen":{},
|
||||
"widgets.base.keypopup":{}
|
||||
"widgets.lockscreen":{}
|
||||
}
|
||||
|
|
|
@ -4,28 +4,20 @@
|
|||
"options":{
|
||||
"icon":"icons/reno98.png",
|
||||
"title":"Reno 98"
|
||||
}
|
||||
},
|
||||
"vertical":true
|
||||
},
|
||||
{
|
||||
"list": [
|
||||
{"widget": "widgets.base.tagswitcher",
|
||||
"screen":true
|
||||
},
|
||||
{"multimenu": [
|
||||
{"widget": "widgets.rootmenu.controls"},
|
||||
{"widget": "widgets.rootcontrols"},
|
||||
{"widget": "widgets.xdgmenu",
|
||||
"options": {
|
||||
"exclude_category": [
|
||||
"Other"
|
||||
]
|
||||
}
|
||||
}
|
||||
]},
|
||||
{"widget": "widgets.rootmenu.buttons"}
|
||||
},
|
||||
{"widget": "widgets.rootbuttons"}
|
||||
],
|
||||
"vertical": true
|
||||
}
|
||||
],
|
||||
"vertical": false
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
"align": {
|
||||
"left": [
|
||||
{
|
||||
"widget": "widgets.desktop.tasklist",
|
||||
"widget": "widgets.launcher"
|
||||
},
|
||||
{
|
||||
"widget": "widgets.tasklist",
|
||||
"screen": true
|
||||
}
|
||||
],
|
||||
|
@ -10,9 +13,6 @@
|
|||
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"widget": "widgets.desktop.launcher"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,29 @@
|
|||
{
|
||||
"align": {
|
||||
"left": [
|
||||
{ "widget": "widgets.desktop.soundclown" }
|
||||
{ "widget": "widgets.soundclown" }
|
||||
],
|
||||
"center": [
|
||||
|
||||
],
|
||||
"right": [
|
||||
{ "widget":"widgets.desktop.volume" },
|
||||
{ "widget": "widgets.desktop.notifications",
|
||||
{ "widget": "widgets.notifications",
|
||||
"screen": true
|
||||
},
|
||||
{ "widget": "widgets.desktop.wallpapers",
|
||||
{ "widget": "widgets.wallpapers",
|
||||
"screen": true,
|
||||
"options": {
|
||||
"path": "$HOME/Pictures/Wallpapers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget": "widgets.base.subpanel",
|
||||
"layout": {"list":[
|
||||
{ "widget": "widgets.base.layout" },
|
||||
{ "widget": "widgets.desktop.battery" },
|
||||
{ "widget": "widgets.base.subpanel",
|
||||
"layout": {
|
||||
"list": [
|
||||
{ "widget": "widgets.battery" },
|
||||
{ "widget": "widgets.base.systray" },
|
||||
{ "widget": "widgets.base.clock" }
|
||||
]}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Licensed under conditions of CC0 (https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt)
|
||||
|
||||
To the extent possible under law, Yessiest (yessiest@memeware.net) has waived all copyright and related or neighboring rights to Reno icons
|
||||
To the extent possible under law, Yessiest (yessiest@memeware.net) has waived all copyright and related or neighboring rights to Reno98 icons
|
||||
|
|
Before Width: | Height: | Size: 455 B |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 455 B |
Before Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 682 B |
Before Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 751 B |
Before Width: | Height: | Size: 642 B |
|
@ -24,7 +24,6 @@ theme.bg_normal = "#c0c0c0"
|
|||
theme.bg_focus = "#808080"
|
||||
theme.bg_urgent = "#FFEDCC"
|
||||
theme.bg_minimize = "#efefef"
|
||||
theme.bg_highlight = "#dadada"
|
||||
theme.bg_highlight_shadow = "#000000FF"
|
||||
theme.bg_highlight_light = "#FFFFFFFF"
|
||||
theme.bg_highlight_outline = "#808080FF"
|
||||
|
@ -35,7 +34,7 @@ theme.fg_focus = "#000000"
|
|||
theme.fg_urgent = "#000000"
|
||||
theme.fg_minimize = "#000000"
|
||||
|
||||
theme.useless_gap = dpi(10)
|
||||
theme.useless_gap = dpi(0)
|
||||
-- technically speaking these are irrelevant since they're not exactly smart
|
||||
-- borders
|
||||
theme.border_width = dpi(0)
|
||||
|
@ -43,8 +42,6 @@ theme.border_normal = "#c0c0c0"
|
|||
theme.border_focus = "#c0c0c0"
|
||||
theme.border_marked = "#c0c0c0"
|
||||
|
||||
theme.window_rounding = 0
|
||||
|
||||
theme.titlebar_bg_focus = {
|
||||
type = "linear",
|
||||
from = { 0, 0 },
|
||||
|
@ -126,11 +123,6 @@ theme.awesome_icon = theme_assets.awesome_icon(
|
|||
theme.menu_height, theme.bg_focus, theme.fg_focus
|
||||
)
|
||||
|
||||
theme.hotkeys_border_width = 3
|
||||
theme.hotkeys_border_color = theme.bg_focus
|
||||
theme.hotkeys_modifiers_fg = theme.fg_normal
|
||||
theme.hotkeys_label_fg = theme.fg_normal
|
||||
theme_assets.recolor_layout(theme,theme.fg_normal)
|
||||
theme.bgimage_outset = function(context, cr, width, height,...)
|
||||
local light = gears.color(theme.bg_highlight_light)
|
||||
local shadow = gears.color(theme.bg_highlight_shadow)
|
||||
|
@ -325,7 +317,6 @@ theme["battery-missing-symbolic"] = themes_path.."reno98/icons/battery-missing-s
|
|||
theme["ac-adapter-symbolic"] = themes_path.."reno98/icons/ac-adapter-symbolic.png"
|
||||
theme["backlight-symbolic"] = themes_path.."reno98/icons/backlight-symbolic.png"
|
||||
theme["notifications-area-symbolic"] = themes_path.."reno98/icons/notifications-area-symbolic.png"
|
||||
theme["notifications-area-flagged-symbolic"] = themes_path.."reno98/icons/notifications-area-flagged-symbolic.png"
|
||||
|
||||
theme["mpc-previous-symbolic"] = themes_path.."reno98/icons/mpc-previous-symbolic.png"
|
||||
|
||||
|
@ -336,31 +327,24 @@ theme["mpc-next-symbolic"] = themes_path.."reno98/icons/mpc-next-symbolic.png"
|
|||
theme["action-poweroff-symbolic"] = themes_path.."reno98/icons/action-poweroff-symbolic.png"
|
||||
theme["action-lock-screen-symbolic"] = themes_path.."reno98/icons/action-lock-screen-symbolic.png"
|
||||
theme["action-suspend-symbolic"] = themes_path.."reno98/icons/action-suspend-symbolic.png"
|
||||
theme["volume-high-symbolic"] = themes_path.."reno98/icons/volume-high-symbolic.png"
|
||||
theme["volume-medium-symbolic"] = themes_path.."reno98/icons/volume-medium-symbolic.png"
|
||||
theme["volume-low-symbolic"] = themes_path.."reno98/icons/volume-low-symbolic.png"
|
||||
theme["volume-muted-symbolic"] = themes_path.."reno98/icons/volume-muted-symbolic.png"
|
||||
|
||||
-- Notification popups settings
|
||||
theme.notification_width = 240
|
||||
theme.notification_height = 60
|
||||
theme.notification_width = 200
|
||||
theme.notification_height = 40
|
||||
|
||||
-- Default icon for clients
|
||||
-- This one has to be baked as a surface to avoid memory leaks
|
||||
theme.icon_default = gears.surface(themes_path.."reno98/icons/unknown-app.png")
|
||||
|
||||
theme.widgets = {
|
||||
-- {{{ Widget base
|
||||
default = {
|
||||
container = {
|
||||
bgimage_normal = theme.bgimage_highlight,
|
||||
bgimage = theme.bgimage_highlight,
|
||||
shape = function(cr,width,height)
|
||||
return require("gears").shape.rounded_rect(cr,width,height,0)
|
||||
end,
|
||||
bgimage_highlight = theme.bgimage_inset
|
||||
end
|
||||
},
|
||||
button = {
|
||||
bgimage_normal = theme.bgimage_outset,
|
||||
bgimage = theme.bgimage_outset,
|
||||
shape = function(cr,width,height)
|
||||
return require("gears").shape.rounded_rect(cr,width,height,0)
|
||||
end,
|
||||
|
@ -374,14 +358,13 @@ theme.widgets = {
|
|||
end
|
||||
},
|
||||
popup = {
|
||||
bgimage_normal = theme.bgimage_outset,
|
||||
bgimage = theme.bgimage_outset,
|
||||
shape = function(cr,width,height)
|
||||
return gears.shape.rounded_rect(cr,width,height,0)
|
||||
end,
|
||||
},
|
||||
titlebar = {
|
||||
hidden_size = 2,
|
||||
bgimage_normal = theme.bgimage_outset,
|
||||
bgimage = theme.bgimage_outset,
|
||||
--margins = 5,
|
||||
left = 4,
|
||||
right = 5,
|
||||
|
@ -396,19 +379,9 @@ theme.widgets = {
|
|||
shape = function(cr,width,height)
|
||||
return gears.shape.rounded_rect(cr,width,height,0)
|
||||
end,
|
||||
bgimage_normal = theme.bgimage_outset,
|
||||
bgimage = theme.bgimage_outset,
|
||||
stretch = true
|
||||
},
|
||||
checkbox = {
|
||||
width = 15,
|
||||
height = 15,
|
||||
shape = gears.shape.circle,
|
||||
border_width = 2,
|
||||
border_color = theme.bg_normal,
|
||||
bg = theme.bg_highlight,
|
||||
check_color = "#000000",
|
||||
paddings = 2
|
||||
},
|
||||
slider = {
|
||||
shape = function(cr,width,height)
|
||||
return gears.shape.rounded_rect(cr,width,height,0)
|
||||
|
@ -421,114 +394,83 @@ theme.widgets = {
|
|||
bar_height = 6
|
||||
}
|
||||
},
|
||||
-- }}}
|
||||
-- {{{ Menus
|
||||
generic_menu = {
|
||||
base = {
|
||||
spacing = 2,
|
||||
menu_slide = true
|
||||
},
|
||||
button = {
|
||||
forced_height = 20,
|
||||
forced_width = 160
|
||||
},
|
||||
},
|
||||
--}}}
|
||||
-- {{{ Bars/Panels/Menu popups
|
||||
generic_composite_widget = {
|
||||
xdg_menu = {
|
||||
base = {
|
||||
spacing = 2
|
||||
}
|
||||
},
|
||||
-- }}}
|
||||
-- {{{ Status panel widgets
|
||||
generic_status_widget = {
|
||||
container = {
|
||||
bgimage_normal = function() end,
|
||||
margins = 0
|
||||
},
|
||||
battery = {
|
||||
button = {
|
||||
margins = 0,
|
||||
onpress = function() end,
|
||||
onrelease = function() end,
|
||||
bgimage_normal = function() end
|
||||
bgimage = function() end
|
||||
}
|
||||
},
|
||||
-- }}}
|
||||
-- {{{ Various button lists
|
||||
generic_button_list = {
|
||||
button = {
|
||||
forced_width = 20,
|
||||
forced_height = 20
|
||||
soundclown = {
|
||||
base = {
|
||||
width = 140
|
||||
},
|
||||
container = {
|
||||
bgimage = theme.bgimage_inset,
|
||||
bg = "#9CA875"
|
||||
}
|
||||
},
|
||||
notifications = {
|
||||
button = {
|
||||
height = 40,
|
||||
width = 200
|
||||
},
|
||||
article = {
|
||||
icon_size = 50,
|
||||
title_valign = "top",
|
||||
desc_valign = "top"
|
||||
}
|
||||
},
|
||||
clock = {
|
||||
container = {
|
||||
bgimage = function() end,
|
||||
margins = 0
|
||||
}
|
||||
},
|
||||
root_menu = {
|
||||
base = {
|
||||
spacing = 2
|
||||
}
|
||||
},
|
||||
-- }}}
|
||||
-- {{{ All widgets that fit into a single line
|
||||
generic_oneline_widget = {
|
||||
container = {
|
||||
bgimage_normal = theme.bgimage_inset
|
||||
}
|
||||
},
|
||||
-- }}}
|
||||
-- {{{ All kinds of widget popups
|
||||
generic_popup = {
|
||||
button = {
|
||||
width = 180,
|
||||
height = 40
|
||||
},
|
||||
article = {
|
||||
icon_size = 30
|
||||
},
|
||||
},
|
||||
-- }}}
|
||||
popuptitle = {
|
||||
container = {
|
||||
bg_normal = {
|
||||
type = "linear",
|
||||
from = { 0, 0 },
|
||||
to = { 90, 0 },
|
||||
stops = { {0, "#040582"}, {1, "#0F7FCD"} }
|
||||
},
|
||||
-- awesomewm: yo pass me that pango markup
|
||||
-- pango: you better not make unintuitive cryptic shit
|
||||
-- awesomewm: *attaches foreground text color setting to container*
|
||||
fg_normal = "#FAFAFA"
|
||||
}
|
||||
},
|
||||
soundclown = {
|
||||
--[[ --Uncomment to leetify that MPC
|
||||
container = {
|
||||
bg_normal = "#0c0c0c",
|
||||
fg_normal = "#00FF00"
|
||||
},
|
||||
]]
|
||||
client_menu = {
|
||||
base = {
|
||||
width = 140
|
||||
},
|
||||
},
|
||||
subpanel = {
|
||||
container = {
|
||||
bgimage_normal = theme.bgimage_inset,
|
||||
bg_normal = theme.bg_normal,
|
||||
margins = 2
|
||||
spacing = 2
|
||||
}
|
||||
},
|
||||
taglist = {
|
||||
base = {
|
||||
spacing = 2,
|
||||
layout = require("wibox").layout.flex.horizontal
|
||||
spacing = 2
|
||||
},
|
||||
button = {
|
||||
bgimage_focus = theme.bgimage_inset,
|
||||
bgimage_normal = theme.bgimage_outset,
|
||||
margins = 2
|
||||
},
|
||||
container = {
|
||||
margins = 3
|
||||
}
|
||||
},
|
||||
subpanel = {
|
||||
container = {
|
||||
bgimage = theme.bgimage_inset,
|
||||
bg = theme.bgimage_normal,
|
||||
margins = 2
|
||||
}
|
||||
},
|
||||
dismal = {
|
||||
container = {
|
||||
bg = theme.bg_focus
|
||||
},
|
||||
button = {
|
||||
height = 34
|
||||
}
|
||||
},
|
||||
tasklist = {
|
||||
button = {
|
||||
width = 160,
|
||||
|
@ -539,6 +481,23 @@ theme.widgets = {
|
|||
bgimage_minimize = theme.bgimage_outset
|
||||
}
|
||||
},
|
||||
menu = {
|
||||
base = {
|
||||
-- Enables the ability to just drag the mouse on an entry to open it
|
||||
menu_slide = true,
|
||||
spacing = 2
|
||||
},
|
||||
button = {
|
||||
forced_height = 20,
|
||||
forced_width = 160
|
||||
},
|
||||
},
|
||||
systray = {
|
||||
container = {
|
||||
bgimage = function() end,
|
||||
margins = 0
|
||||
}
|
||||
},
|
||||
lockscreen = {
|
||||
popup = {
|
||||
margins = 0
|
||||
|
@ -563,6 +522,24 @@ theme.widgets = {
|
|||
font = "Terminus 20"
|
||||
}
|
||||
},
|
||||
client_buttons = {
|
||||
button = {
|
||||
forced_width = 20,
|
||||
forced_height = 20
|
||||
},
|
||||
base = {
|
||||
spacing = 2
|
||||
}
|
||||
},
|
||||
root_buttons = {
|
||||
button = {
|
||||
forced_width = 20,
|
||||
forced_height = 20
|
||||
},
|
||||
base = {
|
||||
spacing = 2
|
||||
}
|
||||
},
|
||||
titlebar = {
|
||||
titlebar_top = {
|
||||
bgimage_normal = theme.titlebar_bgimage_top,
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
|
@ -1,12 +0,0 @@
|
|||
Notice: all assets in the layouts directory and taglist directory, as well as submenu.png, belong to the AwesomeWM team and respective authors of these assets.
|
||||
|
||||
The following assets in the titlebar directory are made specifically for reno98 and are licensed under CC0:
|
||||
close_focus.png
|
||||
close_normal.png
|
||||
maximized_focus_inactive.png
|
||||
maximized_normal_inactive.png
|
||||
maximized_normal_active.png
|
||||
minimize_focus.png
|
||||
minimize_normal.png
|
||||
|
||||
Other assets in the directory are kept from the default theme of AwesomeWM and belong to the respecitve authors.
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"list": [
|
||||
{"widget": "widgets.clientmenu.volume","vertical":false},
|
||||
{"widget": "widgets.clientmenu.controls"},
|
||||
{"widget": "widgets.clientmenu.buttons"}
|
||||
],
|
||||
"vertical":true
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"widgets.dismal":{
|
||||
"pos":"top_left",
|
||||
"ignore_wibars":false
|
||||
},
|
||||
"widgets.rootmenu":{},
|
||||
"widgets.lockscreen":{},
|
||||
"widgets.base.keypopup":{}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"widgets.lock.clock":{
|
||||
"format": "%a %b %d\n %H: %M"
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"list": [
|
||||
{"widget": "widgets.base.popuptitle",
|
||||
"options":{
|
||||
"icon":"icons/reno98.png",
|
||||
"title":"Serenity"
|
||||
},
|
||||
"vertical":true
|
||||
},
|
||||
{
|
||||
"list": [
|
||||
{"widget": "widgets.base.tagswitcher",
|
||||
"screen":true
|
||||
},
|
||||
{"multimenu":[
|
||||
{"widget": "widgets.rootmenu.controls"},
|
||||
{"widget": "widgets.xdgmenu",
|
||||
"options": {
|
||||
"exclude_category": [
|
||||
"Other"
|
||||
]
|
||||
}
|
||||
}
|
||||
]},
|
||||
{"widget": "widgets.rootmenu.buttons"}
|
||||
],
|
||||
"vertical": true
|
||||
}
|
||||
],
|
||||
"vertical": false
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"align": {
|
||||
"left": [
|
||||
{
|
||||
"widget":"widgets.clientmenu",
|
||||
"client":true
|
||||
}
|
||||
],
|
||||
"center": [
|
||||
{
|
||||
"list":[
|
||||
{
|
||||
"draggable": true
|
||||
},
|
||||
{
|
||||
"builtin": "titlewidget",
|
||||
"client" : true,
|
||||
"options": {
|
||||
"align": "left"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget":"widgets.base.separator",
|
||||
"vertical":true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"builtin": "minimizebutton",
|
||||
"client": true
|
||||
},
|
||||
{
|
||||
"builtin": "maximizedbutton",
|
||||
"client": true
|
||||
},
|
||||
{
|
||||
"builtin": "closebutton",
|
||||
"client": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"align": {
|
||||
"left": [
|
||||
{
|
||||
"widget": "widgets.desktop.tasklist",
|
||||
"screen": true
|
||||
}
|
||||
],
|
||||
"center": [
|
||||
|
||||
],
|
||||
"right": [
|
||||
{
|
||||
"widget": "widgets.desktop.launcher"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"align": {
|
||||
"left": [
|
||||
{ "widget": "widgets.desktop.soundclown" }
|
||||
],
|
||||
"center": [
|
||||
|
||||
],
|
||||
"right": [
|
||||
{ "widget":"widgets.desktop.volume" },
|
||||
{ "widget": "widgets.desktop.notifications",
|
||||
"screen": true
|
||||
},
|
||||
{ "widget": "widgets.desktop.wallpapers",
|
||||
"screen": true,
|
||||
"options": {
|
||||
"path": "$HOME/Pictures/Wallpapers"
|
||||
}
|
||||
},
|
||||
{ "widget": "widgets.base.layout" },
|
||||
{ "widget": "widgets.desktop.battery" },
|
||||
{ "widget": "widgets.base.systray" },
|
||||
{ "widget": "widgets.base.clock" }
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
Licensed under conditions of CC0 (https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt)
|
||||
|
||||
To the extent possible under law, Yessiest (yessiest@memeware.net) has waived all copyright and related or neighboring rights to Reno icons
|
Before Width: | Height: | Size: 550 B |
Before Width: | Height: | Size: 510 B |
Before Width: | Height: | Size: 787 B |
Before Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 515 B |
Before Width: | Height: | Size: 392 B |
Before Width: | Height: | Size: 480 B |
Before Width: | Height: | Size: 366 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 510 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 541 B |
Before Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 538 B |
Before Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 446 B |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 455 B |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 718 B |
Before Width: | Height: | Size: 768 B |
Before Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 682 B |
Before Width: | Height: | Size: 708 B |
Before Width: | Height: | Size: 751 B |
Before Width: | Height: | Size: 642 B |
Before Width: | Height: | Size: 682 B |
Before Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 263 B |
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 264 B |
Before Width: | Height: | Size: 263 B |