mirror of
https://github.com/ItsDrike/nixdots
synced 2024-12-25 17:54:35 +00:00
Add eww bar
This configuration was simply copied from my old Arch Linux system. There are some issues that still need to be solved, namely with fonts and missing bitcoin price script, but it's mostly minor.
This commit is contained in:
parent
a2632b4dea
commit
ac23da55c5
5
home/programs/graphical/bars/default.nix
Normal file
5
home/programs/graphical/bars/default.nix
Normal file
|
@ -0,0 +1,5 @@
|
|||
_: {
|
||||
imports = [
|
||||
./eww
|
||||
];
|
||||
}
|
43
home/programs/graphical/bars/eww/config/css/_colors.scss
Normal file
43
home/programs/graphical/bars/eww/config/css/_colors.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
$rosewater: #f5e0dc;
|
||||
$flamingo: #f2cdcd;
|
||||
$pink: #f5c2e7;
|
||||
$mauve: #cba6f7;
|
||||
$red: #f38ba8;
|
||||
$maroon: #eba0ac;
|
||||
$peach: #fab387;
|
||||
$yellow: #f9e2af;
|
||||
$gold: #efcb10;
|
||||
$green: #a6e3a1;
|
||||
$lime: #78db32;
|
||||
$teal: #94e2d5;
|
||||
$sky: #89dceb;
|
||||
$sapphire: #74c7ec;
|
||||
$blue: #89b4fa;
|
||||
$lavender: #b4befe;
|
||||
$orange: #ffa500;
|
||||
|
||||
$text: #cdd6f4;
|
||||
$subtext1: #bac2de;
|
||||
$subtext0: #a6adc8;
|
||||
$overlay2: #9399b2;
|
||||
$overlay1: #7f849c;
|
||||
$overlay0: #6c7086;
|
||||
|
||||
$surface2: #585b70;
|
||||
$surface1: #45475a;
|
||||
$surface0: #313244;
|
||||
|
||||
$base-a: rgba(30, 30, 40, 0.65);
|
||||
$base: rgba(30, 30, 40, 1);
|
||||
$base1-a: rgba(49, 50, 68, 0.85);
|
||||
$base1: rgba(49, 50, 68, 1);
|
||||
$mantle: #181825;
|
||||
$crust: #11111b;
|
||||
|
||||
$fg: $text;
|
||||
$bg-a: $base-a;
|
||||
$bg: $base;
|
||||
$bg1: $base1;
|
||||
$bg1-a: $base1-a;
|
||||
$border: #28283d;
|
||||
$shadow: $crust;
|
|
@ -0,0 +1,55 @@
|
|||
@keyframes blink {
|
||||
0%{
|
||||
opacity: 0;
|
||||
}
|
||||
50%{
|
||||
opacity: 0.7;
|
||||
}
|
||||
100%{
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// .unplugged.low {
|
||||
// color: #0000ff;
|
||||
// }
|
||||
|
||||
.battery {
|
||||
|
||||
.icon, .icon label {
|
||||
font-family: "Material Symbols Outlined";
|
||||
|
||||
.extra, .extra label {
|
||||
font-family: "Font Awesome 6 Free";
|
||||
}
|
||||
}
|
||||
|
||||
.critical {
|
||||
.unplugged {
|
||||
color: #f00;
|
||||
.extra { animation: blink 1.2s linear infinite; }
|
||||
}
|
||||
|
||||
.plugged {
|
||||
.icon { color: $orange }
|
||||
.extra { color: $green; }
|
||||
}
|
||||
}
|
||||
|
||||
.normal {
|
||||
.unplugged {
|
||||
.icon { color: $orange }
|
||||
}
|
||||
.plugged {
|
||||
.icon { color: $green; }
|
||||
}
|
||||
}
|
||||
|
||||
.full {
|
||||
.icon { color: $green; }
|
||||
.extra { color: $lime; }
|
||||
}
|
||||
|
||||
.extra { margin-right: 5px; }
|
||||
.icon { margin-right: 5px; }
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.bitcoin {
|
||||
.icon { margin-right: 6px; color: $gold; }
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.clock {
|
||||
// color: $sapphire;
|
||||
.icon { margin-right: 6px; color: $sapphire; }
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.cpu {
|
||||
.icon { color: $lime; }
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.gammarelay {
|
||||
.icon {
|
||||
color: $peach;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.kernel {
|
||||
.icon { color: $lavender; }
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.memory {
|
||||
.icon { color: $maroon; }
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.uptime {
|
||||
.icon { color: $green; }
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
.volume {
|
||||
.icon { color: $peach; }
|
||||
.speaker {
|
||||
.icon {
|
||||
margin-left: 8px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.window_name {
|
||||
font-family: "JetBrains Mono", "Font Awesome 6 Free", sans-serif;
|
||||
font-size: 1.4rem;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
.workspaces {
|
||||
background-color: $bg1-a;
|
||||
border-radius: 25px;
|
||||
|
||||
.icon, .icon label {
|
||||
font-family: "Material Symbols Outlined";
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.value {
|
||||
margin: 0 9px;
|
||||
}
|
||||
|
||||
.focused {
|
||||
// text-decoration: underline;
|
||||
// text-decoration-color: red;
|
||||
// text-decoration-style: double;
|
||||
color: $fg;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.inactive {
|
||||
color: #555;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
.calendar-win {
|
||||
@include window;
|
||||
background-color: $bg;
|
||||
border: 1px solid $border;
|
||||
color: $fg;
|
||||
padding: .2em;
|
||||
}
|
||||
|
||||
calendar {
|
||||
padding: 5px;
|
||||
|
||||
:selected {
|
||||
color: $mauve;
|
||||
}
|
||||
|
||||
.header {
|
||||
color: $subtext1;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: $maroon;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.button {
|
||||
color: $sapphire;
|
||||
}
|
||||
|
||||
:indeterminate {
|
||||
color: $overlay0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
.radio-menu-box {
|
||||
@include window;
|
||||
background-color: $bg;
|
||||
border: 1px solid $border;
|
||||
color: $text;
|
||||
font-family: "Jost *", "JetBrains Mono", "Font Awesome 6 Free", sans-serif;
|
||||
|
||||
.icon, .icon label {
|
||||
font-family: "Material Symbols Outlined";
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.text-row {
|
||||
margin: 1rem 1.5rem 0;
|
||||
|
||||
.title { font-size: 1.2rem; }
|
||||
}
|
||||
|
||||
.element-row {
|
||||
margin: .5rem .7rem;
|
||||
|
||||
label {
|
||||
font-size: 1rem;
|
||||
margin: 0 .1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.element {
|
||||
@include rounding;
|
||||
background-color: $surface0;
|
||||
margin: .3rem;
|
||||
|
||||
button {
|
||||
@include rounding;
|
||||
padding: .5rem;
|
||||
|
||||
label {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $overlay0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
home/programs/graphical/bars/eww/config/eww.scss
Normal file
114
home/programs/graphical/bars/eww/config/eww.scss
Normal file
|
@ -0,0 +1,114 @@
|
|||
@import "css/colors";
|
||||
|
||||
@mixin rounding {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
@mixin window {
|
||||
border: 1px solid $border;
|
||||
box-shadow: 0 2px 3px $shadow;
|
||||
margin: 5px 5px 10px;
|
||||
@include rounding;
|
||||
}
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
transition: 200ms ease;
|
||||
}
|
||||
|
||||
@import "css/windows/calendar";
|
||||
@import "css/windows/radio_menu";
|
||||
@import "css/modules/clock";
|
||||
@import "css/modules/volume";
|
||||
@import "css/modules/bitcoin";
|
||||
@import "css/modules/cpu";
|
||||
@import "css/modules/memory";
|
||||
@import "css/modules/uptime";
|
||||
@import "css/modules/kernel";
|
||||
@import "css/modules/battery";
|
||||
@import "css/modules/workspaces";
|
||||
@import "css/modules/gammarelay";
|
||||
@import "css/modules/window_name";
|
||||
|
||||
.bar {
|
||||
background-color: $bg-a;
|
||||
color: $fg;
|
||||
|
||||
font-family: "JetBrains Mono", "Jost *", sans-serif;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
// TODO: Use percentages (for some reason it fails now)
|
||||
min-width: 1900px;
|
||||
}
|
||||
|
||||
tooltip {
|
||||
background: $bg;
|
||||
border: 1px solid $border;
|
||||
border-radius: 8px;
|
||||
|
||||
label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.icon,
|
||||
.icon label {
|
||||
font-family: "Font Awesome 6 Free", "Material Symbols Outlined";
|
||||
}
|
||||
|
||||
.module {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.separ {
|
||||
color: $surface0;
|
||||
font-size: 1.5rem;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
scale trough {
|
||||
background-color: $bg1-a;
|
||||
border-radius: 24px;
|
||||
margin: 0 1rem;
|
||||
min-height: 10px;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.tray {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
menu {
|
||||
background: $bg1;
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
border: 2px solid rgba($crust, 0.5);
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
menu menu {
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
|
||||
menu > menuitem {
|
||||
padding: 0.4em 1rem;
|
||||
background: transparent;
|
||||
transition: 0.2s ease background;
|
||||
}
|
||||
|
||||
menu > menuitem:hover {
|
||||
background: rgba($overlay1, 0.4);
|
||||
}
|
||||
|
||||
menu > menuitem check:checked ~ label {
|
||||
color: $bg1-a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
menubar > menuitem {
|
||||
margin-left: 0.6rem;
|
||||
}
|
94
home/programs/graphical/bars/eww/config/eww.yuck
Normal file
94
home/programs/graphical/bars/eww/config/eww.yuck
Normal file
|
@ -0,0 +1,94 @@
|
|||
(defvar terminal "kitty -e")
|
||||
|
||||
(include "./modules/variables.yuck")
|
||||
|
||||
(include "./modules/clock.yuck")
|
||||
(include "./modules/volume.yuck")
|
||||
(include "./modules/bitcoin.yuck")
|
||||
(include "./modules/cpu.yuck")
|
||||
(include "./modules/memory.yuck")
|
||||
(include "./modules/uptime.yuck")
|
||||
; (include "./modules/kernel.yuck")
|
||||
(include "./modules/battery.yuck")
|
||||
(include "./modules/window_name.yuck")
|
||||
(include "./modules/workspaces.yuck")
|
||||
(include "./modules/gammarelay.yuck")
|
||||
|
||||
(include "./windows/calendar.yuck")
|
||||
(include "./windows/radio-menu.yuck")
|
||||
|
||||
(defwidget sep []
|
||||
(label :class "separ module" :text "|"))
|
||||
|
||||
(defwidget left []
|
||||
(box
|
||||
:space-evenly false
|
||||
:halign "start"
|
||||
(gammarelay_module)
|
||||
(sep)
|
||||
(window_name_module)
|
||||
))
|
||||
|
||||
(defwidget right []
|
||||
(box
|
||||
:space-evenly false
|
||||
:halign "end"
|
||||
; (kernel_module)
|
||||
; (sep)
|
||||
(volume_module)
|
||||
(sep)
|
||||
(battery_module)
|
||||
(sep)
|
||||
(bitcoin_module)
|
||||
(sep)
|
||||
(cpu_module)
|
||||
(sep)
|
||||
(memory_module)
|
||||
(sep)
|
||||
; (uptime_module)
|
||||
; (sep)
|
||||
(clock_module)
|
||||
(sep)
|
||||
(systray
|
||||
:pack-direction "left"
|
||||
:class "module tray"
|
||||
)
|
||||
))
|
||||
|
||||
(defwidget center []
|
||||
(box
|
||||
:space-evenly false
|
||||
:halign "center"
|
||||
(workspaces_module)
|
||||
))
|
||||
|
||||
(defwidget bar []
|
||||
(centerbox
|
||||
:class "bar"
|
||||
:orientation "horizontal"
|
||||
(left)
|
||||
(center)
|
||||
(right)))
|
||||
|
||||
(defwindow bar0
|
||||
:monitor 0
|
||||
:geometry (geometry :x "0%"
|
||||
:y "0%"
|
||||
:width: "100%"
|
||||
:height "32px"
|
||||
:anchor "top center")
|
||||
:stacking "fg"
|
||||
:exclusive true
|
||||
(bar))
|
||||
|
||||
|
||||
(defwindow bar1
|
||||
:monitor 1
|
||||
:geometry (geometry :x "0%"
|
||||
:y "0%"
|
||||
:width: "100%"
|
||||
:height "32px"
|
||||
:anchor "top center")
|
||||
:stacking "fg"
|
||||
:exclusive true
|
||||
(bar))
|
24
home/programs/graphical/bars/eww/config/modules/battery.yuck
Normal file
24
home/programs/graphical/bars/eww/config/modules/battery.yuck
Normal file
|
@ -0,0 +1,24 @@
|
|||
(defwidget battery_module []
|
||||
(eventbox
|
||||
:class "module battery"
|
||||
|
||||
(box
|
||||
:class {battery.critical ? "critical" : battery.full ? "full" : "normal"}
|
||||
(box
|
||||
:space-evenly false
|
||||
:class {battery.plugged ? "plugged" : "unplugged" }
|
||||
|
||||
(box
|
||||
:class "icon"
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "extra"
|
||||
:text {battery.extra_icon})
|
||||
(label
|
||||
:text {battery.capacity_icon}))
|
||||
(label
|
||||
:class "value"
|
||||
:text "${battery.percent}%"
|
||||
)
|
||||
))))
|
14
home/programs/graphical/bars/eww/config/modules/bitcoin.yuck
Normal file
14
home/programs/graphical/bars/eww/config/modules/bitcoin.yuck
Normal file
|
@ -0,0 +1,14 @@
|
|||
(defwidget bitcoin_module []
|
||||
(eventbox
|
||||
:onclick "~/.local/bin/scripts/cli/bitcoin | xargs -I_ ${EWW_CMD} update bitcoin=_"
|
||||
:class "module bitcoin"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text "")
|
||||
(label :text {bitcoin}))
|
||||
)
|
||||
)
|
18
home/programs/graphical/bars/eww/config/modules/clock.yuck
Normal file
18
home/programs/graphical/bars/eww/config/modules/clock.yuck
Normal file
|
@ -0,0 +1,18 @@
|
|||
(defwidget clock_module []
|
||||
|
||||
(eventbox
|
||||
:tooltip {time.day}
|
||||
:class "module clock"
|
||||
;; :onclick "${EWW_CMD} open --toggle calendar"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text "")
|
||||
(label
|
||||
:class "value"
|
||||
:text "${time.date} ${time.hour}:${time.minute}")
|
||||
)))
|
||||
|
15
home/programs/graphical/bars/eww/config/modules/cpu.yuck
Normal file
15
home/programs/graphical/bars/eww/config/modules/cpu.yuck
Normal file
|
@ -0,0 +1,15 @@
|
|||
(defwidget cpu_module []
|
||||
(eventbox
|
||||
:class "module cpu"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text " ")
|
||||
(label
|
||||
:class "value"
|
||||
:text "${round(EWW_CPU.avg,2)}%"
|
||||
)
|
||||
)))
|
|
@ -0,0 +1,41 @@
|
|||
(defwidget gammarelay_module []
|
||||
(box
|
||||
:class "module gammarelay"
|
||||
|
||||
(eventbox
|
||||
:onscroll "scripts/gammarelay temperature scroll {}"
|
||||
:onclick "scripts/gammarelay temperature set toggle"
|
||||
:onrightclick "scripts/gammarelay temperature set off"
|
||||
:tooltip "${temperature} K"
|
||||
:class "temperature"
|
||||
(box
|
||||
(label
|
||||
:class "icon"
|
||||
:text "")
|
||||
))
|
||||
|
||||
(eventbox
|
||||
:onscroll "scripts/gammarelay brightness scroll {}"
|
||||
:onclick "scripts/gammarelay brightness set toggle"
|
||||
:onrightclick "scripts/gammarelay brightness set off"
|
||||
:tooltip "${brightness}%"
|
||||
:class "brightness"
|
||||
(box
|
||||
(label
|
||||
:class "icon"
|
||||
:text "")
|
||||
))
|
||||
|
||||
; (eventbox
|
||||
; :onscroll "scripts/gammarelay gamma scroll {}"
|
||||
; :onclick "scripts/gammarelay gamma set toggle"
|
||||
; :onrightclick "scripts/gammarelay gamma set off"
|
||||
; :tooltip "${gamma}%"
|
||||
; :class "gamma"
|
||||
; :valign "top"
|
||||
; (box
|
||||
; (label
|
||||
; :class "icon"
|
||||
; :text "γ")
|
||||
; ))
|
||||
))
|
15
home/programs/graphical/bars/eww/config/modules/kernel.yuck
Normal file
15
home/programs/graphical/bars/eww/config/modules/kernel.yuck
Normal file
|
@ -0,0 +1,15 @@
|
|||
(defwidget kernel_module []
|
||||
(eventbox
|
||||
:class "module kernel"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text " ")
|
||||
(label
|
||||
:class "value"
|
||||
:text {kernel}
|
||||
)
|
||||
)))
|
15
home/programs/graphical/bars/eww/config/modules/memory.yuck
Normal file
15
home/programs/graphical/bars/eww/config/modules/memory.yuck
Normal file
|
@ -0,0 +1,15 @@
|
|||
(defwidget memory_module []
|
||||
(eventbox
|
||||
:class "module memory"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text " ")
|
||||
(label
|
||||
:class "value"
|
||||
:text "${round(EWW_RAM.used_mem / 1000000000,1)}G: ${round(EWW_RAM.used_mem_perc,0)}%"
|
||||
)
|
||||
)))
|
|
@ -0,0 +1,3 @@
|
|||
(deflisten nightlight
|
||||
:initial `{"running": false,"temperature": 0}`
|
||||
`scripts/nightlight --state`)
|
15
home/programs/graphical/bars/eww/config/modules/uptime.yuck
Normal file
15
home/programs/graphical/bars/eww/config/modules/uptime.yuck
Normal file
|
@ -0,0 +1,15 @@
|
|||
(defwidget uptime_module []
|
||||
(eventbox
|
||||
:class "module uptime"
|
||||
|
||||
(box
|
||||
:space-evenly false
|
||||
|
||||
(label
|
||||
:class "icon"
|
||||
:text " ")
|
||||
(label
|
||||
:class "value"
|
||||
:text {uptime}
|
||||
)
|
||||
)))
|
|
@ -0,0 +1,60 @@
|
|||
(defpoll time
|
||||
:interval "5s"
|
||||
:initial '{"date": "01 Jan", "hour": "00", "minute": "00", "day": "Monday"}'
|
||||
`date +'{"date": "%d %b", "hour": "%H", "minute": "%M", "day": "%A"}'`)
|
||||
|
||||
(deflisten volume
|
||||
:initial '{ "speaker_vol": "100", "speaker_mute": false, "speaker_icon": "", "microphone_mute": false, "microphone_vol": "100", "microphone_icon": "" }'
|
||||
`scripts/volume loop`)
|
||||
|
||||
(deflisten window_name
|
||||
:initial `{"class":"","name":"","formatted_name":""}`
|
||||
`scripts/window_name`)
|
||||
|
||||
(deflisten workspaces
|
||||
:initial `[{"id": 1,"name": "N/A","monitor": "N/A","windows": 1,"hasfullscreen": false,"lastwindow": "N/A","lastwindowtitle": "N/A","format_name": "N/A","active": true}]`
|
||||
`scripts/workspaces --loop`)
|
||||
|
||||
(defpoll battery
|
||||
:interval "1s"
|
||||
:initial '{"percent":"0","plugged":"false","status":"N/A","capacity_icon":"","extra_icon":"","manufacturer":"N/A","model_name":"N/A","technology":"N/A","energy_now":"0","enerfy_full":"0","enerfy_full_design":"0","cycle_count":"0","critical":"false","full":"false"}'
|
||||
`scripts/battery`)
|
||||
|
||||
(defpoll uptime
|
||||
:interval "1m"
|
||||
:initial 'N/A'
|
||||
`uptime -p | sed \\
|
||||
-e 's/^up //' \\
|
||||
-e 's/ years\\?,\\?/y/' \\
|
||||
-e 's/ months\\?,\\?/m/' \\
|
||||
-e 's/ weeks\\?,\\?/w/' \\
|
||||
-e 's/ days\\?,\\?/d/' \\
|
||||
-e 's/ hours\\?,\\?/h/' \\
|
||||
-e 's/ minutes\\?,\\?/m/' \\
|
||||
-e 's/ seconds\\?,\\?/s/' \\
|
||||
| cut -d' ' -f-2`)
|
||||
|
||||
(defpoll bitcoin
|
||||
:interval "5m"
|
||||
:initial "$N/A"
|
||||
`~/.local/bin/scripts/cli/bitcoin`)
|
||||
|
||||
; TODO: Figure out how to store this one-time
|
||||
(defpoll kernel
|
||||
:interval "10000h"
|
||||
:initial 'N/A'
|
||||
; `uname -r | sed -r 's/(.+)-arch(.+)/\\1/'`
|
||||
`uname -r`)
|
||||
|
||||
(deflisten temperature `scripts/gammarelay temperature watch`)
|
||||
(deflisten brightness `scripts/gammarelay brightness watch`)
|
||||
(deflisten gamma `scripts/gammarelay gamma watch`)
|
||||
|
||||
(defpoll net
|
||||
:interval "3s"
|
||||
:initial '{"essid":"N/A","icon":"","state":"unknown","signal":"0"}'
|
||||
`scripts/net status`)
|
||||
(defpoll bluetooth
|
||||
:interval "3s"
|
||||
:initial '{"icon":"","status":"unknown","name":"","mac":"","battery":""}'
|
||||
`scripts/bluetooth status`)
|
40
home/programs/graphical/bars/eww/config/modules/volume.yuck
Normal file
40
home/programs/graphical/bars/eww/config/modules/volume.yuck
Normal file
|
@ -0,0 +1,40 @@
|
|||
(defvar mic_rev false)
|
||||
|
||||
(defwidget volume_module []
|
||||
(box
|
||||
:class "module volume"
|
||||
:space-evenly false
|
||||
|
||||
(eventbox
|
||||
:onscroll "scripts/volume setvol SOURCE 0.5 {}"
|
||||
:onclick "scripts/volume togglemute SOURCE"
|
||||
:onrightclick "${terminal} pulsemixer &"
|
||||
:onhover "${EWW_CMD} update mic_rev=true"
|
||||
:onhoverlost "${EWW_CMD} update mic_rev=false"
|
||||
:class "microphone"
|
||||
(box
|
||||
(label
|
||||
:class "icon"
|
||||
:text {volume.microphone_icon})
|
||||
(label
|
||||
:visible {mic_rev && !volume.microphone_mute}
|
||||
:class "value"
|
||||
:text "${volume.microphone_vol}%")
|
||||
))
|
||||
|
||||
(eventbox
|
||||
:onscroll "scripts/volume setvol SINK 0.5 {}"
|
||||
:onclick "scripts/volume togglemute SINK"
|
||||
:onrightclick "${terminal} pulsemixer &"
|
||||
:class "speaker"
|
||||
(box
|
||||
(label
|
||||
:class "icon"
|
||||
:text {volume.speaker_icon})
|
||||
(label
|
||||
:visible {!volume.speaker_mute}
|
||||
:class "value"
|
||||
:text "${volume.speaker_vol}%")
|
||||
))
|
||||
|
||||
))
|
|
@ -0,0 +1,11 @@
|
|||
; Consider making the window name clickable, opening up a full window that's showing
|
||||
; the selected window details (class, unformatted name, and perhaps even more, like
|
||||
; xwayland status, ...)
|
||||
(defwidget window_name_module []
|
||||
(box
|
||||
:class "module window_name"
|
||||
|
||||
(label
|
||||
:class "value"
|
||||
:text "${window_name.formatted_name}")
|
||||
))
|
|
@ -0,0 +1,21 @@
|
|||
; (defwidget sep []
|
||||
; (label :class "separ module" :text "|"))
|
||||
|
||||
|
||||
; Consider making the window name clickable, opening up a full window that's showing
|
||||
; the selected window details (class, unformatted name, and perhaps even more, like
|
||||
; xwayland status, ...)
|
||||
(defwidget workspaces_module []
|
||||
(box
|
||||
:class "module workspaces"
|
||||
|
||||
(for workspace in workspaces
|
||||
(eventbox
|
||||
:class {workspace.active ? 'focused' : workspace.windows > 0 ? 'active' : 'inactive'}
|
||||
:onclick `scripts/workspaces --switch ${workspace.id}`
|
||||
|
||||
(label
|
||||
:class "value icon"
|
||||
:text {workspace.format_name}))
|
||||
)
|
||||
))
|
11
home/programs/graphical/bars/eww/config/scripts/.flake8
Normal file
11
home/programs/graphical/bars/eww/config/scripts/.flake8
Normal file
|
@ -0,0 +1,11 @@
|
|||
[flake8]
|
||||
max-line-length=119
|
||||
extend-ignore=E203
|
||||
extend-select=B902,B904
|
||||
exclude=.venv,.git,.cache
|
||||
ignore=
|
||||
ANN002, # *args annotation
|
||||
ANN003, # **kwargs annotation
|
||||
ANN101, # self param annotation
|
||||
ANN102, # cls param annotation
|
||||
ANN204, # return type annotation for special methods
|
85
home/programs/graphical/bars/eww/config/scripts/battery
Executable file
85
home/programs/graphical/bars/eww/config/scripts/battery
Executable file
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
# $BATTERY and $ADAPTER env vars can be set manually, being the names of the
|
||||
# devices (in /sys/class/power_supply/) i.e. BATTERY=BAT0 ADAPTER=ADP0
|
||||
# or, if left unset, they will be automatically picked.
|
||||
|
||||
CAPACITY_ICONS=("" "" "" "" "" "" "" "" "" "")
|
||||
CHARGING_ICON=""
|
||||
DISCHARGING_ICON=""
|
||||
FULL_ICON="" # Plugged in, but no longer charging (fully charged)
|
||||
CRITICAL_ICON=""
|
||||
CRITICAL_PERCENTAGE=15
|
||||
|
||||
if [ -z "$BATTERY" ]; then
|
||||
# shellcheck disable=SC2010
|
||||
BATTERY="$(\ls -t /sys/class/power_supply | grep "BAT" | head -n 1)"
|
||||
fi
|
||||
|
||||
if [ -z "$ADAPTER" ]; then
|
||||
# shellcheck disable=SC2010
|
||||
ADAPTER="$(\ls -t /sys/class/power_supply | grep -E "ADP|AC" | head -n 1)"
|
||||
fi
|
||||
|
||||
get_bat_info() {
|
||||
cat /sys/class/power_supply/"$BATTERY"/"$1"
|
||||
}
|
||||
|
||||
get_adp_info() {
|
||||
cat /sys/class/power_supply/"$ADAPTER"/"$1"
|
||||
}
|
||||
|
||||
manufacturer="$(get_bat_info manufacturer)"
|
||||
model_name="$(get_bat_info model_name)"
|
||||
technology="$(get_bat_info technology)"
|
||||
energy_now="$(get_bat_info energy_now)"
|
||||
energy_full="$(get_bat_info energy_full)"
|
||||
energy_full_design="$(get_bat_info energy_full_design)"
|
||||
cycle_count="$(get_bat_info cycle_count)"
|
||||
|
||||
capacity="$(get_bat_info capacity)"
|
||||
status="$(get_bat_info status)"
|
||||
[ "$(get_adp_info online)" -eq 1 ] && adp_connected="true" || adp_connected="false"
|
||||
|
||||
# Quick overrides to showcase how battery works
|
||||
# capacity=100
|
||||
# adp_connected="true"
|
||||
# status="Charging"
|
||||
# status="Not charging"
|
||||
# status="Discharging"
|
||||
|
||||
full="false"
|
||||
capacity_icon="$(pick_icon "$capacity" 0 100 "${CAPACITY_ICONS[@]}")"
|
||||
|
||||
if [ "$status" = "Not charging" ] || [ "$status" = "Full" ] && [ "$adp_connected" = "true" ]; then
|
||||
extra_icon="$FULL_ICON"
|
||||
full="true"
|
||||
elif [ "$status" = "Discharging" ] && [ "$capacity" -le "$CRITICAL_PERCENTAGE" ]; then
|
||||
extra_icon="$CRITICAL_ICON"
|
||||
elif [ "$status" = "Discharging" ]; then
|
||||
extra_icon="$DISCHARGING_ICON"
|
||||
elif [ "$status" = "Charging" ]; then
|
||||
extra_icon="$CHARGING_ICON"
|
||||
fi
|
||||
|
||||
[ "$capacity" -le "$CRITICAL_PERCENTAGE" ] && critical="true" || critical="false"
|
||||
|
||||
jq -n -c --monochrome-output \
|
||||
--arg percent "$capacity" \
|
||||
--arg plugged "$adp_connected" \
|
||||
--arg status "$status" \
|
||||
--arg capacity_icon "$capacity_icon" \
|
||||
--arg extra_icon "$extra_icon" \
|
||||
--arg manufacturer "$manufacturer" \
|
||||
--arg model_name "$model_name" \
|
||||
--arg technology "$technology" \
|
||||
--arg energy_now "$energy_now" \
|
||||
--arg energy_full "$energy_full" \
|
||||
--arg energy_full_design "$energy_full_design" \
|
||||
--arg cycle_count "$cycle_count" \
|
||||
--arg critical "$critical" \
|
||||
--arg full "$full" \
|
||||
'$ARGS.named'
|
85
home/programs/graphical/bars/eww/config/scripts/bluetooth
Executable file
85
home/programs/graphical/bars/eww/config/scripts/bluetooth
Executable file
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
ICON_IDLE=""
|
||||
ICON_CONNECTED=""
|
||||
ICON_OFF=""
|
||||
|
||||
toggle() {
|
||||
status=$(rfkill -J | jq -r '.rfkilldevices[] | select(.type == "bluetooth") | .soft' | head -1)
|
||||
|
||||
if [ "$status" = "unblocked" ]; then
|
||||
rfkill block bluetooth
|
||||
else
|
||||
rfkill unblock bluetooth
|
||||
if ! systemctl -q is-active bluetooth.service; then
|
||||
# This will use polkit for privillege elevation
|
||||
systemctl start bluetooth
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
get_report() {
|
||||
status=$(rfkill -J | jq -r '.rfkilldevices[] | select(.type == "bluetooth") | .soft' | head -1)
|
||||
if [ "$status" = "blocked" ] || ! systemctl -q is-active bluetooth.service || ! command -v bluetoothctl >/dev/null 2>&1; then
|
||||
jq -n -c --monochrome-output \
|
||||
--arg icon "$ICON_OFF" \
|
||||
--arg status "unknown" \
|
||||
--arg name "" \
|
||||
--arg mac "" \
|
||||
--arg battery "" \
|
||||
'$ARGS.named'
|
||||
|
||||
return
|
||||
fi
|
||||
|
||||
status="$(bluetoothctl show)"
|
||||
|
||||
powered="$(echo "$status" | grep Powered | cut -d' ' -f 2-)"
|
||||
if [ "$powered" != "yes" ]; then
|
||||
jq -n -c --monochrome-output \
|
||||
--arg icon "$ICON_OFF" \
|
||||
--arg status "unpowered" \
|
||||
--arg name "" \
|
||||
--arg mac "" \
|
||||
--arg battery "" \
|
||||
'$ARGS.named'
|
||||
|
||||
return
|
||||
fi
|
||||
|
||||
status="$(bluetoothctl info)"
|
||||
if [ "$status" == "Missing device address argument" ]; then
|
||||
jq -n -c --monochrome-output \
|
||||
--arg icon "$ICON_IDLE" \
|
||||
--arg status "idle" \
|
||||
--arg name "" \
|
||||
--arg mac "" \
|
||||
--arg battery "" \
|
||||
'$ARGS.named'
|
||||
|
||||
return
|
||||
fi
|
||||
|
||||
name="$(echo "$status" | grep Name | cut -d' ' -f 2-)"
|
||||
mac="$(echo "$status" | head -1 | awk '{print $2}' | tr ':' '_')"
|
||||
|
||||
if [ "$(echo "$status" | grep Percentage)" != "" ] && command -v upower >/dev/null 2>&1; then
|
||||
battery="$(upower -i /org/freedesktop/UPower/devices/headset_dev_"$mac" | grep percentage | awk '{print $2}' | cut -f 1 -d '%')%"
|
||||
else
|
||||
battery=""
|
||||
fi
|
||||
|
||||
jq -n -c --monochrome-output \
|
||||
--arg icon "$ICON_CONNECTED" \
|
||||
--arg status "connected" \
|
||||
--arg name "$name" \
|
||||
--arg mac "$mac" \
|
||||
--arg battery "$battery" \
|
||||
'$ARGS.named'
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"status") get_report ;;
|
||||
"toggle") toggle ;;
|
||||
*) >&2 echo "Invalid usage, argument '$1' not recognized."; exit 1 ;;
|
||||
esac
|
82
home/programs/graphical/bars/eww/config/scripts/gammarelay
Executable file
82
home/programs/graphical/bars/eww/config/scripts/gammarelay
Executable file
|
@ -0,0 +1,82 @@
|
|||
#!/bin/env bash
|
||||
|
||||
if [ "$1" = "temperature" ]; then
|
||||
watch_cmd="{t}"
|
||||
update_cmd="UpdateTemperature"
|
||||
update_signature="n"
|
||||
set_cmd="Temperature"
|
||||
set_signature="q"
|
||||
default_val=6500
|
||||
click_val=4500
|
||||
scroll_change=100
|
||||
cmp_op="<"
|
||||
|
||||
elif [ "$1" = "brightness" ]; then
|
||||
watch_cmd="{bp}"
|
||||
update_cmd="UpdateBrightness"
|
||||
update_signature="d"
|
||||
set_cmd="Brightness"
|
||||
set_signature="d"
|
||||
default_val=1
|
||||
click_val=0.8
|
||||
scroll_change=0.02
|
||||
cmp_op="<"
|
||||
|
||||
elif [ "$1" = "gamma" ]; then
|
||||
watch_cmd="{g}"
|
||||
update_cmd="UpdateGamma"
|
||||
update_signature="d"
|
||||
set_cmd="Gamma"
|
||||
set_signature="d"
|
||||
default_val=1
|
||||
click_val=1.1
|
||||
scroll_change=0.02
|
||||
cmp_op=">"
|
||||
|
||||
else
|
||||
>&2 echo "Invalid option, first argument must be one of: temperature, brightness, gamma"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$2" = "watch" ]; then
|
||||
exec wl-gammarelay-rs watch "$watch_cmd"
|
||||
|
||||
elif [ "$2" = "get" ]; then
|
||||
exec busctl --user get-property rs.wl-gammarelay / rs.wl.gammarelay "$set_cmd" | cut -d' ' -f2
|
||||
|
||||
elif [ "$2" = "scroll" ]; then
|
||||
if [ "$3" = "up" ]; then
|
||||
sign="+"
|
||||
elif [ "$3" = "down" ]; then
|
||||
sign="-"
|
||||
else
|
||||
>&2 echo "Invalid sign, second argument must be one of: up, down"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec busctl --user -- call rs.wl-gammarelay / rs.wl.gammarelay "$update_cmd" "$update_signature" ${sign}${scroll_change}
|
||||
|
||||
elif [ "$2" = "set" ]; then
|
||||
mode="$3"
|
||||
if [ "$mode" = "toggle" ]; then
|
||||
cur_val="$(busctl --user get-property rs.wl-gammarelay / rs.wl.gammarelay "$set_cmd" | cut -d' ' -f2)"
|
||||
if [ "$(echo "$cur_val $cmp_op $default_val" | bc -l)" = "1" ]; then
|
||||
mode="off"
|
||||
else
|
||||
mode="on"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$mode" = "on" ]; then
|
||||
exec busctl --user -- set-property rs.wl-gammarelay / rs.wl.gammarelay "$set_cmd" "$set_signature" "$click_val"
|
||||
elif [ "$mode" = "off" ]; then
|
||||
exec busctl --user -- set-property rs.wl-gammarelay / rs.wl.gammarelay "$set_cmd" "$set_signature" "$default_val"
|
||||
else
|
||||
>&2 echo "Invalid mode, third argument, must be one of: toggle, on, off"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
else
|
||||
>&2 echo "Invalid operation, second argument must be one of: watch, scroll, set"
|
||||
exit 1
|
||||
fi
|
50
home/programs/graphical/bars/eww/config/scripts/include
Executable file
50
home/programs/graphical/bars/eww/config/scripts/include
Executable file
|
@ -0,0 +1,50 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# $1: Current number
|
||||
# $2: Range minimum
|
||||
# $3: Range maximum
|
||||
# $4-: Icons as individual arguments
|
||||
pick_icon() {
|
||||
cur="$1"
|
||||
min="$2"
|
||||
max="$3"
|
||||
shift 3
|
||||
icons=("$@")
|
||||
|
||||
index="$(echo "($cur-$min)/(($max-$min)/${#icons[@]})" | bc)"
|
||||
|
||||
# Print the picked icon, handling overflows/underflows, i.e. if our index is <0 or >len(icons)
|
||||
if [ "$index" -ge "${#icons[@]}" ]; then
|
||||
index=-1
|
||||
elif [ "$index" -lt 0 ]; then
|
||||
index=0
|
||||
fi
|
||||
|
||||
echo "${icons[index]}"
|
||||
}
|
||||
|
||||
# Will block and listen to the hyprland socket messages and output them
|
||||
# Generally used like: hyprland_ipc | while read line; do handle $line; done
|
||||
# Read <https://wiki.hyprland.org/IPC/> for output format and available events
|
||||
# Note: requires openbsd version of netcat.
|
||||
# $1 - Optional event to listen for (no event filtering will be done if not provided)
|
||||
hyprland_ipc() {
|
||||
if [ -z "$HYPRLAND_INSTANCE_SIGNATURE" ]; then
|
||||
>&2 echo "Hyprland is not running, IPC not available"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOCKET_PATH="${XDG_RUNTIME_DIR:-/run/user/$UID}/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
|
||||
#SOCKET_PATH="/tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
nc -U "$SOCKET_PATH" | while read -r test; do
|
||||
echo "$test"
|
||||
done
|
||||
else
|
||||
nc -U "$SOCKET_PATH" | while read -r test; do
|
||||
# shellcheck disable=SC2016
|
||||
echo "$test" | grep --line-buffered -E "^$1>>" | stdbuf -oL awk -F '>>' '{print $2}'
|
||||
done
|
||||
fi
|
||||
}
|
52
home/programs/graphical/bars/eww/config/scripts/net
Executable file
52
home/programs/graphical/bars/eww/config/scripts/net
Executable file
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
STRENGTH_ICONS=("" "" "" "" "" "")
|
||||
DISCONNECTED_ICON=""
|
||||
WIFI_OFF=""
|
||||
|
||||
toggle() {
|
||||
status=$(rfkill -J | jq -r '.rfkilldevices[] | select(.type == "wlan") | .soft' | head -1)
|
||||
|
||||
if [ "$status" = "unblocked" ]; then
|
||||
rfkill block wlan
|
||||
else
|
||||
rfkill unblock wlan
|
||||
fi
|
||||
}
|
||||
|
||||
get_report() {
|
||||
connection_details="$(nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep wireless | head -n1)"
|
||||
essid="$(echo $connection_details | cut -d':' -f1)"
|
||||
device="$(echo $connection_details | cut -d':' -f3)"
|
||||
if [ -n "$device" ]; then
|
||||
state="$(nmcli -t -f DEVICE,STATE device status | grep "$device" | head -n1 | cut -d':' -f2)"
|
||||
signal="$(nmcli -t -f in-use,signal dev wifi | grep "\*" | cut -d':' -f2)"
|
||||
else
|
||||
state="unknown"
|
||||
signal="0"
|
||||
fi
|
||||
|
||||
if [ "$state" = "disconnected" ] ; then
|
||||
icon="$DISCONNECTED_ICON"
|
||||
elif [ "$state" = "unknown" ]; then
|
||||
icon="$WIFI_OFF"
|
||||
else
|
||||
icon="$(pick_icon "$signal" 0 100 "${STRENGTH_ICONS[@]}")"
|
||||
fi
|
||||
|
||||
jq -n -c --monochrome-output \
|
||||
--arg essid "$essid" \
|
||||
--arg icon "$icon" \
|
||||
--arg state "$state" \
|
||||
--arg signal "$signal" \
|
||||
'$ARGS.named'
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"toggle") toggle ;;
|
||||
"status") get_report ;;
|
||||
*) >&2 echo "Invalid usage, argument '$1' not recognized."; exit 1 ;;
|
||||
esac
|
12
home/programs/graphical/bars/eww/config/scripts/nightlight
Normal file
12
home/programs/graphical/bars/eww/config/scripts/nightlight
Normal file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
# Consider usning a file as a flag for whether nightlight is on or off
|
||||
# as we might be in transition state and just killing the program might
|
||||
# not be enough.
|
||||
|
||||
if [ "$1" = "toggle" ]; then
|
||||
gammastep -x
|
||||
fi
|
|
@ -0,0 +1,12 @@
|
|||
[tool.black]
|
||||
line-length = 119
|
||||
extend-exclude = "^/.cache"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
line_length = 119
|
||||
atomic = true
|
||||
order_by_type = false
|
||||
case_sensitive = true
|
||||
combine_as_imports = true
|
||||
skip = [".venv", ".git", ".cache"]
|
35
home/programs/graphical/bars/eww/config/scripts/storage
Executable file
35
home/programs/graphical/bars/eww/config/scripts/storage
Executable file
|
@ -0,0 +1,35 @@
|
|||
#!/bin/env bash
|
||||
|
||||
MOUNTPOINTS=("/" "/mnt/ext")
|
||||
|
||||
data="$(df -H)"
|
||||
|
||||
as_json() {
|
||||
mountpoint="$1"
|
||||
res="$2"
|
||||
arr_res=($res)
|
||||
|
||||
jq -n -c --monochrome-output \
|
||||
--arg mountpoint "$mountpoint" \
|
||||
--arg size "${arr_res[0]}" \
|
||||
--arg used "${arr_res[1]}" \
|
||||
--arg avail "${arr_res[2]}" \
|
||||
--arg percent "${arr_res[3]}" \
|
||||
'$ARGS.named'
|
||||
}
|
||||
|
||||
output_json="[]"
|
||||
for mountpoint in "${MOUNTPOINTS[@]}"; do
|
||||
res="$(echo "$data" | awk -v m="$mountpoint" '$6 == m {print $2 " " $3 " " $4 " " $5}')"
|
||||
out="$(as_json "$mountpoint" "$res")"
|
||||
|
||||
# echo "$output_json $out" | jq -c -s
|
||||
|
||||
jq --argjson arr1 "$output_json" --argjson arr2 "[$out]" -n \
|
||||
'$arr1 + $arr2'
|
||||
|
||||
# mount_data+=("$mountpoint" $res)
|
||||
# echo "${mount_data[@]}"
|
||||
done
|
||||
|
||||
# echo "${mount_data[@]}"
|
7
home/programs/graphical/bars/eww/config/scripts/temp
Executable file
7
home/programs/graphical/bars/eww/config/scripts/temp
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
#hyprland_ipc "workspace|createworkspace|destroyworkspace|activewindow"
|
||||
hyprland_ipc
|
112
home/programs/graphical/bars/eww/config/scripts/volume
Executable file
112
home/programs/graphical/bars/eww/config/scripts/volume
Executable file
|
@ -0,0 +1,112 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# Define some icons
|
||||
SPEAKER_ICONS=("" "" "")
|
||||
SPEAKER_MUTED_ICON=""
|
||||
MIC_ICON=""
|
||||
MIC_MUTED_ICON=""
|
||||
|
||||
# Define some helper functions for getting/setting audio data using wireplumber (wpctl)
|
||||
|
||||
# $1 can either be "SINK" (speaker) or "SOURCE" (microphone)
|
||||
get_vol() {
|
||||
wpctl get-volume "@DEFAULT_AUDIO_${1}@" | awk '{print int($2*100)}'
|
||||
}
|
||||
|
||||
# $1 can either be "SINK" (speaker) or "SOURCE" (microphone)
|
||||
# #2 is the voulme (as percentage) to set the volume to
|
||||
# $3 is optional, if set, it can be '+' or '-', which then adds/decreases volume, instead of setting
|
||||
set_vol() {
|
||||
wpctl set-volume "@DEFAULT_AUDIO_${1}@" "$(awk -v n="$2" 'BEGIN{print (n / 100)}')$3"
|
||||
}
|
||||
|
||||
# $1 can either be "SINK" (speaker) or "SOURCE" (microphone)
|
||||
check_mute() {
|
||||
wpctl get-volume "@DEFAULT_AUDIO_${1}@" | grep -i muted >/dev/null
|
||||
echo $?
|
||||
}
|
||||
|
||||
# $1 can either be "SINK" (speaker) or "SOURCE" (microphone)
|
||||
toggle_mute() {
|
||||
wpctl set-mute "@DEFAULT_AUDIO_${1}@" toggle
|
||||
}
|
||||
|
||||
get_report() {
|
||||
spkr_vol="$(get_vol "SINK")"
|
||||
mic_vol="$(get_vol "SOURCE")"
|
||||
|
||||
if [ "$(check_mute "SINK")" == "0" ]; then
|
||||
spkr_mute="true"
|
||||
spkr_icon="$SPEAKER_MUTED_ICON"
|
||||
else
|
||||
spkr_mute="false"
|
||||
index="$(awk -v n="$spkr_vol" -v m="${#SPEAKER_ICONS[@]}" 'BEGIN{print int(n/(100/m))}')"
|
||||
|
||||
# We might end up with an higher than the length of icons, if the volume is over 100%
|
||||
# in this case, set the index to last icon
|
||||
if [ "$index" -ge "${#SPEAKER_ICONS[@]}" ]; then
|
||||
spkr_icon="${SPEAKER_ICONS[-1]}"
|
||||
else
|
||||
spkr_icon="${SPEAKER_ICONS[$index]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$(check_mute "SOURCE")" = "0" ]; then
|
||||
mic_mute="true"
|
||||
mic_icon="$MIC_MUTED_ICON"
|
||||
else
|
||||
mic_mute="false"
|
||||
mic_icon="$MIC_ICON"
|
||||
fi
|
||||
|
||||
echo "{ \"speaker_vol\": \"$spkr_vol\", \"speaker_mute\": $spkr_mute, \"speaker_icon\": \"$spkr_icon\", \"microphone_mute\": $mic_mute, \"microphone_vol\": \"$mic_vol\", \"microphone_icon\": \"$mic_icon\" }"
|
||||
}
|
||||
|
||||
# Continually run and report every volume change (into stdout)
|
||||
loop() {
|
||||
get_report
|
||||
pactl subscribe | grep --line-buffered "change" | while read -r _; do
|
||||
get_report
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"loop") loop ;;
|
||||
|
||||
"once") get_report ;;
|
||||
|
||||
"togglemute")
|
||||
if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then
|
||||
>&2 echo "Invalid usage, expected second argument to be 'SINK' or 'SOURCE', got '$2'"
|
||||
exit 1
|
||||
fi
|
||||
toggle_mute "$2"
|
||||
;;
|
||||
|
||||
"setvol")
|
||||
if [ "$2" != "SOURCE" ] && [ "$2" != "SINK" ]; then
|
||||
>&2 echo "Invalid usage, expected second argument to be 'SINK' or 'SOURCE', got '$2'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$3" =~ ^[+-]?[0-9]*\.?[0-9]+$ ]]; then
|
||||
case "$4" in
|
||||
"") set_vol "$2" "$3" ;;
|
||||
up | +) set_vol "$2" "$3" "+" ;;
|
||||
down | -) set_vol "$2" "$3" "-" ;;
|
||||
*)
|
||||
>&2 echo "Invalid usage, expected fourth argument to be up/down or +/-, got '$4'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
>&2 echo "Invalid usage, exepcted third argument to be a number, got '$3'"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
>&2 echo "Invalid usage, argument '$1' not recognized."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
6
home/programs/graphical/bars/eww/config/scripts/window_name
Executable file
6
home/programs/graphical/bars/eww/config/scripts/window_name
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
hyprland_ipc "activewindow" | ./scripts/window_name.py
|
112
home/programs/graphical/bars/eww/config/scripts/window_name.py
Executable file
112
home/programs/graphical/bars/eww/config/scripts/window_name.py
Executable file
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env python3
|
||||
"""This is a utility script for regex remaps on window names.
|
||||
|
||||
This is done in python, because of the complex regex logic, which would be pretty hard to
|
||||
recreate in bash. This python script is expected to be called from the bash script controlling
|
||||
the window names. Window name and class are obtained from piped stdin, to prevent having to
|
||||
needlessly keep restarting this program, which takes a while due to the interpreter starting
|
||||
overhead.
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from typing import Iterator, Optional
|
||||
|
||||
|
||||
class RemapRule:
|
||||
__slots__ = ("name_pattern", "output_pattern", "class_pattern")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name_pattern: str,
|
||||
output_pattern: str,
|
||||
class_pattern: Optional[str] = None,
|
||||
):
|
||||
self.name_pattern = re.compile(name_pattern)
|
||||
self.output_pattern = output_pattern
|
||||
self.class_pattern = re.compile(class_pattern) if class_pattern else None
|
||||
|
||||
def apply(self, window_name: str, window_class: str) -> str:
|
||||
"""Returns new name after this remap rule was applied."""
|
||||
if self.class_pattern is not None:
|
||||
if not self.class_pattern.fullmatch(window_class):
|
||||
# Rule doesn't apply, class mismatch, return original name
|
||||
return window_name
|
||||
|
||||
res = self.name_pattern.fullmatch(window_name)
|
||||
if not res:
|
||||
# Rule doesn't apply, name mismatch, return original name
|
||||
return window_name
|
||||
|
||||
# NOTE: This is potentially unsafe, since output_pattern might be say {0.__class__}, etc.
|
||||
# meaning this allows arbitrary attribute access, however it doesn't allow running functions
|
||||
# here. That said, code could still end up being run if there's some descriptor defined,
|
||||
# and generally I wouldn't trust this in production. However, this code is for my personal
|
||||
# use here, and all of the output patterns are hard-coded in this file, so in this case, it's
|
||||
# fine. But if you see this code and you'd like to use it in your production code, maybe don't
|
||||
return self.output_pattern.format(*res.groups())
|
||||
|
||||
|
||||
# Rules will be applied in specified order
|
||||
REMAP_RULES: list[RemapRule] = [
|
||||
RemapRule(r"", "", ""),
|
||||
RemapRule(r"(.*) — Mozilla Firefox", " {}", "firefox"),
|
||||
RemapRule(r"Mozilla Firefox", " Mozilla Firefox", "firefox"),
|
||||
RemapRule(r"Alacritty", " Alacritty", "Alacritty"),
|
||||
RemapRule(
|
||||
r"zsh;#toggleterm#1 - \(term:\/\/(.+)\/\/(\d+):(.+)\) - N?VIM",
|
||||
" Terminal: {0}",
|
||||
),
|
||||
RemapRule(r"(.+) \+ \((.+)\) - N?VIM", " {0} ({1}) [MODIFIED]"),
|
||||
RemapRule(r"(.+) \((.+)\) - N?VIM", " {0} ({1})"),
|
||||
RemapRule(r"(?:\[\d+\] )?\*?WebCord - (.+)", " {}", "WebCord"),
|
||||
RemapRule(r"(.+) - Discord", " {}", "discord"),
|
||||
RemapRule(r"(?:\(\d+\) )?Discord \| (.+)", " {}", "vesktop"),
|
||||
RemapRule(r"(.+) - mpv", " {}", "mpv"),
|
||||
RemapRule(r"Stremio - (.+)", " Stremio - {}", r"(Stremio)|(com.stremio.stremio)"),
|
||||
RemapRule(r"Spotify", " Spotify", "Spotify"),
|
||||
RemapRule(r"pulsemixer", " Pulsemixer"),
|
||||
RemapRule(r"(.*)", " {}", "Pcmanfm"),
|
||||
RemapRule(r"(.*)", " {}", "pcmanfm-qt"),
|
||||
# Needs to be last
|
||||
RemapRule(r"(.*)", " {}", "kitty"),
|
||||
]
|
||||
|
||||
MAX_LENGTH = 65
|
||||
|
||||
|
||||
def iter_window() -> Iterator[tuple[str, str]]:
|
||||
"""Listen for the window parameters from stdin/pipe, yields (window_name, window_class)."""
|
||||
for line in sys.stdin:
|
||||
line = line.removesuffix("\n")
|
||||
els = line.split(",", maxsplit=1)
|
||||
if len(els) != 2:
|
||||
raise ValueError(f"Expected 2 arguments from stdin line (name, class), but got {len(els)}")
|
||||
yield els[1], els[0]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
for window_name, window_class in iter_window():
|
||||
formatted_name = window_name
|
||||
for remap_rule in REMAP_RULES:
|
||||
new_name = remap_rule.apply(formatted_name, window_class)
|
||||
if new_name != formatted_name:
|
||||
formatted_name = new_name
|
||||
break
|
||||
|
||||
if len(formatted_name) > MAX_LENGTH:
|
||||
formatted_name = formatted_name[: MAX_LENGTH - 3] + "..."
|
||||
|
||||
ret = json.dumps(
|
||||
{
|
||||
"name": window_name,
|
||||
"class": window_class,
|
||||
"formatted_name": formatted_name,
|
||||
}
|
||||
)
|
||||
print(ret)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
13
home/programs/graphical/bars/eww/config/scripts/workspaces
Executable file
13
home/programs/graphical/bars/eww/config/scripts/workspaces
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/env bash
|
||||
|
||||
# shellcheck source=include
|
||||
source "./scripts/include"
|
||||
|
||||
if [ "$1" = "--switch" ]; then
|
||||
$HOME/.local/bin/scripts/gui/hyprland/swap-workspace "$2" >/dev/null
|
||||
# hyprctl dispatch workspace "$2" >/dev/null
|
||||
elif [ "$1" = "--loop" ]; then
|
||||
hyprland_ipc "workspace|createworkspace|destroyworkspace" | ./scripts/workspaces.py "$@"
|
||||
else
|
||||
./scripts/workspaces.py "$@"
|
||||
fi
|
188
home/programs/graphical/bars/eww/config/scripts/workspaces.py
Executable file
188
home/programs/graphical/bars/eww/config/scripts/workspaces.py
Executable file
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import TypedDict, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import SupportsRichComparison
|
||||
|
||||
|
||||
class WorkspaceInfo(TypedDict):
|
||||
id: int
|
||||
name: str
|
||||
monitor: str
|
||||
windows: int
|
||||
hasfullscreen: bool
|
||||
lastwindow: str
|
||||
lastwindowtitle: str
|
||||
|
||||
|
||||
class ActiveWorkspaceInfo(TypedDict):
|
||||
id: int
|
||||
name: str
|
||||
|
||||
|
||||
class MonitorInfo(TypedDict):
|
||||
id: int
|
||||
name: str
|
||||
description: str
|
||||
make: str
|
||||
model: str
|
||||
width: int
|
||||
height: int
|
||||
refreshRate: float
|
||||
x: int
|
||||
y: int
|
||||
activeWorkspace: ActiveWorkspaceInfo
|
||||
reserved: list[int]
|
||||
scale: float
|
||||
transform: int
|
||||
focused: bool
|
||||
dpmsStatus: bool
|
||||
vrr: bool
|
||||
|
||||
|
||||
class OutputWorkspaceInfo(WorkspaceInfo):
|
||||
format_name: str
|
||||
active: bool
|
||||
monitor_id: int
|
||||
|
||||
|
||||
# workspace id -> remapped name
|
||||
REMAPS = {
|
||||
1: "",
|
||||
2: "",
|
||||
3: "",
|
||||
4: "",
|
||||
5: "",
|
||||
6: "",
|
||||
7: "7",
|
||||
8: "8",
|
||||
9: "9",
|
||||
}
|
||||
|
||||
# Skip the special (scratchpad) workspace
|
||||
SKIP = {-99}
|
||||
|
||||
|
||||
def workspace_sort(obj: OutputWorkspaceInfo) -> "SupportsRichComparison":
|
||||
"""Returns a key to sort by, given the current element."""
|
||||
return obj["id"]
|
||||
|
||||
|
||||
def fill_blank_workspaces(open: list[OutputWorkspaceInfo]) -> list[OutputWorkspaceInfo]:
|
||||
"""Add in the rest of the workspaces which don't have any open windows on them.
|
||||
|
||||
This is needed because hyprland deletes workspaces with nothing in them.
|
||||
Note that this assumes all available workspaces were listed in REMAPS, and will
|
||||
only fill those. These blank workspaces will have most string values set to "N/A",
|
||||
and most int values set to 0.
|
||||
"""
|
||||
# Work on a copy, we don't want to alter the original list
|
||||
lst = open.copy()
|
||||
|
||||
for remap_id, format_name in REMAPS.items():
|
||||
# Skip for already present workspaces
|
||||
if any(ws_info["id"] == remap_id for ws_info in lst):
|
||||
continue
|
||||
|
||||
blank_ws: OutputWorkspaceInfo = {
|
||||
"id": remap_id,
|
||||
"name": str(remap_id),
|
||||
"monitor": "N/A",
|
||||
"windows": 0,
|
||||
"hasfullscreen": False,
|
||||
"lastwindow": "N/A",
|
||||
"lastwindowtitle": "N/A",
|
||||
"active": False,
|
||||
"format_name": format_name,
|
||||
"monitor_id": 0,
|
||||
}
|
||||
lst.append(blank_ws)
|
||||
|
||||
return lst
|
||||
|
||||
|
||||
def get_workspaces() -> list[OutputWorkspaceInfo]:
|
||||
"""Obtain workspaces from hyprctl, sort them and add format_name arg."""
|
||||
proc = subprocess.run(["hyprctl", "workspaces", "-j"], stdout=subprocess.PIPE)
|
||||
proc.check_returncode()
|
||||
try:
|
||||
workspaces: list[WorkspaceInfo] = json.loads(proc.stdout)
|
||||
except json.JSONDecodeError:
|
||||
sys.stderr.writelines([
|
||||
"Error decoding json response from hyprctl, returning empty workspaces",
|
||||
f"Actual captured output from hyprctl: {proc.stdout!r}"
|
||||
])
|
||||
sys.stderr.flush()
|
||||
workspaces = []
|
||||
|
||||
proc = subprocess.run(["hyprctl", "monitors", "-j"], stdout=subprocess.PIPE)
|
||||
proc.check_returncode()
|
||||
monitors: list[MonitorInfo] = json.loads(proc.stdout)
|
||||
|
||||
active_workspaces = {monitor["activeWorkspace"]["id"] for monitor in monitors}
|
||||
|
||||
out: list[OutputWorkspaceInfo] = []
|
||||
for workspace in workspaces:
|
||||
if workspace["id"] in SKIP:
|
||||
continue
|
||||
format_name = REMAPS.get(workspace["id"], workspace["name"])
|
||||
active = workspace["id"] in active_workspaces
|
||||
try:
|
||||
mon_id = [monitor["id"] for monitor in monitors if monitor["name"] == workspace["monitor"]][0]
|
||||
except IndexError: # Sometimes workspace["monitor"] is "?", which doesn't match any monitor
|
||||
mon_id = -1
|
||||
out.append({**workspace, "format_name": format_name, "active": active, "monitor_id": mon_id})
|
||||
|
||||
out = fill_blank_workspaces(out)
|
||||
out.sort(key=workspace_sort)
|
||||
return out
|
||||
|
||||
|
||||
def print_workspaces() -> None:
|
||||
wks = get_workspaces()
|
||||
ret = json.dumps(wks)
|
||||
print(ret)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--oneshot",
|
||||
action="store_true",
|
||||
help="Don't listen to stdout for updates, only run once and quit",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--loop",
|
||||
action="store_true",
|
||||
help="Listen to stdout input, once something is received, re-print workspaces"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.loop and args.oneshot:
|
||||
print("Can't use both --oneshot and --loop", file=sys.stdout)
|
||||
sys.exit(1)
|
||||
|
||||
if args.loop is None and args.oneshot is None:
|
||||
print("No option specified!", file=sys.stdout)
|
||||
sys.exit(1)
|
||||
|
||||
# Print workspaces here immediately, we don't want to have to wait for the first
|
||||
# update from stdin as we only receive those on actual workspace change.
|
||||
print_workspaces()
|
||||
|
||||
if args.oneshot:
|
||||
# We've already printed the workspaces once, we can exit now
|
||||
return
|
||||
|
||||
# Reprint workspaces on each stdin update (flush)
|
||||
for _ in sys.stdin:
|
||||
print_workspaces()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,14 @@
|
|||
(defwidget calendar-win []
|
||||
(box
|
||||
:class "calendar-win"
|
||||
(calendar)))
|
||||
|
||||
(defwindow calendar
|
||||
:monitor 0
|
||||
:geometry (geometry
|
||||
:x "0%"
|
||||
:y "0%"
|
||||
:anchor "top right"
|
||||
:width "0px"
|
||||
:height "0px")
|
||||
(calendar-win))
|
|
@ -0,0 +1,78 @@
|
|||
(defwidget radio-menu-win []
|
||||
(box
|
||||
:class "radio-menu-box"
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
|
||||
(box
|
||||
:class "text-row"
|
||||
:space-evenly false
|
||||
(label
|
||||
:class "title"
|
||||
:text "Radio/Connections Panel"))
|
||||
|
||||
(box
|
||||
:class "element-row"
|
||||
|
||||
(box
|
||||
:class "wifi-box"
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
(box
|
||||
:class "element icon"
|
||||
:space-evenly false
|
||||
:halign "center"
|
||||
(button
|
||||
:class "wifi-button"
|
||||
:tooltip "${net.state} (strength: ${net.signal}%)"
|
||||
:onclick "scripts/net toggle"
|
||||
{net.icon})
|
||||
(label
|
||||
:class "separator"
|
||||
:text "│")
|
||||
(button
|
||||
:class "wifi-arrow-btn"
|
||||
:onclick "eww close radio-menu && nm-connection-editor &"
|
||||
""))
|
||||
(label
|
||||
:text {net.essid}
|
||||
:xalign 0.5
|
||||
:limit-width 15))
|
||||
|
||||
(box
|
||||
:class "bluetooth-box"
|
||||
:space-evenly false
|
||||
:orientation "v"
|
||||
(box
|
||||
:class "element icon"
|
||||
:space-evenly false
|
||||
:halign "center"
|
||||
(button
|
||||
:class "bluetooth-button"
|
||||
:onclick "scripts/bluetooth toggle"
|
||||
:tooltip "${bluetooth.name} (${bluetooth.mac}) ${bluetooth.battery}"
|
||||
{bluetooth.icon})
|
||||
(label
|
||||
:class "separator"
|
||||
:text "│")
|
||||
(button
|
||||
:class "bluetooth-arrow-btn"
|
||||
:onclick "eww close radio-menu && blueberry"
|
||||
""))
|
||||
(label
|
||||
:text {bluetooth.name}
|
||||
:xalign 0.5
|
||||
:tooltip "${bluetooth.name} (${bluetooth.mac}) ${bluetooth.battery}"
|
||||
:limit-width 15)))
|
||||
))
|
||||
|
||||
(defwindow radio-menu
|
||||
:stacking "fg"
|
||||
:monitor 0
|
||||
:geometry (geometry
|
||||
:x "0"
|
||||
:y "0"
|
||||
:width "0%"
|
||||
:height "0%"
|
||||
:anchor "right top")
|
||||
(radio-menu-win))
|
62
home/programs/graphical/bars/eww/default.nix
Normal file
62
home/programs/graphical/bars/eww/default.nix
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
osConfig,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) mkIf;
|
||||
|
||||
cfg = osConfig.myOptions.home-manager.programs.bars.eww;
|
||||
in {
|
||||
config = mkIf cfg.enable {
|
||||
programs.eww = {
|
||||
enable = true;
|
||||
configDir = ./config;
|
||||
};
|
||||
|
||||
systemd.user.services = {
|
||||
"eww" = {
|
||||
Unit = {
|
||||
Description = "ElKowar's Wacky Widgets (eww) daemon";
|
||||
After = [ "graphical-session-pre.target" ];
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
ExecStart = pkgs.writeShellScript "eww-daemon" ''
|
||||
${pkgs.eww}/bin/eww daemon --no-daemonize
|
||||
'';
|
||||
|
||||
# Takes a value between -20 and 19. Higher values (e.g. 19) mean lower priority.
|
||||
# Lower priority means the process will get less CPU time and therefore will be slower.
|
||||
# Fortunately, I do not need my status bar to be fast. Also, te difference is almost
|
||||
# unnoticeable and definitely negligible.
|
||||
Nice = 19;
|
||||
};
|
||||
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
"eww-window@" = {
|
||||
Unit = {
|
||||
Description = "Open %I eww (ElKowar's Wacky Widgets) window";
|
||||
After = [ "eww.service" ];
|
||||
PartOf = [ "graphical-session-pre.target" ];
|
||||
};
|
||||
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStartPre = "${pkgs.eww}/bin/eww ping";
|
||||
ExecStart = "${pkgs.eww}/bin/eww open %i";
|
||||
ExecStop = "${pkgs.eww}/bin/eww close %i";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -3,5 +3,6 @@ _: {
|
|||
./wms
|
||||
./launchers
|
||||
./apps
|
||||
./bars
|
||||
];
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
};
|
||||
|
||||
programs = {
|
||||
bars.eww.enable = true;
|
||||
spotify.enable = true;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -12,6 +12,13 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
bars = {
|
||||
eww = {
|
||||
enable = mkEnableOption "Eww bar";
|
||||
autostart.enable = mkEnableOption "auto-starting eww daemon on graphical-session.target";
|
||||
};
|
||||
};
|
||||
|
||||
spotify.enable = mkEnableOption "Spotify";
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue