Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions .config/rofi/scripts/clipboard_history.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,41 @@
#!/usr/bin/env bash
# Copy, delete, or confirm clearing clipboard history without changing it on cancel.
set -euo pipefail
trap 'notify-send "Clipboard" "Clipboard operation failed." >&2 || true' ERR
payload=$(mktemp)
trap 'rm -f "$payload"' EXIT

# Show clipboard history with custom message using dedicated theme
cliphist list | rofi -dmenu -i \
-p " CLIPBOARD HISTORY" \
-mesg " [ ENTER ] PASTE [ DEL ] DELETE ENTRY [ CTRL+DEL ] CLEAR ALL" \
-theme ~/.config/rofi/themes/clipboard.rasi | cliphist decode | wl-copy

# Notify if copied
if [ -n "$(wl-paste 2>/dev/null || true)" ]; then
notify-send "Clipboard" "Content copied!" -t 2000
fi
while true; do
history=$(cliphist list)
[[ -n $history ]] || { notify-send 'Clipboard' 'History is empty.'; exit 0; }
mapfile -t entries <<< "$history"
status=0
selected=$(printf '%s\n' "${entries[@]}" | rofi -dmenu -i -no-custom -format i \
-kb-remove-char-forward Control+d -kb-remove-word-forward Control+Alt+d \
-kb-custom-1 Delete -kb-custom-2 Control+Delete \
-p 'CLIPBOARD HISTORY' -mesg '[ ENTER ] COPY [ DEL ] DELETE ENTRY [ CTRL+DEL ] CLEAR ALL' \
-theme "$HOME/.config/rofi/themes/clipboard.rasi") || status=$?
[[ $status != 1 ]] || exit 0
case $status in
0|10)
[[ $selected =~ ^(0|[1-9][0-9]*)$ ]] && (( selected < ${#entries[@]} )) || exit 1
if [[ $status == 10 ]]; then
printf '%s\n' "${entries[selected]}" | cliphist delete
continue
fi
# Decode first: a failed lookup must not replace the current clipboard.
printf '%s\n' "${entries[selected]}" | cliphist decode > "$payload"
wl-copy < "$payload"
notify-send 'Clipboard' 'Content copied!' -t 2000
exit 0
;;
11)
confirm_status=0
confirmation=$(printf 'Cancel\nClear all\n' | rofi -dmenu -no-custom -format i \
-p 'Clear clipboard history?' -theme "$HOME/.config/rofi/themes/current.rasi") || confirm_status=$?
if [[ $confirm_status == 0 && $confirmation == 1 ]]; then cliphist wipe; fi
[[ $confirm_status == 0 || $confirm_status == 1 ]] || exit 1
;;
*) exit "$status" ;;
esac
done
59 changes: 30 additions & 29 deletions .config/rofi/scripts/wallpaper.sh
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
#!/bin/bash

WALLPAPER_DIR="$HOME/Pictures/Wallpapers"
[ -d "$WALLPAPER_DIR" ] || WALLPAPER_DIR="$HOME/Pictures/Wallpaper"

# Get all wallpapers
WALLPAPERS=$(find "$WALLPAPER_DIR" -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.jpeg" \) | sort)

# Check if directory has wallpapers
if [ -z "$WALLPAPERS" ]; then
notify-send "Wallpaper Selector" "No wallpapers found in $WALLPAPER_DIR"
exit 1
fi

# Create rofi menu with image names
SELECTED=$(echo "$WALLPAPERS" | while read -r wallpaper; do
basename "$wallpaper"
done | rofi -dmenu -i -theme ~/.config/rofi/themes/current.rasi -p "Select Wallpaper" \
-theme-str 'window {width: 600px;} listview {lines: 10;}')

# If something was selected
if [ -n "$SELECTED" ]; then
WALLPAPER_PATH=$(echo "$WALLPAPERS" | grep "$SELECTED$")

if [ -f "$WALLPAPER_PATH" ]; then
awww img "$WALLPAPER_PATH" --transition-type fade --transition-bezier .23,1,.32,1 --transition-duration 0.8 --transition-fps 144
notify-send "Wallpaper Changed" "$SELECTED" -i "$WALLPAPER_PATH" -t 3000
fi
fi
#!/usr/bin/env bash
# Select wallpaper paths by index, preserving punctuation and duplicate basenames.
set -euo pipefail
trap 'notify-send "Wallpaper" "Could not select or apply wallpaper." >&2 || true' ERR
wallpaper_dir="$HOME/Pictures/Wallpapers"
[[ -d $wallpaper_dir ]] || wallpaper_dir="$HOME/Pictures/Wallpaper"
[[ -d $wallpaper_dir ]] || { notify-send 'Wallpaper' 'Wallpaper directory not found.'; exit 1; }
# A temporary null-delimited list preserves paths and lets discovery failures surface.
list=$(mktemp)
trap 'rm -f "$list"' EXIT
find "$wallpaper_dir" -type f \( -iname '*.jpg' -o -iname '*.png' -o -iname '*.jpeg' \) -print0 | sort -z > "$list"
mapfile -d '' -t wallpapers < "$list"
(( ${#wallpapers[@]} )) || { notify-send 'Wallpaper' 'No wallpapers found.'; exit 0; }
names=()
for wallpaper in "${wallpapers[@]}"; do
name=${wallpaper#"$wallpaper_dir/"}
# Display control characters safely; the index still resolves the original path.
printf -v name '%q' "$name"
names+=("$name")
done
status=0
selected=$(printf '%s\n' "${names[@]}" | rofi -dmenu -i -no-custom -format i \
-theme "$HOME/.config/rofi/themes/current.rasi" -p 'Select Wallpaper' \
-theme-str 'window {width: 600px;} listview {lines: 10;}') || status=$?
[[ $status != 1 ]] || exit 0
[[ $status == 0 ]] || exit "$status"
[[ $selected =~ ^(0|[1-9][0-9]*)$ ]] && (( selected < ${#wallpapers[@]} )) || exit 1
awww img "${wallpapers[selected]}" --transition-type fade --transition-bezier .23,1,.32,1 \
--transition-duration 0.8 --transition-fps 144
notify-send 'Wallpaper Changed' "${names[selected]}" -i "${wallpapers[selected]}" -t 3000
32 changes: 21 additions & 11 deletions .config/rofi/scripts/wifi.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
#!/bin/bash
notify-send "WiFi" "Scanning networks..."
WIFI_LIST=$(nmcli -f SSID,SECURITY,SIGNAL device wifi list | tail -n +2)
CHOSEN=$(echo "$WIFI_LIST" | rofi -dmenu -i -theme ~/.config/rofi/themes/current.rasi -p "WiFi Networks" | awk '{print $1}')

if [ -n "$CHOSEN" ]; then
PASSWORD=$(rofi -dmenu -theme ~/.config/rofi/themes/current.rasi -password -p "Password for $CHOSEN")
nmcli device wifi connect "$CHOSEN" password "$PASSWORD" && \
notify-send "WiFi" "Connected to $CHOSEN" || \
notify-send "WiFi" "Failed to connect"
fi
#!/usr/bin/env bash
# Select an intact SSID and delegate credentials to NetworkManager in Kitty.
set -euo pipefail
trap 'notify-send "Wi-Fi" "Could not open the network connection prompt." >&2 || true' ERR
networks=$(nmcli --escape no --get-values SSID device wifi list)
declare -A seen=()
ssids=()
# shortcut: SSIDs containing newlines are not representable in this menu; use nmcli directly for those.
while IFS= read -r ssid; do
[[ -n $ssid ]] || continue
if [[ ! ${seen["$ssid"]+present} ]]; then ssids+=("$ssid"); seen["$ssid"]=1; fi
done <<< "$networks"
(( ${#ssids[@]} )) || { notify-send 'Wi-Fi' 'No named networks found.'; exit 0; }
status=0
selected=$(printf '%s\n' "${ssids[@]}" | rofi -dmenu -i -no-custom -format i \
-theme "$HOME/.config/rofi/themes/current.rasi" -p 'Wi-Fi Networks') || status=$?
[[ $status != 1 ]] || exit 0
[[ $status == 0 ]] || exit "$status"
[[ $selected =~ ^(0|[1-9][0-9]*)$ ]] && (( selected < ${#ssids[@]} )) || exit 1
# No password is collected or placed in command arguments by this script.
kitty --hold -e nmcli --ask device wifi connect "${ssids[selected]}"
2 changes: 1 addition & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ jobs:
- run: sudo apt-get update && sudo apt-get install -y shellcheck jq
- run: bash tests/run.sh
- name: ShellCheck changed shell scripts
run: shellcheck .config/hypr/scripts/wallpaper.sh tests/*.sh
run: shellcheck .config/hypr/scripts/wallpaper.sh .config/rofi/scripts/{wifi,clipboard_history,wallpaper}.sh scripts/apply-system-optimizations.sh tests/*.sh
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,22 +164,18 @@ Existing conflicting configuration files or directories will be safely backed up

## System optimizations

An automated tuning script is included in `scripts/apply-system-optimizations.sh`. It configures:

- DNF parallel downloads (capped at 2), fastest mirror selection, and default yes prompts.
- Tuned power profile set to `desktop` for responsive CPU governor scaling.
- Disables `NetworkManager-wait-online.service` to trim cold boot time.
- Inotify user watch limit raised to 524,288 for large development projects.
- Hardware video decode via RPM Fusion `mesa-va-drivers-freeworld` on AMD Radeon GPUs.
- Disables idle background daemons (`ModemManager`, `thermald`, `rsyslog`, `abrtd`, and `lvm2-monitor`).
- Installs modern developer CLI tools (`fzf`, `zoxide`, `eza`, `git-delta`, `duf`).

To apply these tweaks:
The default script changes only the developer file-watch limit:

```bash
sudo ./scripts/apply-system-optimizations.sh
```

It preserves unrelated sysctl settings and higher watch limits, backs up changed
files, verifies the kernel value, and restores the previous setting if application
fails. It does not install packages, alter power profiles, or disable services.
See [optional system steps](docs/system-tuning.md) for individually verified
changes and rollback. Previously applied system settings are left in place.

## Verification

Run `bash tests/run.sh` for shell syntax and isolated regression checks. Tests use
Expand All @@ -188,3 +184,10 @@ mock commands and temporary homes; they do not alter the running desktop.
The workspace wallpaper listener has one owner per Hyprland session. `SUPER+A`
runs `wallpaper.sh --once` to refresh without creating another listener. Workspaces
6–10 retain the last wallpaper. Missing images and daemon failures go to stderr.

Rofi Wi-Fi selection opens Kitty with NetworkManager's `nmcli --ask` prompt;
saved credentials and authentication remain NetworkManager's responsibility.
Clipboard history supports Enter to copy, Delete to delete the selected entry,
and Ctrl+Delete to clear history after confirmation. Escape leaves the clipboard
and history alone. Wallpaper selection displays relative paths and handles
punctuation and duplicate filenames without regex matching.
67 changes: 67 additions & 0 deletions docs/system-tuning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Optional workstation changes

## TL;DR

The default script only raises the developer file-watch limit. Apply the steps
below separately after recording current settings. These commands are for the
Fedora workstation; they are not automatic optimizations or measured wins.

## DNF preferences

Inspect `/etc/dnf/dnf.conf` and back it up with `sudo cp -a --backup=numbered
/etc/dnf/dnf.conf /etc/dnf/dnf.conf.before-dotfiles` before editing. Keep
`max_parallel_downloads=2` as the existing preference until download measurements
justify another value. `fastestmirror=True` selects by connection latency, not
throughput. `defaultyes=True` changes the default confirmation choice, not speed.
For Fedora defaults remove those overrides only after reviewing the file. Verify
with `dnf --dump-main-config` (check the installed DNF version's help first).
Restore the saved file to roll back.

Reference: [DNF5 configuration](https://dnf5.readthedocs.io/en/latest/dnf5.conf.5.html).

## Power profile

Run `tuned-adm active` and `tuned-adm verify` in the actual host session. Record the
active profile before testing another. Keep the current profile by default.
If testing desktop scaling, first confirm `desktop` appears in `tuned-adm list`,
then run `sudo tuned-adm profile desktop`; verify and compare responsiveness and
idle power. Roll back with `sudo tuned-adm profile PREVIOUS_PROFILE`. A file in
`/etc/tuned` is not proof of live activation; do not disable other power managers
without understanding the host's integration.

## Services and boot

Measure `systemd-analyze critical-chain` and `systemd-analyze blame` first. Inspect
`systemctl list-dependencies --reverse NetworkManager-wait-online.service` and
whether network-dependent mounts/services need it. If demonstrated unnecessary,
record its enabled state, then use `sudo systemctl disable
NetworkManager-wait-online.service`; compare subsequent boots. Restore its
previous enabled state to roll back. Never blindly disable ModemManager, thermald,
rsyslog, abrtd, or lvm2-monitor; identify actual use and dependencies first.
Previously disabled services require individual review, not blanket re-enabling.

## AMD hardware video decoding

Inspect `rpm -q mesa-va-drivers mesa-va-drivers-freeworld` and `vainfo`; confirm
which codecs and driver the running application uses. A package's presence alone
does not establish active browser decoding. If needed, follow the current
[RPM Fusion codec instructions](https://rpmfusion.org/Howto/Multimedia) for the
installed Fedora release. Review a proposed DNF swap interactively; do not add
repositories or import keys silently. Record the transaction ID and package
versions. Review `sudo dnf history undo TRANSACTION_ID` to roll back; availability
of old packages can limit rollback. Do not claim codec acceleration until verified
in the application.

## Developer tools

Review and run `sudo dnf install fzf zoxide eza git-delta duf` separately. Verify
with `command -v` for each executable (`delta` is the git-delta executable).
Record the DNF transaction for rollback; avoid removing pre-existing tools.

## File-watch rollback

On success, the watch-limit helper prints any backup path. Restore that exact
file and apply just its watch-limit value with `sudo sysctl -w
fs.inotify.max_user_watches=PREVIOUS_VALUE`. If no file previously existed, remove
only the newly created workstation file and restore the previously recorded
kernel value. Do not apply every sysctl file as a shortcut to rollback.
64 changes: 5 additions & 59 deletions scripts/apply-system-optimizations.sh
Original file line number Diff line number Diff line change
@@ -1,63 +1,9 @@
#!/usr/bin/env bash
# Apply only the backed-up, verified developer file-watch setting.
set -euo pipefail

if [ "$EUID" -ne 0 ]; then
echo "[-] Please run this script with sudo: sudo ./scripts/apply-system-optimizations.sh"
if (( EUID != 0 )); then
echo 'Run: sudo ./scripts/apply-system-optimizations.sh' >&2
exit 1
fi

echo "[+] 1. Configuring DNF for fast mirrors and parallel downloads (max 2)..."
if ! grep -q "max_parallel_downloads" /etc/dnf/dnf.conf 2>/dev/null; then
cat << 'EOF' >> /etc/dnf/dnf.conf
max_parallel_downloads=2
fastestmirror=True
defaultyes=True
EOF
echo " Done: Added max_parallel_downloads=2, fastestmirror=True, defaultyes=True."
else
sed -i 's/^max_parallel_downloads=.*/max_parallel_downloads=2/' /etc/dnf/dnf.conf
echo " Done: Updated max_parallel_downloads to 2."
fi

echo "[+] 2. Setting tuned power profile to 'desktop' (optimized for Ryzen desktop workloads)..."
if command -v tuned-adm >/dev/null 2>&1; then
tuned-adm profile desktop
echo " Done: Active tuned profile set to desktop."
fi

echo "[+] 3. Shaving ~5.6s off boot time (disabling NetworkManager-wait-online.service)..."
systemctl disable NetworkManager-wait-online.service 2>/dev/null || true
echo " Done: NetworkManager-wait-online disabled."

echo "[+] 4. Raising inotify file watch limits for dev projects..."
cat << 'EOF' > /etc/sysctl.d/99-workstation.conf
# Raise file watch limits for IDEs, Node, Rust, and Python dev
fs.inotify.max_user_watches=524288
EOF
sysctl -p /etc/sysctl.d/99-workstation.conf
echo " Done: inotify watches set to 524288."

echo "[+] 5. Setting up RPM Fusion Free & hardware video acceleration (VA-API freeworld)..."
if ! dnf repolist | grep -q "rpmfusion-free"; then
echo " Enabling RPM Fusion Free repository..."
dnf install -y "https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm" || true
fi

if dnf repolist | grep -q "rpmfusion-free"; then
echo " Swapping mesa-va-drivers for freeworld hardware acceleration..."
dnf swap -y mesa-va-drivers mesa-va-drivers-freeworld || true
echo " Done: Hardware video acceleration installed."
fi

echo "[+] 6. Disabling unnecessary background daemons (ModemManager, thermald, rsyslog, abrtd, lvm2-monitor)..."
systemctl disable --now thermald ModemManager rsyslog abrtd lvm2-monitor 2>/dev/null || true
echo " Done: Unnecessary background daemons disabled."

echo "[+] 7. Installing modern developer CLI tools (fzf, zoxide, eza, git-delta, duf)..."
if dnf install -y fzf zoxide eza git-delta duf; then
echo " Done: Modern developer CLI tools installed."
else
echo " [!] Warning: Failed to install developer CLI tools. Install manually via 'sudo dnf install fzf zoxide eza git-delta duf'."
fi

echo "[✓] All system-level performance optimizations applied successfully!"
python3 "$(dirname "${BASH_SOURCE[0]}")/inotify.py"
echo 'File-watch setting verified. Other system settings were not changed.'
Loading
Loading