made some changes (yet again)

This commit is contained in:
Ludwig Lehnert 2025-04-21 12:13:08 +00:00
parent c2edb5060e
commit 61b35b0405
15 changed files with 191 additions and 294 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.venv/

View File

@ -7,3 +7,5 @@ alias tmux="tmux -2"
alias json-get="curl --request GET -H 'Content-Type: application/json'" alias json-get="curl --request GET -H 'Content-Type: application/json'"
alias json-post="curl --request POST -H 'Content-Type: application/json'" alias json-post="curl --request POST -H 'Content-Type: application/json'"
set -x TERM xterm-256color

View File

@ -27,13 +27,18 @@ monitor=,preferred,auto,auto
# Set programs that you use # Set programs that you use
$terminal = kitty $terminal = kitty
$fileManager = nautilus $fileManager = nautilus
$menu = wofi -S drun -i -I -s .config/wofi/style.css $menu = wofi -S drun -i -I -s $HOME/.config/wofi/style.css
exec-once = waybar exec-once = $HOME/.config/waybar/start.sh
exec-once = hypridle exec-once = hypridle
exec-once = hyprpaper exec-once = hyprpaper
exec-once = nm-applet
exec-once = blueman-applet
# Some default env vars. # Some default env vars.
env = HYPRCURSOR_ENABLED,0
env = XCURSOR_THEME,Bibata-Modern-Ice
env = XCURSOR_SIZE,24 env = XCURSOR_SIZE,24
env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that
@ -80,10 +85,10 @@ decoration {
passes = 1 passes = 1
} }
drop_shadow = yes #drop_shadow = yes
shadow_range = 4 #shadow_range = 4
shadow_render_power = 3 #shadow_render_power = 3
col.shadow = rgba(1a1a1aee) #col.shadow = rgba(1a1a1aee)
} }
animations { animations {
@ -109,7 +114,7 @@ dwindle {
master { master {
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
new_is_master = true #new_is_master = true
} }
gestures { gestures {
@ -209,4 +214,4 @@ bind = , XF86AudioMute, exec, pactl -- set-sink-mute @DEFAULT_SINK@ toggle
bind = , XF86MonBrightnessUp, exec, light -A 5 bind = , XF86MonBrightnessUp, exec, light -A 5
bind = , XF86MonBrightnessDown, exec, light -U 5 bind = , XF86MonBrightnessDown, exec, light -U 5
bind = , Print, exec, grim -g "$(slurp)" - | wl-copy bind = , Print, exec, hyprshot -m region --clipboard-only

View File

@ -117,9 +117,10 @@ label {
label { label {
monitor = monitor =
#text = 31% 
text = cmd[update:1000] ~/.config/waybar/battery.sh once text = cmd[update:1000] ~/.config/waybar/battery.sh once
color = $foreground color = $foreground
font_size = 24 font_size = 20
font_family = JetBrains Mono font_family = JetBrains Mono
position = -10, -10 position = -10, -10
halign = right halign = right

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 MiB

After

Width:  |  Height:  |  Size: 2.9 MiB

73
install
View File

@ -1,62 +1,7 @@
#!/usr/bin/bash #!/usr/bin/env bash
DIR=$(dirname $(realpath "$0")) DIR="$(dirname $(realpath "$0"))"
cd "$DIR"
if [[ -f /etc/os-release && $1 == "packages" ]]; then
. /etc/os-release
distro=${NAME,,}
if [[ $distro == "ubuntu" ]]; then
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update && sudo apt-get -y install --no-install-recommends wget curl
# install pfetch
curl -s https://raw.githubusercontent.com/dylanaraps/pfetch/master/pfetch > "$HOME/.local/bin/pfetch"
chmod +x "$HOME/.local/bin/pfetch"
# TODO: further setup
# add google chrome repository
curl -s https://dl-ssl.google.com/linux/linux_signing_key.pub > /tmp/google.pub
gpg --no-default-keyring --keyring /etc/apt/keyrings/google-chrome.gpg --import /tmp/google.pub
echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo add-apt-repository ppa:maveonair/helix-editor
sudo apt-get update && sudo apt-get -y install --no-install-recommends \
fish grim slurp wl-clipboard sway swaylock swaybg alacritty rofi \
light network-manager network-manager-openvpn google-chrome-stable xournalpp \
php python3 helix openjdk-21-jdk openjdk-21-jre gdm3 gnome seahorse
sudo systemctl enable gdm3
sudo systemctl enable NetworkManager
sudo apt-get upgrade -y
elif [[ $distro == "arch linux" ]]; then
sudo pacman -Sy --noconfirm
sudo pacman -S git curl wget --noconfirm
sudo pacman --noconfirm -S \
fish networkmanager networkmanager-openvpn \
gdm gnome helix docker docker-buildx \
flatpak gnome-software \
distrobox
sudo systemctl enable gdm
sudo systemctl enable NetworkManager
sudo systemctl enable docker
fi
sudo usermod -aG video $USER
sudo usermod -aG docker $USER
fi
cd $DIR
# install config files # install config files
for filename in $(ls -p | grep -v "install" | grep -v "/" | grep -v "README.md"); do for filename in $(ls -p | grep -v "install" | grep -v "/" | grep -v "README.md"); do
@ -73,11 +18,15 @@ cp ./backgrounds/* "$HOME/.local/share/wallpapers/"
mkdir -p "$HOME/.local/bin" mkdir -p "$HOME/.local/bin"
# install pfetch # install pfetch
curl -s https://raw.githubusercontent.com/dylanaraps/pfetch/master/pfetch > "$HOME/.local/bin/pfetch" if [[ ! -e "$HOME/.local/bin/pfetch" ]]; then
chmod +x "$HOME/.local/bin/pfetch" curl -s https://raw.githubusercontent.com/dylanaraps/pfetch/master/pfetch > "$HOME/.local/bin/pfetch"
chmod +x "$HOME/.local/bin/pfetch"
fi
# provide rickroll # provide rickroll
curl -s https://raw.githubusercontent.com/keroserene/rickrollrc/master/roll.sh > "$HOME/.local/bin/rickroll" if [[ ! -e "$HOME/.local/bin/rickroll" ]]; then
chmod +x "$HOME/.local/bin/rickroll" curl -s https://raw.githubusercontent.com/keroserene/rickrollrc/master/roll.sh > "$HOME/.local/bin/rickroll"
chmod +x "$HOME/.local/bin/rickroll"
fi

View File

@ -3,28 +3,24 @@ font_family GoMono
window_margin_width 0 window_margin_width 0
background #323232 background #131313
foreground #ffffff foreground #d6dae4
cursor #d6d6d6 cursor #b9b9b9
selection_background #5b5b5b selection_background #1f1f1f
selection_foreground #323232 color0 #1f1f1f
color0 #353535 color8 #d6dae4
color8 #535353 color1 #f71118
color1 #d25252 color9 #de342e
color9 #f00c0c color2 #2cc55d
color2 #a4c161 color10 #1dd260
color10 #c1df74 color3 #ecb90f
color3 #ffc56d color11 #f2bd09
color11 #e1e48a color4 #2a84d2
color4 #6c99ba color12 #0f80d5
color12 #8ab6d9 color5 #4e59b7
color5 #d096d9 color13 #524fb9
color13 #efb5f7 color6 #0f80d5
color6 #bdd6ff color14 #0f7cda
color14 #dbf4ff color7 #d6dae4
color7 #ededec
color15 #ffffff color15 #ffffff
active_tab_foreground #ffffff selection_foreground #131313
active_tab_background #535353
inactive_tab_foreground #ffffff
inactive_tab_background #353535

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/usr/bin/env bash
print_status() { print_status() {
echo $(upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep percentage | sed 's/percentage://g') " " echo $(upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep percentage | sed 's/percentage://g') " "
} }
print_status print_status

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
print_status() { print_status() {
echo "$(light -G | xargs printf %.0f)%" " " echo "$(light -G | xargs printf %.0f)%" " "

View File

@ -1,7 +1,7 @@
{ {
"modules-left": ["hyprland/workspaces", "hyprland/window"], "modules-left": ["hyprland/workspaces", "hyprland/window"],
"modules-center": ["clock", "custom/lock"], "modules-center": ["clock", "custom/lock"],
"modules-right": ["tray", "custom/spotify", "pulseaudio", "custom/brightness", "battery"], "modules-right": ["tray", "custom/spotify", "custom/ram", "custom/cpu", "pulseaudio", "custom/brightness", "battery"],
"network": { "network": {
"format-wifi": "{essid} ({signalStrength}%) ", "format-wifi": "{essid} ({signalStrength}%) ",
@ -11,10 +11,10 @@
"on-click": "kitty -e 'nmtui'" "on-click": "kitty -e 'nmtui'"
}, },
// "tray": { "tray": {
// "icon-size": 15, "icon-size": 15,
// "spacing": 10 "spacing": 2
// }, },
"clock": { "clock": {
// "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>", // "tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
@ -60,6 +60,16 @@
"format-icons": [" ", " ", " "] "format-icons": [" ", " ", " "]
}, },
"custom/ram": {
"return-type": "text",
"exec": "~/.config/waybar/ram.sh"
},
"custom/cpu": {
"return-type": "text",
"exec": "~/.config/waybar/cpu.sh"
},
// "custom/battery": { // "custom/battery": {
// "return-type": "text", // "return-type": "text",
// "exec": "~/.config/waybar/battery.sh" // "exec": "~/.config/waybar/battery.sh"

15
waybar-cpu.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
print_status() {
top -bn1 | awk '/^%Cpu/ {printf "%d%%  \n", 100 - $8}'
}
print_status
if [ -z "$1" ] || [ "$1" -ne "once" ]; then
while sleep 1; do
print_status
done
fi

View File

@ -1,203 +1,90 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import gi
gi.require_version("Playerctl", "2.0")
from gi.repository import Playerctl, GLib
from gi.repository.Playerctl import Player
import argparse
import logging
import sys import sys
import signal
import gi
import json import json
import os import time
import io import argparse
import json import subprocess
from typing import List
logger = logging.getLogger(__name__) LIMIT = 23
def signal_handler(sig, frame): def parse_args():
logger.info("Received signal to stop, exiting") parser = argparse.ArgumentParser()
sys.stdout.write("\n") parser.add_argument('--player', type=str, required=True)
sys.stdout.flush() return parser.parse_args()
# loop.quit()
sys.exit(0)
def query_status(player: str) -> str:
try:
return subprocess.check_output(["playerctl", "-p", player, "status"], stderr=subprocess.DEVNULL).decode().strip().lower()
except:
return 'no players found'
class PlayerManager: def query_metadata(player: str) -> dict[str, str]:
def __init__(self, selected_player=None, excluded_player=[], limit=35): try:
self.manager = Playerctl.PlayerManager() raw = subprocess.check_output(["playerctl", "-p", player, "metadata"], stderr=subprocess.DEVNULL).decode().strip()
self.loop = GLib.MainLoop() except:
self.manager.connect( return {}
"name-appeared", lambda *args: self.on_player_appeared(*args))
self.manager.connect(
"player-vanished", lambda *args: self.on_player_vanished(*args))
signal.signal(signal.SIGINT, signal_handler) results = {}
signal.signal(signal.SIGTERM, signal_handler) for line in raw.split('\n'):
signal.signal(signal.SIGPIPE, signal.SIG_DFL) line = line[line.index(' ')+1:]
self.selected_player = selected_player key = line[:line.index(' ')].strip()
self.excluded_player = excluded_player.split(',') if excluded_player else [] value = line[line.index(' ')+1:].strip()
self.limit = limit results[key] = value
self.init_players() return results
def init_players(self): def write_text(player: str, text: str, last_text = str | None) -> str:
for player in self.manager.props.player_names: if text == last_text: return text
if player.name in self.excluded_player:
continue
if self.selected_player is not None and self.selected_player != player.name:
logger.debug(f"{player.name} is not the filtered player, skipping it")
continue
self.init_player(player)
def run(self): sys.stdout.write(json.dumps({
logger.info("Starting main loop") "text": text,
self.loop.run() "class": player,
"alt": player
def init_player(self, player): }))
logger.info(f"Initialize new player: {player.name}") sys.stdout.write('\n')
player = Playerctl.Player.new_from_name(player)
player.connect("playback-status",
self.on_playback_status_changed, None)
player.connect("metadata", self.on_metadata_changed, None)
self.manager.manage_player(player)
self.on_metadata_changed(player, player.props.metadata)
def get_players(self) -> List[Player]:
return self.manager.props.players
def write_output(self, text, player):
logger.debug(f"Writing output: {text}")
if len(text) > self.limit:
text = text[:self.limit] + ''
output = {"text": text,
"class": "custom-" + player.props.player_name,
"alt": player.props.player_name}
sys.stdout.write(json.dumps(output) + "\n")
sys.stdout.flush() sys.stdout.flush()
def clear_output(self): return text
sys.stdout.write("\n")
def print_info(player: str, last_text = str | None) -> str | None:
status = query_status(player)
if status == 'no players found':
if last_text is not None:
sys.stdout.write('\n')
sys.stdout.flush() sys.stdout.flush()
def on_playback_status_changed(self, player, status, _=None):
logger.debug(f"Playback status changed for player {player.props.player_name}: {status}")
self.on_metadata_changed(player, player.props.metadata)
def get_first_playing_player(self):
players = self.get_players()
logger.debug(f"Getting first playing player from {len(players)} players")
if len(players) > 0:
# if any are playing, show the first one that is playing
# reverse order, so that the most recently added ones are preferred
for player in players[::-1]:
if player.props.status == "Playing":
return player
# if none are playing, show the first one
return players[0]
else:
logger.debug("No players found")
return None return None
def show_most_important_player(self): metadata = query_metadata(player)
logger.debug("Showing most important player") artist = metadata.get('xesam:artist')
# show the currently playing player title = metadata.get('xesam:title').replace("&", "&amp;")
# or else show the first paused player album = metadata.get('xesam:album')
# or else show nothing
current_player = self.get_first_playing_player()
if current_player is not None:
self.on_metadata_changed(current_player, current_player.props.metadata)
else:
self.clear_output()
def on_metadata_changed(self, player, metadata, _=None): icon = '' if status == 'playing' else ''
logger.debug(f"Metadata changed for player {player.props.player_name}")
player_name = player.props.player_name
artist = player.get_artist()
title = player.get_title()
title = title.replace("&", "&amp;")
track_info = "" text = f'{title} {artist}' if artist and title else title
if player_name == "spotify" and "mpris:trackid" in metadata.keys() and ":ad:" in player.props.metadata["mpris:trackid"]: if len(text) > LIMIT:
track_info = "Advertisement" text = text[:LIMIT] + ''
elif artist is not None and title is not None:
track_info = f"{title}{artist}"
else:
track_info = title
if track_info: return write_text(player, f'{icon} {text}', last_text)
if player.props.status == "Playing":
track_info = "" + track_info
else:
track_info = "" + track_info
# only print output if no other player is playing
current_playing = self.get_first_playing_player()
if current_playing is None or current_playing.props.player_name == player.props.player_name:
self.write_output(track_info, player)
else:
logger.debug(f"Other player {current_playing.props.player_name} is playing, skipping")
def on_player_appeared(self, _, player):
logger.info(f"Player has appeared: {player.name}")
if player.name in self.excluded_player:
logger.debug(
"New player appeared, but it's in exclude player list, skipping")
return
if player is not None and (self.selected_player is None or player.name == self.selected_player):
self.init_player(player)
else:
logger.debug(
"New player appeared, but it's not the selected player, skipping")
def on_player_vanished(self, _, player):
logger.info(f"Player {player.props.player_name} has vanished")
self.show_most_important_player()
def parse_arguments():
parser = argparse.ArgumentParser()
# Increase verbosity with every occurrence of -v
parser.add_argument("-v", "--verbose", action="count", default=0)
parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player")
parser.add_argument("-l", "--limit", "- Limit resulting text length", default=25, type=int)
# Define for which player we"re listening
parser.add_argument("--player")
parser.add_argument("--enable-logging", action="store_true")
return parser.parse_args()
def main(): def main():
arguments = parse_arguments() args = parse_args()
# Initialize logging sys.stdout.write('\n')
if arguments.enable_logging: sys.stdout.flush()
logfile = os.path.join(os.path.dirname(
os.path.realpath(__file__)), "media-player.log")
logging.basicConfig(filename=logfile, level=logging.DEBUG,
format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s")
# Logging is set by default to WARN and higher. try:
# With every occurrence of -v it's lowered by one text = None
logger.setLevel(max((3 - arguments.verbose) * 10, 0)) while True:
try: text = print_info(args.player, text)
except: pass
time.sleep(0.5)
except KeyboardInterrupt:
pass
logger.info("Creating player manager")
if arguments.player:
logger.info(f"Filtering for player: {arguments.player}")
if arguments.exclude:
logger.info(f"Exclude player {arguments.exclude}")
player = PlayerManager(arguments.player, arguments.exclude, arguments.limit)
player.run()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

15
waybar-ram.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
print_status() {
free -m | awk '/^Mem:/ {printf "%.2f GiB  \n", $3 / 1024}'
}
print_status
if [ -z "$1" ] || [ "$1" -ne "once" ]; then
while sleep 1; do
print_status
done
fi

5
waybar-start.sh Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
cd "$(dirname "$0")"
ls * | entr -r sh -c 'killall waybar; waybar &'

View File

@ -2,7 +2,7 @@
border: none; border: none;
font-family: Font Awesome, Roboto, Arial, sans-serif; font-family: Font Awesome, Roboto, Arial, sans-serif;
font-size: 13px; font-size: 13px;
color: #ffffff; color: white;
border-radius: 20px; border-radius: 20px;
} }
@ -47,14 +47,24 @@ window#waybar {
padding: 0 10px; padding: 0 10px;
} }
#tray * {
color: #303030;
}
#custom-cpu {
min-width: 48px;
}
#clock, #clock,
#battery, #battery,
#pulseaudio, #pulseaudio,
#custom-lock, #custom-lock,
#custom-media, #custom-media,
#custom-spotify, #custom-spotify,
#custom-brightness { #custom-brightness,
padding: 0 6px; #custom-cpu,
#custom-ram {
padding: 0 4px;
} }
#pulseaudio.muted { #pulseaudio.muted {
color: #cc3436; color: #cc3436;