replace the old dotfiles with the sakura line rice
The bare $HOME repo is gone. This is now the rice: install.sh, rice-* scripts in bin/, configs in home/ linked into $HOME, theme templates, st and the particle wallpaper. Clone to ~/rice and run install.sh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BLLuuQfeDiqFHHSgd2VWrQ
This commit is contained in:
commit
4b203d06bd
104 changed files with 8077 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# build output
|
||||
particles/particles
|
||||
particles/*.o
|
||||
|
||||
# never commit private files, even if someone copies them in here
|
||||
home/.config/rice/secrets/
|
||||
home/.config/rice/local.sh
|
||||
home/.bashrc.local
|
||||
home/.config/rice/theme.override.toml
|
||||
106
README.md
Normal file
106
README.md
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Sakura Line
|
||||
|
||||
A bspwm rice for Arch Linux where everything — bar, menus, popups,
|
||||
notifications, lock screen, terminal and prompt — is drawn from one palette and
|
||||
one set of shapes, so it feels like one system instead of a pile of tools.
|
||||
|
||||

|
||||
|
||||
## Install
|
||||
|
||||
On a clean Arch install, as your normal user:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://rice.srtk.in/install.sh | bash
|
||||
```
|
||||
|
||||
or from a clone:
|
||||
|
||||
```sh
|
||||
git clone https://git.srtk.in/sarthak/dotfiles.git ~/rice
|
||||
~/rice/install.sh # full install, safe to re-run
|
||||
~/rice/install.sh --dry-run # print every step, change nothing
|
||||
~/rice/install.sh --update # pull, re-link, re-render, rebuild what changed
|
||||
```
|
||||
|
||||
The installer installs the packages (bootstrapping paru for the AUR), symlinks
|
||||
every file in `home/` into `$HOME` and every script in `bin/` into
|
||||
`~/.local/bin`, renders the theme, builds st with sixel images, and offers
|
||||
calendar sync and a stock Plymouth boot splash. Anything it replaces is moved
|
||||
to `~/.rice-backup-<date>/`. Log in on tty1 and the desktop starts.
|
||||
|
||||
## What you get
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Window manager | bspwm + sxhkd |
|
||||
| Bar | polybar, floating and rounded; every label opens its panel |
|
||||
| Popups | eww: control centre, audio, network, bluetooth, display, power, media, calendar, notifications |
|
||||
| Menus | rofi: launcher, rice menu, power, toggles, capture, theme and wallpaper pickers, emoji, clipboard, keybindings |
|
||||
| Notifications | dunst, with a notification centre, per-notification dismiss and do-not-disturb |
|
||||
| Compositor | picom 13, one motion language for windows and popups |
|
||||
| Terminal | st (st-flexipatch with sixel, ligatures, scrollback) and kitty |
|
||||
| Prompt | starship, one line: `user@host dir branch ❯` |
|
||||
| Idle | xscreensaver hacks after 5 minutes, xsecurelock after 10, skipped during fullscreen video |
|
||||
| Calendar | khal + vdirsyncer against any CalDAV server; reminders as systemd timers |
|
||||
| Capture | flameshot, gpu-screen-recorder, OCR, QR codes, colour picker, webcam overlay, transcode |
|
||||
|
||||
`super + k` lists every shortcut. The ones to learn first:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `super + Return` | terminal |
|
||||
| `super + w` / `super + m` | app launcher / rice menu |
|
||||
| `super + a` / `super + n` | control centre / notification centre |
|
||||
| `super + ctrl + a / w / b / d / p / m` | audio / network / bluetooth / display / power / media |
|
||||
| `super + ctrl + c` | calendar |
|
||||
| `super + ctrl + r` | new reminder ("20m tea", "15:30 standup") |
|
||||
| `super + c` / `super + v` | copy / paste, terminals included |
|
||||
| `super + ctrl + v` / `super + ctrl + e` | clipboard history / emoji |
|
||||
| `Print` or `super + alt + p` | screenshot a region |
|
||||
| `shift + Print` or `super + alt + shift + p` | screenshot the whole screen |
|
||||
| `ctrl + Print` or `super + alt + t` | copy text from a region |
|
||||
| `alt + Print` or `super + alt + v` | start or stop recording |
|
||||
| `super + shift + Print` or `super + alt + c` | pick a colour |
|
||||
| `super + Print` or `super + alt + s` | capture menu |
|
||||
| `` super + ` `` | drop-down terminal |
|
||||
| `super + Escape` / `super + ctrl + l` | power menu / lock |
|
||||
|
||||
## Theming
|
||||
|
||||
A theme is one file, `themes/<name>/colors.toml`. `rice-theme set <name>`
|
||||
renders every file in `templates/` from it into `~/.local/state/rice/theme/`
|
||||
and reloads the running apps; configs include or link those rendered files.
|
||||
|
||||
- Your own themes go in `~/.config/rice/themes/<name>/`.
|
||||
- `~/.config/rice/theme.override.toml` wins over every theme (`rice-font` writes the font there).
|
||||
- Your own templates go in `~/.config/rice/templates/*.tpl` and win over the built-in ones.
|
||||
|
||||
Placeholders are `{{ key }}`, `{{ key_strip }}` (no `#`) and `{{ key_rgb }}` (`r,g,b`).
|
||||
|
||||
## Your own settings
|
||||
|
||||
These are never in the repo and never overwritten:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `~/.bashrc.local` | personal aliases, exports, SDK paths |
|
||||
| `~/.config/rice/local.sh` | autostarted apps, per-app desktops, monitor tweaks (sourced by bspwmrc) |
|
||||
| `~/.config/rice/idle.conf` | `SCREENSAVER=` and `LOCK=` in seconds |
|
||||
| `~/.config/rice/screensavers` | the xscreensaver hacks to rotate through |
|
||||
| `~/.config/rice/hooks/<event>.d/` | scripts run on `post-boot`, `theme-set`, `font-set`, `wallpaper-set`, `lock`, `unlock`, `update` |
|
||||
| `~/.config/rice/secrets/` | the CalDAV password (mode 600) |
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
install.sh one-shot installer
|
||||
packages/ pacman.txt and aur.txt
|
||||
bin/ rice-* scripts: panels' data and actions, menus, capture, idle, theme
|
||||
home/ files linked into $HOME
|
||||
templates/ *.tpl rendered from a theme's colors.toml
|
||||
themes/ one directory per theme
|
||||
st/ st-flexipatch patch list, config changes and build script
|
||||
particles/ the particle wallpaper layer
|
||||
wallpapers/
|
||||
```
|
||||
3
bin/rice-about
Executable file
3
bin/rice-about
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-about - system details (fastfetch) in a floating terminal. Any key closes it.
|
||||
exec st -c rice-about -e bash -c 'clear; fastfetch; printf "\n \e[2mpress any key\e[0m"; read -rsn1'
|
||||
61
bin/rice-audio
Executable file
61
bin/rice-audio
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-audio - audio state for the eww panel, and the actions it runs.
|
||||
#
|
||||
# rice-audio watch JSON on every change (eww deflisten)
|
||||
# rice-audio sink <name> default output; playing streams move with it
|
||||
# rice-audio source <name> default input
|
||||
# rice-audio app-volume <index> <pct> volume of one playing app
|
||||
# rice-audio app-mute <index>
|
||||
# rice-audio mic <pct> | mic-mute
|
||||
set -uo pipefail
|
||||
|
||||
snapshot() {
|
||||
jq -cn \
|
||||
--arg dsink "$(pactl get-default-sink 2>/dev/null)" \
|
||||
--arg dsrc "$(pactl get-default-source 2>/dev/null)" \
|
||||
--argjson sinks "$(pactl -f json list sinks 2>/dev/null || echo '[]')" \
|
||||
--argjson sources "$(pactl -f json list sources 2>/dev/null || echo '[]')" \
|
||||
--argjson apps "$(pactl -f json list sink-inputs 2>/dev/null || echo '[]')" '
|
||||
def pct: [.volume[]?.value_percent | rtrimstr("%") | tonumber] | if length > 0 then (add / length | round) else 0 end;
|
||||
def level: {name: (.description // ""), vol: pct, muted: (.mute // false)};
|
||||
{
|
||||
sink: (($sinks | map(select(.name == $dsink)) | .[0]) // {} | level),
|
||||
source: (($sources | map(select(.name == $dsrc)) | .[0]) // {} | level),
|
||||
sinks: [ $sinks[] | {index, name, desc: .description, default: (.name == $dsink)} ],
|
||||
sources: [ $sources[] | select(.name | endswith(".monitor") | not)
|
||||
| {index, name, desc: .description, default: (.name == $dsrc)} ],
|
||||
apps: [ $apps[] | {id: .index,
|
||||
name: (.properties["application.name"] // .properties["media.name"] // "app"),
|
||||
vol: pct, muted: .mute} ]
|
||||
}'
|
||||
}
|
||||
|
||||
watch() {
|
||||
snapshot
|
||||
pactl subscribe 2>/dev/null |
|
||||
grep --line-buffered -E "'(change|new|remove)' on (sink|source|sink-input|server)" |
|
||||
while read -r _; do
|
||||
# A slider drag fires dozens of events; wait for the burst to end.
|
||||
while read -r -t 0.08 _; do :; done
|
||||
snapshot
|
||||
done
|
||||
}
|
||||
|
||||
# Panel buttons pass numeric indexes, never device names, so nothing a
|
||||
# device reports about itself ends up inside a shell command.
|
||||
num() { [[ ${1:-} =~ ^[0-9]+$ ]] || { echo "rice-audio: expected a number, got '${1:-}'" >&2; exit 2; }; }
|
||||
|
||||
case "${1:-}" in
|
||||
watch) watch ;;
|
||||
sink)
|
||||
num "${2:-}"
|
||||
pactl set-default-sink "$2"
|
||||
pactl -f json list sink-inputs | jq -r '.[].index' | while read -r i; do pactl move-sink-input "$i" "$2"; done
|
||||
;;
|
||||
source) num "${2:-}"; pactl set-default-source "$2" ;;
|
||||
app-volume) num "${2:-}"; num "${3:-}"; pactl set-sink-input-volume "$2" "$3%" ;;
|
||||
app-mute) num "${2:-}"; pactl set-sink-input-mute "$2" toggle ;;
|
||||
mic) num "${2:-}"; wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SOURCE@ "$2%" ;;
|
||||
mic-mute) rice-volume mic-mute ;;
|
||||
*) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
21
bin/rice-bar
Executable file
21
bin/rice-bar
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-bar - polybar on every connected monitor.
|
||||
#
|
||||
# rice-bar start (re)launch the bar
|
||||
# rice-bar toggle hide or show it without restarting
|
||||
set -uo pipefail
|
||||
|
||||
start() {
|
||||
pkill -u "$UID" -x polybar
|
||||
while pgrep -u "$UID" -x polybar >/dev/null; do sleep 0.1; done
|
||||
local m
|
||||
for m in $(xrandr --query | awk '/ connected/ {print $1}'); do
|
||||
MONITOR="$m" setsid -f polybar --reload top >/dev/null 2>&1
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start) start ;;
|
||||
toggle) if pgrep -u "$UID" -x polybar >/dev/null; then polybar-msg cmd toggle >/dev/null; else start; fi ;;
|
||||
*) echo "usage: rice-bar start|toggle" >&2; exit 2 ;;
|
||||
esac
|
||||
100
bin/rice-bt
Executable file
100
bin/rice-bt
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-bt - bluetooth state for the eww panel, and the actions it runs.
|
||||
#
|
||||
# rice-bt list JSON: power, discovery, paired and nearby devices
|
||||
# rice-bt power on|off|toggle
|
||||
# rice-bt scan look for new devices for 15 seconds
|
||||
# rice-bt toggle <MAC> connect or disconnect; new devices are paired and trusted first
|
||||
# rice-bt forget <MAC>
|
||||
#
|
||||
# Device names are chosen by the device, so the panel only ever passes MAC
|
||||
# addresses, and every MAC is checked before it is used.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
|
||||
say() { rice-notify send "$1" "${2:-}" -i bluetooth; }
|
||||
poke() { date +%s%N > "$run/bt.rev"; }
|
||||
mac_ok() { [[ ${1:-} =~ ^([0-9A-F]{2}:){5}[0-9A-F]{2}$ ]] || { echo "rice-bt: not a MAC address: '${1:-}'" >&2; exit 2; }; }
|
||||
powered() { bluetoothctl show 2>/dev/null | grep -q 'Powered: yes'; }
|
||||
|
||||
glyph() {
|
||||
case "$1" in
|
||||
audio-headset|audio-headphones) echo ;;
|
||||
audio-card) echo ;;
|
||||
input-keyboard) echo ;;
|
||||
input-mouse) echo ;;
|
||||
input-gaming) echo ;;
|
||||
phone) echo ;;
|
||||
computer) echo ;;
|
||||
*) echo ;;
|
||||
esac
|
||||
}
|
||||
|
||||
device_json() {
|
||||
local mac="$1" info name icon paired connected battery
|
||||
info=$(bluetoothctl info "$mac" 2>/dev/null)
|
||||
name=$(sed -n 's/^\s*Alias: //p' <<<"$info" | head -n1)
|
||||
icon=$(sed -n 's/^\s*Icon: //p' <<<"$info" | head -n1)
|
||||
paired=$(grep -q 'Paired: yes' <<<"$info" && echo true || echo false)
|
||||
connected=$(grep -q 'Connected: yes' <<<"$info" && echo true || echo false)
|
||||
battery=$(sed -n 's/^\s*Battery Percentage: .*(\([0-9]*\))/\1/p' <<<"$info" | head -n1)
|
||||
jq -cn --arg mac "$mac" --arg name "${name:-$mac}" --arg glyph "$(glyph "$icon")" \
|
||||
--argjson paired "$paired" --argjson connected "$connected" --arg battery "$battery" \
|
||||
'{mac: $mac, name: $name, glyph: $glyph, paired: $paired, connected: $connected,
|
||||
battery: (if $battery == "" then "" else $battery + "%" end),
|
||||
sub: (if $connected then "connected" elif $paired then "paired" else "tap to pair" end)}'
|
||||
}
|
||||
|
||||
list() {
|
||||
local on=false discovering=false paired="[]" nearby="[]" mac
|
||||
if powered; then
|
||||
on=true
|
||||
bluetoothctl show 2>/dev/null | grep -q 'Discovering: yes' && discovering=true
|
||||
paired=$(bluetoothctl devices Paired 2>/dev/null | awk '{ print $2 }' | grep -E '^([0-9A-F]{2}:){5}[0-9A-F]{2}$' |
|
||||
while read -r mac; do device_json "$mac"; done | jq -cs 'sort_by(.connected | not)')
|
||||
nearby=$(comm -23 <(bluetoothctl devices 2>/dev/null | awk '{ print $2 }' | sort) \
|
||||
<(bluetoothctl devices Paired 2>/dev/null | awk '{ print $2 }' | sort) |
|
||||
grep -E '^([0-9A-F]{2}:){5}[0-9A-F]{2}$' | head -n 8 |
|
||||
while read -r mac; do device_json "$mac"; done | jq -cs '[.[] | select(.name != .mac)]')
|
||||
fi
|
||||
jq -cn --argjson powered "$on" --argjson discovering "$discovering" \
|
||||
--argjson paired "$paired" --argjson nearby "$nearby" \
|
||||
'{powered: $powered, discovering: $discovering, paired: $paired, nearby: $nearby,
|
||||
connected: ([$paired[] | select(.connected)] | length)}'
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
list) list ;;
|
||||
power)
|
||||
case "${2:-toggle}" in
|
||||
on) rfkill unblock bluetooth 2>/dev/null; bluetoothctl power on >/dev/null ;;
|
||||
off) bluetoothctl power off >/dev/null ;;
|
||||
*) if powered; then bluetoothctl power off >/dev/null; else rfkill unblock bluetooth 2>/dev/null; bluetoothctl power on >/dev/null; fi ;;
|
||||
esac
|
||||
poke ;;
|
||||
scan)
|
||||
powered || bluetoothctl power on >/dev/null
|
||||
setsid -f bluetoothctl --timeout 15 scan on >/dev/null 2>&1
|
||||
poke ;;
|
||||
toggle)
|
||||
mac_ok "${2:-}"; mac="$2"
|
||||
info=$(bluetoothctl info "$mac" 2>/dev/null)
|
||||
name=$(sed -n 's/^\s*Alias: //p' <<<"$info" | head -n1)
|
||||
if grep -q 'Connected: yes' <<<"$info"; then
|
||||
bluetoothctl disconnect "$mac" >/dev/null && say "Disconnected" "$name"
|
||||
else
|
||||
if ! grep -q 'Paired: yes' <<<"$info"; then
|
||||
bluetoothctl --timeout 20 pair "$mac" >/dev/null 2>&1 || { say "Pairing failed" "$name"; exit 1; }
|
||||
bluetoothctl trust "$mac" >/dev/null
|
||||
fi
|
||||
bluetoothctl --timeout 15 connect "$mac" >/dev/null 2>&1 && say "Connected" "$name" || say "Could not connect" "$name"
|
||||
fi
|
||||
poke ;;
|
||||
forget)
|
||||
mac_ok "${2:-}"
|
||||
bluetoothctl remove "$2" >/dev/null && say "Forgot device" "$2"
|
||||
poke ;;
|
||||
*) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
219
bin/rice-cal
Executable file
219
bin/rice-cal
Executable file
|
|
@ -0,0 +1,219 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-cal - data and actions for the calendar panel. Events live in khal,
|
||||
# synced with the CalDAV server by vdirsyncer; reminders come from rice-reminder.
|
||||
#
|
||||
# rice-cal month month grid JSON (ISO weeks, Monday first); marks days with events
|
||||
# rice-cal agenda events and reminders for the selected day
|
||||
# rice-cal select <YYYY-MM-DD> pick a day
|
||||
# rice-cal shift <-1|1> previous / next month
|
||||
# rice-cal today
|
||||
# rice-cal add new event on the selected day, typed in rofi:
|
||||
# "15:00 dentist" "15:00-16:30 review" "holiday" (all day)
|
||||
# timed events remind you 10 minutes before; end with !30m or !no
|
||||
# rice-cal sync sync with the server in the background
|
||||
# rice-cal setup connect a CalDAV server (asks for URL, user, password; run in a terminal)
|
||||
set -uo pipefail
|
||||
|
||||
e() { eww -c "$HOME/.config/eww" "$@"; }
|
||||
var() { e get "$1" 2>/dev/null; }
|
||||
have_khal() { command -v khal >/dev/null && [[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/khal/config ]]; }
|
||||
isodate() { [[ ${1:-} =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] && date -d "$1" +%F >/dev/null 2>&1; }
|
||||
|
||||
selected() { local d; d=$(var cal_day); isodate "$d" && echo "$d" || date +%F; }
|
||||
offset() { local o; o=$(var cal_offset); [[ $o =~ ^-?[0-9]+$ ]] && echo "$o" || echo 0; }
|
||||
|
||||
reminders() { rice-reminder json 2>/dev/null || echo '[]'; }
|
||||
|
||||
month() {
|
||||
local off="${1:-$(offset)}" sel="${2:-$(selected)}" first start end counts="{}"
|
||||
first=$(date -d "$(date +%Y-%m-01) $off month" +%F)
|
||||
start=$(date -d "$first -$(( $(date -d "$first" +%u) - 1 )) days" +%F)
|
||||
end=$(date -d "$start +42 days" +%F)
|
||||
|
||||
if have_khal; then
|
||||
counts=$(khal list --once --day-format "" --format "{start-date}" "$start" "$end" 2>/dev/null |
|
||||
grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' | sort | uniq -c |
|
||||
jq -R -s -c 'split("\n") | map(select(length > 0) | ltrimstr(" ") | capture("^ *(?<n>[0-9]+) (?<d>.+)$") | {(.d): (.n | tonumber)}) | add // {}')
|
||||
fi
|
||||
|
||||
for i in $(seq 0 41); do date -d "$start +$i days" '+%F %-d %m %u %V'; done |
|
||||
jq -R -s -c --arg title "$(date -d "$first" '+%B %Y')" --arg today "$(date +%F)" --arg sel "$sel" \
|
||||
--arg month "$(date -d "$first" +%m)" --argjson counts "$counts" --argjson rem "$(reminders)" '
|
||||
($rem | group_by(.date) | map({(.[0].date): length}) | add // {}) as $rc |
|
||||
split("\n") | map(select(length > 0) | split(" ")) as $days |
|
||||
{
|
||||
title: $title,
|
||||
dow: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
|
||||
weeks: [ range(0; 6) as $w | $days[$w * 7:($w + 1) * 7] | {
|
||||
wk: (.[0][4] | tonumber | tostring),
|
||||
days: map({
|
||||
date: .[0], d: .[1],
|
||||
other: (.[2] != $month),
|
||||
weekend: ((.[3] | tonumber) >= 6),
|
||||
today: (.[0] == $today),
|
||||
selected: (.[0] == $sel and .[0] != $today),
|
||||
events: (($counts[.[0]] // 0) + ($rc[.[0]] // 0))
|
||||
})
|
||||
} ]
|
||||
}'
|
||||
}
|
||||
|
||||
agenda() {
|
||||
local day="${1:-$(selected)}" heading items="[]" status="" sep=$'\x1f'
|
||||
heading=$(date -d "$day" '+%A %-d %B')
|
||||
[[ $day == "$(date +%F)" ]] && heading="Today · $heading"
|
||||
|
||||
if have_khal; then
|
||||
items=$(khal list --day-format "" --format "{start-time}${sep}{end-time}${sep}{title}${sep}{location}" "$day" 1d 2>/dev/null |
|
||||
jq -R -s -c --arg sep "$sep" '
|
||||
split("\n") | map(select(length > 0) | split($sep) | {
|
||||
kind: "event", id: "",
|
||||
time: (if .[0] == "" then "all day" else .[0] end),
|
||||
title: .[2],
|
||||
sub: ([(if .[0] != "" and .[1] != "" then "until " + .[1] else empty end),
|
||||
(if (.[3] // "") != "" then .[3] else empty end)] | join(" · "))
|
||||
})')
|
||||
else
|
||||
status="Events need khal set up (run install.sh)"
|
||||
fi
|
||||
|
||||
jq -cn --arg heading "$heading" --arg status "$status" --arg day "$day" \
|
||||
--argjson items "${items:-[]}" --argjson rem "$(reminders)" '
|
||||
{heading: $heading, status: $status,
|
||||
items: ($items + [ $rem[] | select(.date == $day) | {kind: "reminder", id: .id, time: .time, title: .text, sub: "reminder"} ]
|
||||
| sort_by(if .time == "all day" then "00:00" else .time end))}'
|
||||
}
|
||||
|
||||
# Push new data straight into eww so clicks feel instant.
|
||||
refresh() {
|
||||
local off="$1" day="$2"
|
||||
e update cal_offset="$off" cal_day="$day"
|
||||
e update month="$(month "$off" "$day")" agenda="$(agenda "$day")"
|
||||
}
|
||||
|
||||
add() {
|
||||
have_khal || { rice-notify send "Calendar" "Events need khal set up (run install.sh)"; exit 1; }
|
||||
local day input remind=10m start="" end="" title
|
||||
day=$(selected)
|
||||
input=$(rofi -dmenu -theme "$HOME/.config/rofi/prompt.rasi" -p "$(date -d "$day" '+%a %-d %b')" \
|
||||
-mesg "15:00 dentist · 15:00-16:30 review · holiday · end with !30m or !no") || exit 0
|
||||
[[ -n $input ]] || exit 0
|
||||
|
||||
if [[ $input =~ ^(.*[^[:space:]])[[:space:]]+!(no|[0-9]+[mhd])$ ]]; then
|
||||
input="${BASH_REMATCH[1]}"; remind="${BASH_REMATCH[2]}"
|
||||
fi
|
||||
|
||||
if [[ $input =~ ^([0-9]{1,2}:[0-9]{2})(-([0-9]{1,2}:[0-9]{2}))?[[:space:]]+(.+)$ ]]; then
|
||||
start="${BASH_REMATCH[1]}"; end="${BASH_REMATCH[3]}"; title="${BASH_REMATCH[4]}"
|
||||
khal new "$day" "$start" "${end:-1h}" "$title" >/dev/null 2>&1 || { rice-notify send "Calendar" "khal could not add that"; exit 1; }
|
||||
if [[ $remind != no ]]; then
|
||||
local amount="${remind%?}" unit
|
||||
case "${remind: -1}" in m) unit=minutes ;; h) unit=hours ;; d) unit=days ;; esac
|
||||
at=$(date -d "$day $start $amount $unit ago" '+%F %H:%M' 2>/dev/null) &&
|
||||
rice-reminder add "$at" "$title at $start" >/dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
title="$input"
|
||||
khal new "$day" "$title" >/dev/null 2>&1 || { rice-notify send "Calendar" "khal could not add that"; exit 1; }
|
||||
fi
|
||||
|
||||
rice-notify send "Event added" "$title · $(date -d "$day" '+%a %-d %b') ${start:-all day}"
|
||||
setsid -f vdirsyncer sync >/dev/null 2>&1
|
||||
refresh "$(offset)" "$day"
|
||||
}
|
||||
|
||||
setup() {
|
||||
local conf="${XDG_CONFIG_HOME:-$HOME/.config}" share="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
local secrets="$conf/rice/secrets" data="$share/calendars" url user pass first
|
||||
command -v vdirsyncer >/dev/null && command -v khal >/dev/null ||
|
||||
{ echo "rice-cal: install khal and vdirsyncer first"; return 1; }
|
||||
|
||||
read -rp "CalDAV URL [https://dav.srtk.in/dav.php/]: " url </dev/tty
|
||||
url="${url:-https://dav.srtk.in/dav.php/}"
|
||||
[[ $url =~ ^https://[^\"[:space:]]+$ ]] || { echo "rice-cal: use an https:// URL"; return 1; }
|
||||
read -rp "Username: " user </dev/tty
|
||||
[[ $user =~ ^[A-Za-z0-9._@+-]+$ ]] || { echo "rice-cal: that username has characters vdirsyncer can't take"; return 1; }
|
||||
read -rsp "Password: " pass </dev/tty; echo
|
||||
|
||||
# The password stays in a private file; the configs only point at it.
|
||||
mkdir -p "$secrets" && chmod 700 "$secrets"
|
||||
(umask 077; printf '%s' "$pass" > "$secrets/caldav")
|
||||
unset pass
|
||||
mkdir -p "$conf/vdirsyncer" "$conf/khal" "$data" "$share/vdirsyncer/status"
|
||||
|
||||
cat > "$conf/vdirsyncer/config" <<EOF
|
||||
# Written by rice-cal setup. The password is read from $secrets/caldav.
|
||||
[general]
|
||||
status_path = "$share/vdirsyncer/status/"
|
||||
|
||||
[pair calendar]
|
||||
a = "calendar_local"
|
||||
b = "calendar_remote"
|
||||
collections = ["from a", "from b"]
|
||||
metadata = ["displayname", "color"]
|
||||
conflict_resolution = "b wins"
|
||||
|
||||
[storage calendar_local]
|
||||
type = "filesystem"
|
||||
path = "$data/"
|
||||
fileext = ".ics"
|
||||
|
||||
[storage calendar_remote]
|
||||
type = "caldav"
|
||||
url = "$url"
|
||||
username = "$user"
|
||||
password.fetch = ["command", "cat", "$secrets/caldav"]
|
||||
EOF
|
||||
|
||||
echo "Connecting to $url ..."
|
||||
yes | vdirsyncer discover calendar >/dev/null || { echo "rice-cal: could not sign in to $url with those details"; return 1; }
|
||||
vdirsyncer metasync >/dev/null 2>&1
|
||||
vdirsyncer sync || { echo "rice-cal: first sync failed"; return 1; }
|
||||
|
||||
first=$(find "$data" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort | head -n1)
|
||||
[[ -n $first ]] || { first=personal; mkdir -p "$data/$first"; }
|
||||
|
||||
cat > "$conf/khal/config" <<EOF
|
||||
# Written by rice-cal setup.
|
||||
[calendars]
|
||||
[[synced]]
|
||||
path = $data/*
|
||||
type = discover
|
||||
|
||||
[locale]
|
||||
timeformat = %H:%M
|
||||
dateformat = %Y-%m-%d
|
||||
longdateformat = %Y-%m-%d
|
||||
datetimeformat = %Y-%m-%d %H:%M
|
||||
longdatetimeformat = %Y-%m-%d %H:%M
|
||||
firstweekday = 0
|
||||
|
||||
[default]
|
||||
default_calendar = $first
|
||||
highlight_event_days = True
|
||||
EOF
|
||||
|
||||
systemctl --user enable --now vdirsyncer.timer >/dev/null 2>&1
|
||||
echo "Calendar ready: new events go to '$first', and it syncs every 15 minutes."
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
setup) setup ;;
|
||||
month) month ;;
|
||||
agenda) agenda ;;
|
||||
select)
|
||||
isodate "${2:-}" || { echo "rice-cal: select takes YYYY-MM-DD" >&2; exit 2; }
|
||||
now=$(date +%Y*12+%-m | sed 's/^0*//'); then=$(date -d "$2" +%Y*12+%-m)
|
||||
refresh "$(( then - now ))" "$2"
|
||||
;;
|
||||
shift)
|
||||
[[ ${2:-} =~ ^-?[0-9]+$ ]] || { echo "rice-cal: shift takes a number" >&2; exit 2; }
|
||||
off=$(( $(offset) + $2 ))
|
||||
if (( off == 0 )); then day=$(date +%F); else day=$(date -d "$(date +%Y-%m-01) $off month" +%F); fi
|
||||
refresh "$off" "$day"
|
||||
;;
|
||||
today) refresh 0 "$(date +%F)" ;;
|
||||
add) add ;;
|
||||
sync) setsid -f vdirsyncer sync >/dev/null 2>&1 ;;
|
||||
*) sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
212
bin/rice-capture
Executable file
212
bin/rice-capture
Executable file
|
|
@ -0,0 +1,212 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-capture - screenshots, recordings, OCR, QR codes, colours, webcam, transcoding.
|
||||
#
|
||||
# rice-capture region flameshot as usual: drag, annotate, Ctrl+C copies, Ctrl+S saves
|
||||
# rice-capture screen the whole screen, saved and copied
|
||||
# rice-capture text copy the text inside a region (OCR)
|
||||
# rice-capture qr copy what a QR code inside a region says
|
||||
# rice-capture color copy the colour under the cursor
|
||||
# rice-capture record start a recording (asks what to record), or stop the running one
|
||||
# rice-capture webcam webcam overlay on/off, for recordings
|
||||
# rice-capture transcode shrink a recent picture or video for sharing
|
||||
#
|
||||
# Full-screen shots land in Pictures/Screenshots, recordings in Videos/Recordings.
|
||||
# Recordings and transcodes end with a notification: click it to open the file.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
pics="$(xdg-user-dir PICTURES 2>/dev/null || echo "$HOME/Pictures")"; pics="${pics%/}"
|
||||
vids="$(xdg-user-dir VIDEOS 2>/dev/null || echo "$HOME/Videos")"; vids="${vids%/}"
|
||||
shots="$pics/Screenshots"
|
||||
recordings="$vids/Recordings"
|
||||
|
||||
stamp() { date +%Y-%m-%d_%H-%M-%S; }
|
||||
menu() { rofi -dmenu -i -no-custom -format i -theme "$HOME/.config/rofi/menu.rasi" -p "$1"; }
|
||||
|
||||
# The selection rectangle uses the theme's accent.
|
||||
slop_opts() {
|
||||
local hex; hex=$(rice-theme get accent); hex=${hex#\#}
|
||||
awk -v r=$((16#${hex:0:2})) -v g=$((16#${hex:2:2})) -v b=$((16#${hex:4:2})) \
|
||||
'BEGIN { printf "-b 2 -l -c %.2f,%.2f,%.2f,0.18", r / 255, g / 255, b / 255 }'
|
||||
}
|
||||
|
||||
# Select a region into a temp PNG; prints its path, or nothing if cancelled.
|
||||
grab_region() {
|
||||
local img; img=$(mktemp --suffix .png -p "$run")
|
||||
# shellcheck disable=SC2046
|
||||
maim -s -u $(slop_opts) "$img" 2>/dev/null
|
||||
if [[ -s $img ]]; then echo "$img"; else rm -f "$img"; fi
|
||||
}
|
||||
|
||||
# announce <file> <title> [icon] - notification whose click opens the file.
|
||||
announce() {
|
||||
local file="$1" title="$2" icon="${3:-$1}"
|
||||
(
|
||||
action=$(notify-send -a rice -i "$icon" -A default=Open -A folder="Show in folder" -A delete=Delete \
|
||||
"$title" "$(basename "$file") · copied")
|
||||
case "$action" in
|
||||
default) xdg-open "$file" ;;
|
||||
folder) thunar "$(dirname "$file")" ;;
|
||||
delete) rm -f -- "$file" && notify-send -a rice -u low "Deleted" "$(basename "$file")" ;;
|
||||
esac
|
||||
) >/dev/null 2>&1 &
|
||||
}
|
||||
|
||||
# Screenshots reach the clipboard through flameshot, never xclip: flameshot (Qt)
|
||||
# answers paste requests properly, while pasting a PNG held by xclip crashed
|
||||
# Brave and st. Any flag on `flameshot gui` (--raw, --path, --clipboard) also
|
||||
# swaps its Copy/Save/Pin buttons and Ctrl+C for a single accept button.
|
||||
region() {
|
||||
flameshot gui
|
||||
}
|
||||
|
||||
screen() {
|
||||
mkdir -p "$shots"
|
||||
flameshot full --clipboard --path "$shots/screenshot-$(stamp).png"
|
||||
}
|
||||
|
||||
text() {
|
||||
command -v tesseract >/dev/null || { rice-notify send "Text capture" "Install tesseract to read text"; exit 1; }
|
||||
local img out
|
||||
img=$(grab_region); [[ -n $img ]] || exit 0
|
||||
out=$(tesseract "$img" stdout -l "${RICE_OCR_LANGS:-eng}" --psm 6 2>/dev/null | sed -e 's/[[:space:]]*$//' -e '/./,$!d')
|
||||
rm -f "$img"
|
||||
[[ -n $out ]] || { rice-notify send "Text capture" "No text found in that region"; exit 1; }
|
||||
printf '%s' "$out" | xclip -selection clipboard
|
||||
rice-notify send "Text copied" "$(head -c 160 <<<"$out")"
|
||||
}
|
||||
|
||||
qr() {
|
||||
local img out
|
||||
img=$(grab_region); [[ -n $img ]] || exit 0
|
||||
out=$(zbarimg -q --raw -Sdisable -Sqrcode.enable "$img" 2>/dev/null)
|
||||
rm -f "$img"
|
||||
[[ -n $out ]] || { rice-notify send "QR code" "No QR code in that region"; exit 1; }
|
||||
printf '%s' "$out" | xclip -selection clipboard
|
||||
rice-notify send "QR code copied" "It is on the clipboard (and in clipboard history)"
|
||||
}
|
||||
|
||||
color() {
|
||||
local hex swatch="$run/swatch.png"
|
||||
hex=$(xcolor --format hex 2>/dev/null) || exit 0
|
||||
[[ $hex =~ ^#[0-9a-fA-F]{6}$ ]] || exit 0
|
||||
printf '%s' "$hex" | xclip -selection clipboard
|
||||
magick -size 64x64 "xc:$hex" "$swatch" 2>/dev/null
|
||||
notify-send -a rice -u low -i "$swatch" "Colour copied" "$hex"
|
||||
}
|
||||
|
||||
record() {
|
||||
if pgrep -x gpu-screen-reco >/dev/null; then
|
||||
local file thumb="$run/recording.jpg"
|
||||
file=$(cat "$run/recording.path" 2>/dev/null)
|
||||
pkill -INT -x gpu-screen-reco
|
||||
for _ in {1..80}; do pgrep -x gpu-screen-reco >/dev/null || break; sleep 0.1; done
|
||||
rm -f "$run/recording"
|
||||
if [[ -s $file ]]; then
|
||||
ffmpeg -y -loglevel error -ss 1 -i "$file" -frames:v 1 -vf scale=320:-1 "$thumb" 2>/dev/null
|
||||
printf 'file://%s\n' "$file" | xclip -selection clipboard -t text/uri-list
|
||||
announce "$file" "Recording saved" "$thumb"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
local audio target file args
|
||||
audio=$(printf '%s\n' " No audio" " Desktop audio" " Desktop audio and microphone" | menu "Record") || exit 0
|
||||
target=$(printf '%s\n' " Whole screen" " A region (drag it)" " One window (click it)" | menu "Record") || exit 0
|
||||
mkdir -p "$recordings"
|
||||
file="$recordings/recording-$(stamp).mp4"
|
||||
args=(-f 60 -c mp4 -o "$file")
|
||||
case "$audio" in
|
||||
1) args+=(-a default_output) ;;
|
||||
2) args+=(-a "default_output|default_input") ;;
|
||||
esac
|
||||
case "$target" in
|
||||
0)
|
||||
# gpu-screen-recorder 6 records a monitor by name: pick the one under the pointer.
|
||||
local X Y mon
|
||||
eval "$(xdotool getmouselocation --shell 2>/dev/null)"
|
||||
mon=$(xrandr --query | awk -v x="${X:-0}" -v y="${Y:-0}" '
|
||||
/ connected/ { for (i = 3; i <= NF; i++) if ($i ~ /^[0-9]+x[0-9]+\+[0-9]+\+[0-9]+$/) {
|
||||
split($i, g, /[x+]/)
|
||||
if (x >= g[3] && x < g[3] + g[1] && y >= g[4] && y < g[4] + g[2]) { print $1; exit }
|
||||
} }')
|
||||
[[ -n $mon ]] || mon=$(gpu-screen-recorder --list-monitors 2>/dev/null | head -n1 | cut -d'|' -f1)
|
||||
[[ -n $mon ]] || { rice-notify send "Recording" "Found no monitor to record"; exit 1; }
|
||||
args+=(-w "$mon")
|
||||
;;
|
||||
1)
|
||||
local geo
|
||||
# shellcheck disable=SC2046
|
||||
geo=$(slop $(slop_opts) -f '%wx%h+%x+%y' 2>/dev/null) || exit 0
|
||||
args+=(-w region -region "$geo")
|
||||
;;
|
||||
2)
|
||||
local wid
|
||||
wid=$(xdotool selectwindow 2>/dev/null) || exit 0
|
||||
args+=(-w "$wid")
|
||||
;;
|
||||
esac
|
||||
echo "$file" > "$run/recording.path"
|
||||
touch "$run/recording"
|
||||
setsid -f gpu-screen-recorder "${args[@]}" > "$run/recording.log" 2>&1
|
||||
notify-send -a rice -u low -t 1500 "Recording" "Stop with alt + PrtSc or the REC indicator"
|
||||
}
|
||||
|
||||
webcam() {
|
||||
local pid dev
|
||||
if [[ -f $run/webcam.pid ]] && pid=$(cat "$run/webcam.pid") && kill -0 "$pid" 2>/dev/null; then
|
||||
kill "$pid"; rm -f "$run/webcam.pid"; return
|
||||
fi
|
||||
dev=$(ls /dev/video* 2>/dev/null | head -n1)
|
||||
[[ -n $dev ]] || { rice-notify send "Webcam" "No camera found"; exit 1; }
|
||||
mpv --x11-name=rice-webcam --title=rice-webcam --profile=low-latency --untimed --no-osc --no-audio \
|
||||
--no-input-default-bindings --input-conf=/dev/null --no-border --ontop \
|
||||
--geometry=260x260-24-24 --vf=crop=ih:ih "av://v4l2:$dev" >/dev/null 2>&1 &
|
||||
echo $! > "$run/webcam.pid"
|
||||
}
|
||||
|
||||
transcode() {
|
||||
local files choice file fmt out base
|
||||
mapfile -t files < <(find "$pics" "$vids" -maxdepth 2 -type f \
|
||||
\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' -o -iname '*.heic' \
|
||||
-o -iname '*.mp4' -o -iname '*.mov' -o -iname '*.mkv' -o -iname '*.webm' \) \
|
||||
-printf '%T@\t%p\n' 2>/dev/null | sort -rn | head -n 40 | cut -f2-)
|
||||
(( ${#files[@]} > 0 )) || { rice-notify send "Transcode" "No pictures or videos in Pictures or Videos"; exit 1; }
|
||||
choice=$(for f in "${files[@]}"; do basename "$f"; done | menu "Transcode") || exit 0
|
||||
file="${files[$choice]}"
|
||||
base="${file%.*}"
|
||||
|
||||
case "$(file -b --mime-type "$file")" in
|
||||
image/*)
|
||||
fmt=$(printf '%s\n' "jpg · 2160px wide" "jpg · 1080px wide" "png · 1080px wide" | menu "Picture") || exit 0
|
||||
case "$fmt" in
|
||||
0) out="$base-2160px.jpg"; magick "$file" -resize '2160x>' -quality 85 -strip "$out" ;;
|
||||
1) out="$base-1080px.jpg"; magick "$file" -resize '1080x>' -quality 85 -strip "$out" ;;
|
||||
2) out="$base-1080px.png"; magick "$file" -resize '1080x>' -strip "$out" ;;
|
||||
esac
|
||||
;;
|
||||
video/*)
|
||||
fmt=$(printf '%s\n' "mp4 · 1080p" "mp4 · 720p" "gif · 720p" | menu "Video") || exit 0
|
||||
rice-notify send "Transcoding" "$(basename "$file")"
|
||||
case "$fmt" in
|
||||
0) out="$base-1080p.mp4"; ffmpeg -y -loglevel error -i "$file" -vf "scale=-2:'min(1080,ih)'" -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k -movflags +faststart "$out" ;;
|
||||
1) out="$base-720p.mp4"; ffmpeg -y -loglevel error -i "$file" -vf "scale=-2:'min(720,ih)'" -c:v libx264 -crf 24 -preset medium -c:a aac -b:a 128k -movflags +faststart "$out" ;;
|
||||
2) out="$base-720p.gif"; ffmpeg -y -loglevel error -i "$file" -vf "fps=15,scale=-2:'min(720,ih)':flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" "$out" ;;
|
||||
esac
|
||||
;;
|
||||
*) rice-notify send "Transcode" "That is not a picture or a video"; exit 1 ;;
|
||||
esac
|
||||
|
||||
if [[ -s ${out:-} ]]; then
|
||||
printf 'file://%s\n' "$out" | xclip -selection clipboard -t text/uri-list
|
||||
announce "$out" "Transcoded"
|
||||
else
|
||||
rice-notify send "Transcode" "Conversion failed"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
region|screen|text|qr|color|record|webcam|transcode) "$1" ;;
|
||||
*) sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
18
bin/rice-clip
Executable file
18
bin/rice-clip
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-clip copy|paste - one copy/paste shortcut for terminals and GUI apps.
|
||||
# Terminals want Ctrl+Shift+C/V; everything else wants Ctrl+C/V.
|
||||
set -uo pipefail
|
||||
|
||||
class=$(xdotool getactivewindow getwindowclassname 2>/dev/null)
|
||||
case "${class,,}" in
|
||||
st|st-256color|kitty|alacritty|xterm|urxvt|org.wezfurlong.wezterm|com.mitchellh.ghostty|rice-float|rice-scratchpad|rice-about)
|
||||
mods=ctrl+shift ;;
|
||||
*)
|
||||
mods=ctrl ;;
|
||||
esac
|
||||
|
||||
case "${1:-}" in
|
||||
copy) xdotool key --clearmodifiers "$mods+c" ;;
|
||||
paste) xdotool key --clearmodifiers "$mods+v" ;;
|
||||
*) echo "usage: rice-clip copy|paste" >&2; exit 2 ;;
|
||||
esac
|
||||
11
bin/rice-clipboard
Executable file
11
bin/rice-clipboard
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-clipboard - clipboard history (greenclip). Picking an entry puts it back on
|
||||
# the clipboard, ready for super + v. rofi runs in dmenu mode so the header reads
|
||||
# "Clipboard": custom rofi modes ignore a custom title and show their mode name.
|
||||
pgrep -u "$UID" -x greenclip >/dev/null || setsid -f greenclip daemon >/dev/null 2>&1
|
||||
|
||||
choice=$(greenclip print | sed '/^$/d' |
|
||||
rofi -dmenu -i -no-custom -p "Clipboard" -theme "$HOME/.config/rofi/menu.rasi" \
|
||||
-theme-str 'window { width: 640px; }') || exit 0
|
||||
|
||||
[[ -n $choice ]] && greenclip print "$choice"
|
||||
51
bin/rice-dictate
Executable file
51
bin/rice-dictate
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-dictate - offline voice typing into the focused window (nerd-dictation).
|
||||
#
|
||||
# rice-dictate toggle start listening, or stop and finish typing
|
||||
# rice-dictate status on / off
|
||||
#
|
||||
# The first run downloads a small English speech model (about 40 MB) into
|
||||
# ~/.config/nerd-dictation/model. Drop a different VOSK model there to change language.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
model_dir="${XDG_CONFIG_HOME:-$HOME/.config}/nerd-dictation/model"
|
||||
model_url="https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip"
|
||||
mkdir -p "$run"
|
||||
|
||||
listening() { pgrep -u "$UID" -f 'nerd-dictation begin' >/dev/null; }
|
||||
poke() { date +%s%N > "$run/dictation.rev"; }
|
||||
|
||||
fetch_model() {
|
||||
local tmp
|
||||
rice-notify send "Dictation" "Downloading the speech model (about 40 MB)…"
|
||||
tmp=$(mktemp -d) || return 1
|
||||
if curl -fsSL -o "$tmp/model.zip" "$model_url" && unzip -q "$tmp/model.zip" -d "$tmp"; then
|
||||
mkdir -p "$(dirname "$model_dir")"
|
||||
mv "$tmp"/vosk-model-* "$model_dir"
|
||||
rm -rf "$tmp"
|
||||
rice-notify send "Dictation" "Model ready. Press super + ctrl + x again to start."
|
||||
else
|
||||
rm -rf "$tmp"
|
||||
rice-notify send "Dictation" "Could not download the speech model"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-toggle}" in
|
||||
toggle)
|
||||
command -v nerd-dictation >/dev/null || { rice-notify send "Dictation" "Install nerd-dictation-git to dictate"; exit 1; }
|
||||
if listening; then
|
||||
nerd-dictation end
|
||||
poke
|
||||
elif [[ ! -d $model_dir ]]; then
|
||||
fetch_model
|
||||
else
|
||||
setsid -f nerd-dictation begin --simulate-input-tool=XDOTOOL >/dev/null 2>&1
|
||||
poke
|
||||
notify-send -a rice -u low -t 1500 "Dictation" "Listening. super + ctrl + x to stop."
|
||||
fi
|
||||
;;
|
||||
status) listening && echo on || echo off ;;
|
||||
*) sed -n '2,9p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
112
bin/rice-display
Executable file
112
bin/rice-display
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-display - monitor state for the eww panel, and the actions it runs.
|
||||
#
|
||||
# rice-display info JSON: output, modes, refresh rates, DDC brightness
|
||||
# rice-display brightness <0-100>
|
||||
# rice-display mode <id> switch resolution (reverts in 15s unless you keep it)
|
||||
# rice-display rate <id> switch refresh rate at the current resolution
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
modes_tsv="$run/modes.tsv"
|
||||
rates_tsv="$run/rates.tsv"
|
||||
bright_cache="$run/brightness"
|
||||
mkdir -p "$run"
|
||||
|
||||
say() { rice-notify send "$1" "${2:-}" -i video-display; }
|
||||
|
||||
primary() {
|
||||
xrandr --query | awk '/ connected primary/ { print $1; exit } / connected/ && !first { first = $1 } END { if (first) print first }' | head -n1
|
||||
}
|
||||
|
||||
# "WxH<TAB>rate<TAB>current(1/0)" for every mode of the output, xrandr order.
|
||||
mode_lines() {
|
||||
xrandr --query | awk -v out="$1" '
|
||||
$1 == out { inblock = 1; next }
|
||||
inblock && /^[^ ]/ { exit }
|
||||
inblock {
|
||||
mode = $1
|
||||
for (i = 2; i <= NF; i++) {
|
||||
r = $i; cur = (r ~ /\*/) ? 1 : 0; gsub(/[*+]/, "", r)
|
||||
if (r != "") printf "%s\t%s\t%d\n", mode, r, cur
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
# DDC is slow (up to a second per read), so reads are cached for a minute.
|
||||
brightness() {
|
||||
if [[ -f $bright_cache ]] && (( $(date +%s) - $(stat -c %Y "$bright_cache") < 60 )); then
|
||||
cat "$bright_cache"; return
|
||||
fi
|
||||
local v
|
||||
v=$(ddcutil getvcp 10 --brief 2>/dev/null | awk '$1 == "VCP" { print $4; exit }')
|
||||
[[ -n $v ]] && echo "$v" | tee "$bright_cache"
|
||||
}
|
||||
|
||||
info() {
|
||||
local out cur_mode cur_rate
|
||||
out=$(primary)
|
||||
mode_lines "$out" > "$run/all-modes.tsv"
|
||||
IFS=$'\t' read -r cur_mode cur_rate _ < <(awk -F'\t' '$3 == 1' "$run/all-modes.tsv")
|
||||
|
||||
awk -F'\t' '!seen[$1]++ { print $1 }' "$run/all-modes.tsv" | head -n 6 |
|
||||
awk -v cur="$cur_mode" '{ printf "%d\t%s\t%d\n", NR - 1, $1, ($1 == cur) }' > "$modes_tsv"
|
||||
# One entry per rounded rate (monitors list 120.00, 119.98 and 119.88), keeping the active one.
|
||||
awk -F'\t' -v cur="$cur_mode" '$1 == cur { printf "%d\t%s\t%d\n", n++, $2, $3 }' "$run/all-modes.tsv" |
|
||||
sort -t$'\t' -k3,3nr -k2,2nr | awk -F'\t' '!seen[int($2 + 0.5)]++' |
|
||||
sort -t$'\t' -k2,2nr | head -n 5 > "$rates_tsv"
|
||||
|
||||
local b ddc=false
|
||||
if command -v ddcutil >/dev/null; then
|
||||
b=$(brightness)
|
||||
[[ -n $b ]] && ddc=true
|
||||
fi
|
||||
|
||||
jq -cn --arg output "$out" --arg mode "$cur_mode" --arg rate "$cur_rate" \
|
||||
--argjson ddc "$ddc" --argjson brightness "${b:-0}" \
|
||||
--argjson bar "$(pgrep -x polybar >/dev/null && echo true || echo false)" \
|
||||
--argjson modes "$(jq -R -s -c 'split("\n") | map(select(length > 0) | split("\t") | {id: (.[0] | tonumber), mode: .[1], current: (.[2] == "1")})' "$modes_tsv")" \
|
||||
--argjson rates "$(jq -R -s -c 'split("\n") | map(select(length > 0) | split("\t") | {id: (.[0] | tonumber), label: ((.[1] | tonumber | round | tostring) + "Hz"), current: (.[2] == "1")})' "$rates_tsv")" \
|
||||
'{output: $output, mode: $mode, rate: $rate, ddc: $ddc, brightness: $brightness, bar: $bar, modes: $modes, rates: $rates,
|
||||
ddcHint: "Needs ddcutil and the i2c-dev module"}'
|
||||
}
|
||||
|
||||
# Apply an xrandr change, then revert unless it is confirmed within 15 seconds.
|
||||
apply_with_revert() {
|
||||
local out="$1" old_mode="$2" old_rate="$3" new_mode="$4" new_rate="$5"
|
||||
xrandr --output "$out" --mode "$new_mode" --rate "$new_rate" || { say "Display" "That mode did not apply"; return 1; }
|
||||
local keep
|
||||
keep=$(printf 'Keep %s @ %sHz\nRevert\n' "$new_mode" "$new_rate" |
|
||||
timeout 15 rofi -dmenu -theme "$HOME/.config/rofi/menu.rasi" -p "Display" -mesg "Reverting in 15 seconds") || keep=""
|
||||
if [[ $keep != Keep* ]]; then
|
||||
xrandr --output "$out" --mode "$old_mode" --rate "$old_rate"
|
||||
say "Display" "Reverted to $old_mode @ ${old_rate}Hz"
|
||||
fi
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
info) info ;;
|
||||
brightness)
|
||||
[[ ${2:-} =~ ^[0-9]+$ ]] && (( $2 <= 100 )) || { echo "rice-display: brightness 0-100" >&2; exit 2; }
|
||||
echo "$2" > "$bright_cache"
|
||||
# Sliders fire many values; only the newest one talks to the monitor.
|
||||
pkill -u "$UID" -f 'ddcutil setvcp 10' 2>/dev/null
|
||||
setsid -f ddcutil setvcp 10 "$2" --noverify >/dev/null 2>&1
|
||||
;;
|
||||
mode|rate)
|
||||
[[ ${2:-} =~ ^[0-9]+$ ]] || { echo "rice-display: $1 takes an id" >&2; exit 2; }
|
||||
out=$(primary)
|
||||
IFS=$'\t' read -r old_mode old_rate _ < <(mode_lines "$out" | awk -F'\t' '$3 == 1')
|
||||
if [[ $1 == mode ]]; then
|
||||
new_mode=$(awk -F'\t' -v id="$2" '$1 == id { print $2 }' "$modes_tsv")
|
||||
new_rate=$(mode_lines "$out" | awk -F'\t' -v m="$new_mode" '$1 == m { print $2 }' | sort -nr | head -n1)
|
||||
else
|
||||
new_mode="$old_mode"
|
||||
new_rate=$(awk -F'\t' -v id="$2" '$1 == id { print $2 }' "$rates_tsv")
|
||||
fi
|
||||
[[ -n $new_mode && -n $new_rate ]] || exit 1
|
||||
rice-panel close
|
||||
apply_with_revert "$out" "$old_mode" "$old_rate" "$new_mode" "$new_rate"
|
||||
;;
|
||||
*) sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
12
bin/rice-emoji
Executable file
12
bin/rice-emoji
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-emoji - emoji picker that types the emoji into the focused window.
|
||||
theme="$HOME/.config/rofi/menu.rasi"
|
||||
if [[ -f /usr/lib/rofi/emoji.so ]]; then
|
||||
# The "Emoji" header comes from config.rasi (emoji { display-name }).
|
||||
exec rofi -modi emoji -show emoji -emoji-mode insert \
|
||||
-theme "$theme" -theme-str 'window { width: 640px; } listview { columns: 2; lines: 12; }'
|
||||
elif command -v rofimoji >/dev/null; then
|
||||
exec rofimoji --action type --selector-args="-theme $theme -p Emoji"
|
||||
else
|
||||
rice-notify send "Emoji" "Install rofi-emoji to use the picker"
|
||||
fi
|
||||
33
bin/rice-font
Executable file
33
bin/rice-font
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-font [family] - one monospace font for the whole rice: terminals, bar,
|
||||
# menus, popups and notifications. Without an argument, pick from the
|
||||
# installed Nerd Fonts. The choice is kept in ~/.config/rice/theme.override.toml,
|
||||
# so it survives theme changes.
|
||||
set -uo pipefail
|
||||
|
||||
override="${XDG_CONFIG_HOME:-$HOME/.config}/rice/theme.override.toml"
|
||||
current=$(rice-theme get font 2>/dev/null)
|
||||
|
||||
mapfile -t fonts < <(fc-list :spacing=mono family | tr ',' '\n' | grep -i 'nerd font$' | grep -vi 'mono$\|propo$' | sort -u)
|
||||
(( ${#fonts[@]} > 0 )) || { rice-notify send "Font" "No Nerd Fonts installed"; exit 1; }
|
||||
|
||||
if [[ -n ${1:-} ]]; then
|
||||
font="$1"
|
||||
else
|
||||
active=""
|
||||
for i in "${!fonts[@]}"; do [[ ${fonts[$i]} == "$current" ]] && active=$i; done
|
||||
choice=$(printf '%s\n' "${fonts[@]}" |
|
||||
rofi -dmenu -i -no-custom -format i -theme "$HOME/.config/rofi/menu.rasi" -p "Font" ${active:+-a "$active"}) || exit 0
|
||||
font="${fonts[$choice]}"
|
||||
fi
|
||||
|
||||
printf '%s\n' "${fonts[@]}" | grep -Fxq -- "$font" || { rice-notify send "Font" "\"$font\" is not an installed Nerd Font"; exit 1; }
|
||||
|
||||
mkdir -p "$(dirname "$override")"
|
||||
touch "$override"
|
||||
sed -i '/^font *=/d' "$override"
|
||||
printf 'font = "%s"\n' "$font" >> "$override"
|
||||
|
||||
rice-theme reload
|
||||
rice-hook font-set "$font"
|
||||
rice-notify send "Font" "$font · open terminals pick it up when reopened"
|
||||
28
bin/rice-hook
Executable file
28
bin/rice-hook
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-hook <event> [args...] - run your own scripts when something happens.
|
||||
#
|
||||
# Every executable file in ~/.config/rice/hooks/<event>.d/ runs, in name order,
|
||||
# with the event's arguments. Events:
|
||||
# post-boot the desktop has started
|
||||
# theme-set <name> a theme was applied
|
||||
# font-set <family> the font changed
|
||||
# wallpaper-set <image> the wallpaper changed
|
||||
# lock / unlock the screen locked or unlocked
|
||||
# update install.sh --update finished
|
||||
set -uo pipefail
|
||||
|
||||
event="${1:?usage: rice-hook <event> [args...]}"
|
||||
shift
|
||||
[[ $event =~ ^[a-z-]+$ ]] || { echo "rice-hook: bad event name '$event'" >&2; exit 2; }
|
||||
|
||||
dir="${XDG_CONFIG_HOME:-$HOME/.config}/rice/hooks/$event.d"
|
||||
log="${XDG_STATE_HOME:-$HOME/.local/state}/rice/hooks.log"
|
||||
[[ -d $dir ]] || exit 0
|
||||
mkdir -p "$(dirname "$log")"
|
||||
|
||||
for hook in "$dir"/*; do
|
||||
[[ -f $hook && -x $hook && $hook != *.sample ]] || continue
|
||||
if ! "$hook" "$@" >>"$log" 2>&1; then
|
||||
printf '%s %s failed (exit %s)\n' "$(date -Is)" "$hook" "$?" >>"$log"
|
||||
fi
|
||||
done
|
||||
58
bin/rice-idle
Executable file
58
bin/rice-idle
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-idle - screensaver, then lock, when you walk away.
|
||||
#
|
||||
# rice-idle start start the idle chain (rice-session does this)
|
||||
# rice-idle toggle stay awake on/off
|
||||
# rice-idle status JSON for the control centre
|
||||
#
|
||||
# Timings live in ~/.config/rice/idle.conf (seconds):
|
||||
# SCREENSAVER=300 idle time before the screensaver
|
||||
# LOCK=300 further idle time before the lock
|
||||
# Nothing fires while a window is fullscreen or audio is playing. Suspending
|
||||
# always locks first.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
SCREENSAVER=300
|
||||
LOCK=300
|
||||
conf="${XDG_CONFIG_HOME:-$HOME/.config}/rice/idle.conf"
|
||||
[[ -f $conf ]] && . "$conf"
|
||||
|
||||
chain_running() { pgrep -u "$UID" -x xidlehook >/dev/null; }
|
||||
|
||||
start_chain() {
|
||||
xset s off
|
||||
chain_running || setsid -f xidlehook \
|
||||
--not-when-fullscreen --not-when-audio \
|
||||
--timer "$SCREENSAVER" 'rice-screensaver start' 'rice-screensaver stop' \
|
||||
--timer "$LOCK" 'rice-lock' '' >/dev/null 2>&1
|
||||
}
|
||||
|
||||
start_sleep_lock() {
|
||||
pgrep -u "$UID" -x xss-lock >/dev/null || setsid -f xss-lock --transfer-sleep-lock -- rice-lock >/dev/null 2>&1
|
||||
}
|
||||
|
||||
case "${1:-status}" in
|
||||
start)
|
||||
start_sleep_lock
|
||||
[[ -f $run/stay-awake ]] || start_chain
|
||||
;;
|
||||
toggle)
|
||||
if [[ -f $run/stay-awake ]]; then
|
||||
rm -f "$run/stay-awake"
|
||||
start_chain
|
||||
rice-notify send "Stay awake off" "Screensaver after $((SCREENSAVER / 60)) min, lock $((LOCK / 60)) min later"
|
||||
else
|
||||
touch "$run/stay-awake"
|
||||
pkill -u "$UID" -x xidlehook
|
||||
rice-notify send "Stay awake on" "No screensaver or lock until you turn this off"
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
jq -cn --argjson awake "$([[ -f $run/stay-awake ]] && echo true || echo false)" \
|
||||
--argjson screensaver "$SCREENSAVER" --argjson lock "$LOCK" \
|
||||
'{awake: $awake, screensaver: $screensaver, lock: ($screensaver + $lock)}'
|
||||
;;
|
||||
*) sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
27
bin/rice-keys
Executable file
27
bin/rice-keys
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-keys - searchable cheat sheet of every keybinding, read from sxhkdrc.
|
||||
# A "## Section" line groups bindings; a "# text" line right above a hotkey
|
||||
# describes it.
|
||||
set -uo pipefail
|
||||
|
||||
rc="${XDG_CONFIG_HOME:-$HOME/.config}/sxhkd/sxhkdrc"
|
||||
dim=$(rice-theme get dim)
|
||||
accent=$(rice-theme get accent)
|
||||
|
||||
pretty() {
|
||||
sed -E 's/@//g; s/\{_,/{/g; s/\b(super|ctrl|shift|alt)\b/\u\1/g; s/XF86Audio/Media /g; s/Print/PrtSc/g'
|
||||
}
|
||||
escape() { sed 's/&/\&/g; s/</\</g; s/>/\>/g'; }
|
||||
|
||||
awk '
|
||||
/^## / { section = substr($0, 4); next }
|
||||
/^# / { desc = substr($0, 3); next }
|
||||
/^$/ { desc = ""; next }
|
||||
/^[^#[:space:]]/ && desc != "" { printf "%s\t%s\t%s\n", section, $0, desc; desc = "" }
|
||||
' "$rc" |
|
||||
while IFS=$'\t' read -r section keys desc; do
|
||||
printf '<span foreground="%s">%-30s</span> %s <span foreground="%s">· %s</span>\n' \
|
||||
"$accent" "$(pretty <<<"$keys" | escape)" "$(escape <<<"$desc")" "$dim" "$(escape <<<"$section")"
|
||||
done |
|
||||
rofi -dmenu -i -markup-rows -no-custom -theme "$HOME/.config/rofi/menu.rasi" -p "Keybindings" \
|
||||
-theme-str 'window { width: 900px; } listview { lines: 16; }' >/dev/null
|
||||
29
bin/rice-lock
Executable file
29
bin/rice-lock
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-lock - lock the screen with xsecurelock, in the rice's colours, with a
|
||||
# random xscreensaver hack behind the password prompt. Notifications are held
|
||||
# back while locked and shown once you unlock.
|
||||
set -uo pipefail
|
||||
|
||||
pgrep -u "$UID" -x xsecurelock >/dev/null && exit 0
|
||||
|
||||
theme="${XDG_STATE_HOME:-$HOME/.local/state}/rice/theme"
|
||||
[[ -f $theme/xsecurelock.env ]] && . "$theme/xsecurelock.env"
|
||||
|
||||
# xsecurelock's screensaver picks a random hack from a folder. Give it one that
|
||||
# holds only the hacks from ~/.config/rice/screensavers (or rice's defaults).
|
||||
hacks="${XDG_RUNTIME_DIR:-/tmp}/rice/hacks"
|
||||
rm -rf "$hacks" && mkdir -p "$hacks"
|
||||
for h in $(rice-screensaver list); do ln -s "/usr/lib/xscreensaver/$h" "$hacks/$h"; done
|
||||
export XSECURELOCK_XSCREENSAVER_PATH="$hacks"
|
||||
|
||||
rice-screensaver stop >/dev/null 2>&1
|
||||
rice-panel close >/dev/null 2>&1
|
||||
|
||||
level=$(dunstctl get-pause-level 2>/dev/null || echo 0)
|
||||
dunstctl set-pause-level 100 >/dev/null 2>&1
|
||||
|
||||
xsecurelock
|
||||
status=$?
|
||||
|
||||
dunstctl set-pause-level "$level" >/dev/null 2>&1
|
||||
exit "$status"
|
||||
75
bin/rice-media
Executable file
75
bin/rice-media
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-media - now-playing state for the eww panel and bar.
|
||||
#
|
||||
# rice-media watch JSON on every track or play-state change (eww deflisten)
|
||||
# rice-media position JSON {pos, text} for the seek bar (polled while the panel is open)
|
||||
# rice-media seek <seconds>
|
||||
#
|
||||
# Album art is copied into ~/.cache/rice/art under a hash of its URL, so the
|
||||
# panel only ever points at a file name it created itself.
|
||||
set -uo pipefail
|
||||
|
||||
art_dir="${XDG_CACHE_HOME:-$HOME/.cache}/rice/art"
|
||||
mkdir -p "$art_dir"
|
||||
|
||||
clock() { local s=$1; printf '%d:%02d' $((s / 60)) $((s % 60)); }
|
||||
|
||||
art_path() {
|
||||
local url="$1" key file path=""
|
||||
[[ -n $url ]] || return 0
|
||||
key="$url"
|
||||
if [[ $url == file://* ]]; then
|
||||
path="${url#file://}"
|
||||
path=$(printf '%b' "${path//%/\\x}")
|
||||
# Browsers reuse one temp file for every track, so local art is keyed by mtime too.
|
||||
key+=":$(stat -c %Y -- "$path" 2>/dev/null)"
|
||||
fi
|
||||
file="$art_dir/$(printf '%s' "$key" | sha1sum | cut -c1-16)"
|
||||
if [[ ! -s $file ]]; then
|
||||
if [[ -n $path ]]; then
|
||||
cp -- "$path" "$file" 2>/dev/null
|
||||
elif [[ $url == http*://* ]]; then
|
||||
curl -fsSL --max-time 5 -o "$file" -- "$url" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
[[ -s $file ]] && printf '%s' "$file"
|
||||
}
|
||||
|
||||
emit() {
|
||||
local status="$1" player="$2" title="$3" artist="$4" album="$5" url="$6" length="$7"
|
||||
local secs=$(( ${length:-0} / 1000000 ))
|
||||
jq -cn --arg status "$status" --arg player "$player" --arg title "$title" \
|
||||
--arg artist "$artist" --arg album "$album" --arg art "$(art_path "$url")" \
|
||||
--argjson length "$secs" --arg lengthText "$( (( secs > 0 )) && clock "$secs")" \
|
||||
'{status: $status, player: $player, title: $title, artist: $artist, album: $album,
|
||||
art: $art, length: $length, lengthText: $lengthText}'
|
||||
}
|
||||
|
||||
watch() {
|
||||
# Unit separator, not tab: read collapses runs of whitespace separators, so an
|
||||
# empty field (browsers often send no album) would shift every later field.
|
||||
local sep=$'\x1f'
|
||||
while :; do
|
||||
emit "Stopped" "" "" "" "" "" 0
|
||||
playerctl --follow metadata --format \
|
||||
"{{status}}${sep}{{playerName}}${sep}{{title}}${sep}{{artist}}${sep}{{album}}${sep}{{mpris:artUrl}}${sep}{{mpris:length}}" 2>/dev/null |
|
||||
while IFS="$sep" read -r status player title artist album url length; do
|
||||
emit "${status:-Stopped}" "$player" "$title" "$artist" "$album" "$url" "${length:-0}"
|
||||
done
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
watch) watch ;;
|
||||
position)
|
||||
pos=$(playerctl position 2>/dev/null | cut -d. -f1)
|
||||
pos=${pos:-0}
|
||||
jq -cn --argjson pos "$pos" --arg text "$(clock "$pos")" '{pos: $pos, text: $text}'
|
||||
;;
|
||||
seek)
|
||||
[[ ${2:-} =~ ^[0-9]+$ ]] || { echo "rice-media: seek takes seconds" >&2; exit 2; }
|
||||
playerctl position "$2"
|
||||
;;
|
||||
*) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
132
bin/rice-menu
Executable file
132
bin/rice-menu
Executable file
|
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-menu <menu> - every rofi menu in the rice.
|
||||
# main power toggles capture style theme wallpaper screensavers setup
|
||||
set -uo pipefail
|
||||
|
||||
R="$HOME/.config/rofi"
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
RICE_DIR="${RICE_DIR:-$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)}"
|
||||
|
||||
# show <title> <active-rows> <row>... rows are "glyph|label|command";
|
||||
# active rows (comma-separated indexes) get the accent colour, like panel rows.
|
||||
show() {
|
||||
local title="$1" active="$2"; shift 2
|
||||
local choice row cmd
|
||||
choice=$(for row in "$@"; do IFS='|' read -r g l _ <<<"$row"; printf '%s %s\n' "$g" "$l"; done |
|
||||
rofi -dmenu -i -no-custom -format i -theme "$R/menu.rasi" -p "$title" ${active:+-a "$active"}) || exit 0
|
||||
[[ $choice =~ ^[0-9]+$ ]] || exit 0
|
||||
row="${*:$((choice + 1)):1}"
|
||||
cmd="${row#*|}"; cmd="${cmd#*|}"
|
||||
eval "$cmd"
|
||||
}
|
||||
|
||||
# Open a file in the editor in a floating terminal, then run a follow-up.
|
||||
edit() { setsid -f st -c rice-float -e sh -c "${EDITOR:-nvim} \"\$1\"; ${2:-true}" _ "$1" >/dev/null 2>&1; }
|
||||
|
||||
# Image grid: <title> <command> then lines of "label<TAB>image<TAB>value".
|
||||
grid() {
|
||||
local title="$1" action="$2" lines choice value
|
||||
lines=$(cat)
|
||||
[[ -n $lines ]] || { rice-notify send "$title" "Nothing to pick from"; exit 0; }
|
||||
choice=$(while IFS=$'\t' read -r label image _; do printf '%s\0icon\x1f%s\n' "$label" "$image"; done <<<"$lines" |
|
||||
rofi -dmenu -i -no-custom -format i -show-icons -theme "$R/grid.rasi" -p "$title") || exit 0
|
||||
[[ $choice =~ ^[0-9]+$ ]] || exit 0
|
||||
value=$(sed -n "$((choice + 1))p" <<<"$lines" | cut -f3)
|
||||
"$action" "$value"
|
||||
}
|
||||
|
||||
rice-theme-set() { rice-theme set "$1"; }
|
||||
rice-wallpaper-set() { rice-wallpaper set "$1"; }
|
||||
|
||||
case "${1:-main}" in
|
||||
main)
|
||||
show "Menu" "" \
|
||||
"|Apps|rofi -show drun" \
|
||||
"|Control centre|rice-panel hub" \
|
||||
"|Calendar|rice-panel calendar" \
|
||||
"|Notifications|rice-panel notifications" \
|
||||
"|New reminder|rice-reminder new" \
|
||||
"|Capture|exec rice-menu capture" \
|
||||
"|Toggles|exec rice-menu toggles" \
|
||||
"|Style|exec rice-menu style" \
|
||||
"|Emoji|rice-emoji" \
|
||||
"|Clipboard|rice-clipboard" \
|
||||
"|Keybindings|rice-keys" \
|
||||
"|Setup|exec rice-menu setup" \
|
||||
"|Power|exec rice-menu power"
|
||||
;;
|
||||
power)
|
||||
show "Power" "" \
|
||||
"|Lock|rice-lock" \
|
||||
"|Screensaver|rice-screensaver now" \
|
||||
"|Suspend|systemctl suspend" \
|
||||
"|Log out|rice-power logout" \
|
||||
"|Reboot|rice-power reboot" \
|
||||
"|Shut down|rice-power shutdown"
|
||||
;;
|
||||
toggles)
|
||||
active=()
|
||||
[[ $(rice-notify dnd status) == on ]] && active+=(0)
|
||||
[[ -f $run/stay-awake ]] && active+=(1)
|
||||
pgrep -f 'nerd-dictation begin' >/dev/null && active+=(3)
|
||||
pgrep -x particles >/dev/null && active+=(4)
|
||||
[[ -f $run/webcam.pid ]] && active+=(5)
|
||||
show "Toggles" "$(IFS=,; echo "${active[*]}")" \
|
||||
"|Do not disturb|rice-notify dnd toggle" \
|
||||
"|Stay awake|rice-idle toggle" \
|
||||
"|Bar|rice-bar toggle" \
|
||||
"|Dictation|rice-dictate toggle" \
|
||||
"|Particles|setsid -f particles" \
|
||||
"|Webcam overlay|rice-capture webcam" \
|
||||
"|Drop-down terminal|rice-scratchpad"
|
||||
;;
|
||||
capture)
|
||||
rows=("|Screenshot a region|rice-capture region" "|Screenshot the screen|rice-capture screen")
|
||||
if pgrep -x gpu-screen-reco >/dev/null; then rows+=("|Stop recording|rice-capture record")
|
||||
else rows+=("|Record the screen|rice-capture record"); fi
|
||||
rows+=("|Copy text from a region|rice-capture text" \
|
||||
"|Read a QR code|rice-capture qr" \
|
||||
"|Pick a colour|rice-capture color" \
|
||||
"|Webcam overlay|rice-capture webcam" \
|
||||
"|Transcode a picture or video|rice-capture transcode")
|
||||
show "Capture" "" "${rows[@]}"
|
||||
;;
|
||||
style)
|
||||
show "Style" "" \
|
||||
"|Theme|exec rice-menu theme" \
|
||||
"|Wallpaper|exec rice-menu wallpaper" \
|
||||
"|Font|rice-font" \
|
||||
"|Screensavers|exec rice-menu screensavers" \
|
||||
"|About this machine|rice-about"
|
||||
;;
|
||||
theme)
|
||||
rice-theme list | while read -r t; do
|
||||
dir="$RICE_DIR/themes/$t"; [[ -d $dir ]] || dir="${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes/$t"
|
||||
img="$dir/preview.png"
|
||||
[[ -f $img ]] || img="$RICE_DIR/wallpapers/$(sed -n 's/^wallpaper *= *"\(.*\)"/\1/p' "$dir/colors.toml")"
|
||||
printf '%s\t%s\t%s\n' "$(sed -n 's/^name *= *"\(.*\)"/\1/p' "$dir/colors.toml")" "$(rice-wallpaper thumb "$img")" "$t"
|
||||
done | grid "Theme" rice-theme-set
|
||||
;;
|
||||
wallpaper)
|
||||
rice-wallpaper list | while read -r img; do
|
||||
printf '%s\t%s\t%s\n' "$(basename "${img%.*}")" "$(rice-wallpaper thumb "$img")" "$img"
|
||||
done | grid "Wallpaper" rice-wallpaper-set
|
||||
;;
|
||||
screensavers)
|
||||
mapfile -t hacks < <(rice-screensaver list)
|
||||
rows=(); for h in "${hacks[@]}"; do rows+=("|$h|rice-screensaver now $h"); done
|
||||
show "Screensavers" "" "${rows[@]}"
|
||||
;;
|
||||
setup)
|
||||
show "Setup" "" \
|
||||
"|Edit keybindings|edit \"\$HOME/.config/sxhkd/sxhkdrc\" 'pkill -USR1 -x sxhkd'" \
|
||||
"|Edit bspwm|edit \"\$HOME/.config/bspwm/bspwmrc\" 'bspc wm -r'" \
|
||||
"|Idle timings|edit \"\$HOME/.config/rice/idle.conf\" 'pkill -x xidlehook; rice-idle start'" \
|
||||
"|Screensaver list|edit \"\$HOME/.config/rice/screensavers\"" \
|
||||
"|Machine-specific settings|edit \"\$HOME/.config/rice/local.sh\"" \
|
||||
"|Hooks folder|setsid -f thunar \"\$HOME/.config/rice/hooks\"" \
|
||||
"|Restart rice daemons|rice-session restart" \
|
||||
"|Update the rice|setsid -f st -c rice-float -e sh -c 'cd \"\$1\" && git pull --ff-only && ./install.sh --update; printf \"\\npress enter\"; read -r _' _ \"$RICE_DIR\""
|
||||
;;
|
||||
*) sed -n '2,3p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
152
bin/rice-net
Executable file
152
bin/rice-net
Executable file
|
|
@ -0,0 +1,152 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-net - network state for the eww panel, and the actions it runs.
|
||||
#
|
||||
# rice-net list JSON: Wi-Fi radio, active connection, networks, VPNs
|
||||
# rice-net radio on|off|toggle
|
||||
# rice-net connect <id> id from the last list; asks for a password in rofi if needed
|
||||
# rice-net disconnect
|
||||
# rice-net rescan
|
||||
# rice-net vpn <id> bring a VPN / WireGuard connection up or down
|
||||
# rice-net share show the current Wi-Fi network as a QR code
|
||||
#
|
||||
# SSIDs are chosen by whoever broadcasts them, so the panel only ever passes
|
||||
# the numeric id; the SSID is looked up again here from the cached list.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
cache="$run/wifi.tsv"
|
||||
vpncache="$run/vpn.tsv"
|
||||
mkdir -p "$run"
|
||||
|
||||
say() { rice-notify send "$1" "${2:-}" -i network-wireless; }
|
||||
poke() { date +%s%N > "$run/net.rev"; }
|
||||
|
||||
signal_glyph() {
|
||||
local s=$1
|
||||
if (( s >= 75 )); then echo
|
||||
elif (( s >= 50 )); then echo
|
||||
elif (( s >= 25 )); then echo
|
||||
else echo ; fi
|
||||
}
|
||||
|
||||
list() {
|
||||
local radio=false has_wifi=false active_type="" active_name="" active_dev=""
|
||||
nmcli -g TYPE device 2>/dev/null | grep -qx wifi && has_wifi=true
|
||||
[[ $has_wifi == true && $(nmcli -t radio wifi 2>/dev/null) == enabled ]] && radio=true
|
||||
IFS=: read -r active_type active_name active_dev < <(nmcli -g TYPE,STATE,CONNECTION,DEVICE device status 2>/dev/null |
|
||||
awk -F: '$2 == "connected" && ($1 == "wifi" || $1 == "ethernet") { print $1 ":" $3 ":" $4; exit }')
|
||||
|
||||
local known
|
||||
known=$(nmcli -g NAME,TYPE connection show 2>/dev/null | awk -F: '$2 == "802-11-wireless" { print $1 }')
|
||||
|
||||
# IN-USE:SIGNAL:SECURITY:SSID, strongest first, one row per SSID, top 8.
|
||||
: > "$cache"
|
||||
local inuse signal security ssid id=0 seen=$'\n'
|
||||
while IFS=: read -r inuse signal security ssid; do
|
||||
ssid=${ssid//\\:/:}
|
||||
[[ -z $ssid || $seen == *$'\n'"$ssid"$'\n'* ]] && continue
|
||||
seen+="$ssid"$'\n'
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$id" "$ssid" "$signal" "${security:---}" \
|
||||
"$([[ $inuse == '*' ]] && echo 1 || echo 0)" \
|
||||
"$(grep -Fxq -- "$ssid" <<<"$known" && echo 1 || echo 0)" >> "$cache"
|
||||
(( ++id >= 8 )) && break
|
||||
done < <(nmcli -g IN-USE,SIGNAL,SECURITY,SSID device wifi list --rescan no 2>/dev/null | sort -t: -k2,2nr)
|
||||
|
||||
# VPN and WireGuard profiles known to NetworkManager.
|
||||
: > "$vpncache"
|
||||
local active_names name type vid=0
|
||||
active_names=$(nmcli -g NAME connection show --active 2>/dev/null)
|
||||
while IFS=: read -r name type; do
|
||||
[[ $type == vpn || $type == wireguard ]] || continue
|
||||
printf '%s\t%s\t%s\n' "$vid" "$name" "$(grep -Fxq -- "$name" <<<"$active_names" && echo 1 || echo 0)" >> "$vpncache"
|
||||
(( vid++ ))
|
||||
done < <(nmcli -g NAME,TYPE connection show 2>/dev/null)
|
||||
|
||||
jq -cn --argjson radio "$radio" --argjson haswifi "$has_wifi" \
|
||||
--arg atype "$active_type" --arg aname "$active_name" \
|
||||
--argjson wifi "$(while IFS=$'\t' read -r i s sig sec use kn; do
|
||||
jq -cn --argjson id "$i" --arg ssid "$s" --argjson signal "$sig" --arg sec "$sec" \
|
||||
--argjson active "$([[ $use == 1 ]] && echo true || echo false)" \
|
||||
--argjson known "$([[ $kn == 1 ]] && echo true || echo false)" \
|
||||
--arg glyph "$(signal_glyph "$sig")" \
|
||||
'{id: $id, ssid: $ssid, signal: $signal, active: $active, known: $known, glyph: $glyph,
|
||||
sub: ([(if $active then "connected" elif $known then "saved" else empty end),
|
||||
(if $sec == "--" then "open" else " " + $sec end)] | join(" · "))}'
|
||||
done < "$cache" | jq -cs '.')" \
|
||||
--argjson vpn "$(while IFS=$'\t' read -r i n a; do
|
||||
jq -cn --argjson id "$i" --arg name "$n" --argjson active "$([[ $a == 1 ]] && echo true || echo false)" \
|
||||
'{id: $id, name: $name, active: $active}'
|
||||
done < "$vpncache" | jq -cs '.')" \
|
||||
'{radio: $radio,
|
||||
active: {type: $atype, name: $aname},
|
||||
hasWifi: $haswifi,
|
||||
wifi: $wifi, vpn: $vpn}'
|
||||
}
|
||||
|
||||
field() { awk -F'\t' -v id="$1" -v col="$2" '$1 == id { print $col; exit }' "$3"; }
|
||||
|
||||
connect() {
|
||||
[[ ${1:-} =~ ^[0-9]+$ ]] || { echo "rice-net: connect takes an id" >&2; exit 2; }
|
||||
local ssid security known pass
|
||||
ssid=$(field "$1" 2 "$cache"); security=$(field "$1" 4 "$cache"); known=$(field "$1" 6 "$cache")
|
||||
[[ -n $ssid ]] || { say "Wi-Fi" "That network is gone; rescan and try again"; exit 1; }
|
||||
|
||||
if [[ $known == 1 ]]; then
|
||||
nmcli connection up id "$ssid" >/dev/null 2>&1 && say "Connected" "$ssid" || say "Could not connect" "$ssid"
|
||||
elif [[ $security == "--" ]]; then
|
||||
nmcli device wifi connect "$ssid" >/dev/null 2>&1 && say "Connected" "$ssid" || say "Could not connect" "$ssid"
|
||||
elif [[ $security == *802.1X* ]]; then
|
||||
say "Enterprise network" "Opening the connection editor for $ssid"
|
||||
setsid -f nm-connection-editor >/dev/null 2>&1
|
||||
else
|
||||
pass=$(rofi -dmenu -password -theme "$HOME/.config/rofi/prompt.rasi" -p "$ssid" -mesg "Wi-Fi password") || exit 0
|
||||
[[ -n $pass ]] || exit 0
|
||||
local mgmt=wpa-psk
|
||||
[[ $security == *WPA3* && $security != *WPA2* ]] && mgmt=sae
|
||||
# The password reaches nmcli through a file descriptor, not the command line.
|
||||
nmcli connection add type wifi con-name "$ssid" ssid "$ssid" wifi-sec.key-mgmt "$mgmt" >/dev/null 2>&1
|
||||
if nmcli connection up id "$ssid" passwd-file <(printf '802-11-wireless-security.psk:%s\n' "$pass") >/dev/null 2>&1; then
|
||||
say "Connected" "$ssid"
|
||||
else
|
||||
nmcli connection delete id "$ssid" >/dev/null 2>&1
|
||||
say "Could not connect" "Wrong password for $ssid?"
|
||||
fi
|
||||
fi
|
||||
poke
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
list) list ;;
|
||||
radio)
|
||||
case "${2:-toggle}" in
|
||||
on) nmcli radio wifi on ;;
|
||||
off) nmcli radio wifi off ;;
|
||||
*) [[ $(nmcli -t radio wifi) == enabled ]] && nmcli radio wifi off || nmcli radio wifi on ;;
|
||||
esac
|
||||
poke ;;
|
||||
connect) connect "${2:-}" ;;
|
||||
disconnect)
|
||||
dev=$(nmcli -g TYPE,DEVICE,STATE device status | awk -F: '$1 == "wifi" && $3 == "connected" { print $2; exit }')
|
||||
[[ -n $dev ]] && nmcli device disconnect "$dev" >/dev/null && say "Wi-Fi" "Disconnected"
|
||||
poke ;;
|
||||
rescan) nmcli device wifi rescan >/dev/null 2>&1; poke ;;
|
||||
vpn)
|
||||
[[ ${2:-} =~ ^[0-9]+$ ]] || { echo "rice-net: vpn takes an id" >&2; exit 2; }
|
||||
name=$(field "$2" 2 "$vpncache"); active=$(field "$2" 3 "$vpncache")
|
||||
[[ -n $name ]] || exit 1
|
||||
if [[ $active == 1 ]]; then nmcli connection down id "$name" >/dev/null && say "VPN off" "$name"
|
||||
else nmcli connection up id "$name" >/dev/null && say "VPN on" "$name" || say "VPN failed" "$name"; fi
|
||||
poke ;;
|
||||
share)
|
||||
name=$(nmcli -g TYPE,STATE,CONNECTION device status | awk -F: '$1 == "wifi" && $2 == "connected" { print $3; exit }')
|
||||
[[ -n $name ]] || { say "Wi-Fi" "Not connected to a Wi-Fi network"; exit 1; }
|
||||
ssid=$(nmcli -g 802-11-wireless.ssid connection show id "$name")
|
||||
psk=$(nmcli -s -g 802-11-wireless-security.psk connection show id "$name")
|
||||
esc() { sed -e 's/[\\;,:"]/\\&/g' <<<"$1"; }
|
||||
png="$run/wifi-qr.png"
|
||||
printf 'WIFI:T:%s;S:%s;P:%s;;' "$([[ -n $psk ]] && echo WPA || echo nopass)" "$(esc "$ssid")" "$(esc "$psk")" |
|
||||
qrencode -o "$png" -s 10 -m 2 --foreground="$(rice-theme get bg | tr -d '#')" --background="$(rice-theme get fg | tr -d '#')"
|
||||
setsid -f feh --title "Wi-Fi: $ssid" --geometry 420x420 -Z "$png" >/dev/null 2>&1
|
||||
;;
|
||||
*) sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
95
bin/rice-notify
Executable file
95
bin/rice-notify
Executable file
|
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-notify - one front door for notifications.
|
||||
#
|
||||
# rice-notify send <title> [body] [-i icon] [-u low|normal|critical]
|
||||
# rice-notify dismiss | dismiss-all | action | restore
|
||||
# rice-notify dnd on|off|toggle|status
|
||||
# rice-notify remove <id> | clear
|
||||
# rice-notify watch JSON stream for the notification centre (eww)
|
||||
#
|
||||
# Do-not-disturb is dunst pause level 60, not "paused": rules in dunstrc give
|
||||
# rice's own confirmations and critical CLI alerts level 100 so they still show.
|
||||
set -uo pipefail
|
||||
|
||||
DND_LEVEL=60
|
||||
rev="${XDG_RUNTIME_DIR:-/tmp}/rice/notify.rev"
|
||||
mkdir -p "${rev%/*}"
|
||||
|
||||
poke() {
|
||||
date +%s%N > "$rev"
|
||||
pgrep -x polybar >/dev/null && polybar-msg action indicators hook 0 >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
dnd_on() { (( $(dunstctl get-pause-level 2>/dev/null || echo 0) > 0 )); }
|
||||
|
||||
history_json() {
|
||||
local uptime
|
||||
uptime=$(cut -d' ' -f1 /proc/uptime)
|
||||
dunstctl history 2>/dev/null | jq -c --argjson up "$uptime" '
|
||||
[ .data[0][]? | {
|
||||
id: .id.data,
|
||||
app: .appname.data,
|
||||
summary: .summary.data,
|
||||
body: (.body.data | gsub("<[^>]*>"; "") | split("\n")
|
||||
| map(gsub("^\\s+|\\s+$"; "") | select(length > 0)) | .[0:2] | join(" · ")
|
||||
| if length > 110 then .[0:109] + "…" else . end),
|
||||
icon: (.icon_path.data // ""),
|
||||
urgency: (.urgency.data | tostring | ascii_upcase),
|
||||
age: (($up - (.timestamp.data / 1000000)) | floor)
|
||||
}
|
||||
| .ago = (if .age < 60 then "now"
|
||||
elif .age < 3600 then "\(.age / 60 | floor)m"
|
||||
elif .age < 86400 then "\(.age / 3600 | floor)h"
|
||||
else "\(.age / 86400 | floor)d" end)
|
||||
]' || echo '[]'
|
||||
}
|
||||
|
||||
snapshot() {
|
||||
local items
|
||||
items=$(history_json)
|
||||
jq -cn --argjson items "$items" \
|
||||
--argjson dnd "$(dnd_on && echo true || echo false)" \
|
||||
--argjson waiting "$(dunstctl count waiting 2>/dev/null || echo 0)" \
|
||||
'{count: ($items | length), dnd: $dnd, waiting: $waiting, items: $items[:8]}'
|
||||
}
|
||||
|
||||
cmd="${1:-}"; shift || true
|
||||
case "$cmd" in
|
||||
send)
|
||||
title="${1:-}"; body="${2:-}"; shift 2 2>/dev/null || shift $#
|
||||
notify-send -a rice "$@" "$title" "$body"
|
||||
;;
|
||||
dismiss) dunstctl close; poke ;;
|
||||
dismiss-all) dunstctl close-all; poke ;;
|
||||
action) dunstctl action 0; poke ;;
|
||||
restore) dunstctl history-pop; poke ;;
|
||||
remove) [[ ${1:-} =~ ^[0-9]+$ ]] && dunstctl history-rm "$1"; poke ;;
|
||||
open) [[ ${1:-} =~ ^[0-9]+$ ]] && dunstctl history-pop "$1"; poke ;;
|
||||
clear) dunstctl history-clear; poke ;;
|
||||
dnd)
|
||||
case "${1:-toggle}" in
|
||||
on) dunstctl set-pause-level "$DND_LEVEL" ;;
|
||||
off) dunstctl set-pause-level 0 ;;
|
||||
toggle) if dnd_on; then dunstctl set-pause-level 0; else dunstctl set-pause-level "$DND_LEVEL"; fi ;;
|
||||
status) dnd_on && echo on || echo off; exit 0 ;;
|
||||
esac
|
||||
if dnd_on; then
|
||||
notify-send -a rice -u low -h string:x-dunst-stack-tag:dnd "Do not disturb" "Only alarms and critical alerts will show"
|
||||
else
|
||||
notify-send -a rice -u low -h string:x-dunst-stack-tag:dnd "Do not disturb off" "$(dunstctl count waiting) held back"
|
||||
fi
|
||||
poke
|
||||
;;
|
||||
watch)
|
||||
[[ -f $rev ]] || poke
|
||||
while :; do
|
||||
snapshot
|
||||
inotifywait -qq -t 5 -e close_write "$rev" 2>/dev/null
|
||||
done
|
||||
;;
|
||||
*)
|
||||
sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'
|
||||
[[ -z $cmd ]] || exit 2
|
||||
;;
|
||||
esac
|
||||
6
bin/rice-notify-hook
Executable file
6
bin/rice-notify-hook
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
# Run by dunst for every notification ([notify_centre] in dunstrc). It only
|
||||
# marks "something changed" so the notification centre and bar refresh.
|
||||
rev="${XDG_RUNTIME_DIR:-/tmp}/rice/notify.rev"
|
||||
mkdir -p "${rev%/*}"
|
||||
date +%s%N > "$rev"
|
||||
14
bin/rice-osd
Executable file
14
bin/rice-osd
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-osd <glyph> <label> <percent|text>
|
||||
# An on-screen display that replaces itself in place (dunst [osd] rule).
|
||||
# A number draws dunst's progress bar; anything else is shown as text.
|
||||
glyph="${1:?usage: rice-osd <glyph> <label> <percent|text>}"
|
||||
label="${2:-}"
|
||||
value="${3:-}"
|
||||
tag="osd-${label,,}"
|
||||
|
||||
if [[ $value =~ ^[0-9]+$ ]]; then
|
||||
notify-send -a osd -u low -h "int:value:$value" -h "string:x-dunst-stack-tag:$tag" "$glyph $label $value%"
|
||||
else
|
||||
notify-send -a osd -u low -h "string:x-dunst-stack-tag:$tag" "$glyph $label $value"
|
||||
fi
|
||||
64
bin/rice-panel
Executable file
64
bin/rice-panel
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-panel <name> | close | list
|
||||
#
|
||||
# Opens one eww popup at a time (opening one closes the other). A transparent
|
||||
# full-screen "closer" window sits underneath, so a click anywhere else closes
|
||||
# it, and a throwaway sxhkd grabs Escape for as long as a panel is open.
|
||||
set -uo pipefail
|
||||
|
||||
cfg="$HOME/.config/eww"
|
||||
panels="hub audio network bluetooth display power media calendar notifications"
|
||||
|
||||
e() { eww -c "$cfg" "$@"; }
|
||||
|
||||
ensure_daemon() {
|
||||
pgrep -u "$UID" -x eww >/dev/null && return 0
|
||||
setsid -f eww -c "$cfg" daemon >/dev/null 2>&1
|
||||
for _ in {1..50}; do e ping >/dev/null 2>&1 && return 0; sleep 0.1; done
|
||||
}
|
||||
|
||||
open_windows() { e active-windows 2>/dev/null | cut -d: -f1; }
|
||||
|
||||
# Line panels up with tiled windows. bspwm draws the border inside the gap, so a
|
||||
# window's outer edge is exactly one gap below the bar and in from the screen's
|
||||
# right edge; ask it for the live numbers instead of hard-coding them.
|
||||
position() {
|
||||
local pt pr gap
|
||||
read -r pt pr gap < <(bspc query -T -m 2>/dev/null |
|
||||
jq -r '[.padding.top, .padding.right, .windowGap] | @tsv' 2>/dev/null)
|
||||
pt=${pt:-37} pr=${pr:-0} gap=${gap:-10}
|
||||
case "$1" in
|
||||
media|calendar) echo "0x$(( pt + gap ))" ;;
|
||||
*) echo "$(( -(pr + gap) ))x$(( pt + gap ))" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
close_all() {
|
||||
local w
|
||||
for w in $(open_windows); do
|
||||
[[ " $panels closer " == *" $w "* ]] && e close "$w" >/dev/null 2>&1
|
||||
done
|
||||
pkill -u "$UID" -f "sxhkd -c $cfg/panel-keys" 2>/dev/null
|
||||
pgrep -u "$UID" -x eww >/dev/null && e update open_panel="" >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
case "${1:-close}" in
|
||||
close) close_all ;;
|
||||
list) tr ' ' '\n' <<<"$panels" ;;
|
||||
*)
|
||||
name="$1"
|
||||
[[ " $panels " == *" $name "* ]] || { echo "rice-panel: unknown panel '$name' (try: rice-panel list)" >&2; exit 2; }
|
||||
ensure_daemon
|
||||
if open_windows | grep -qx "$name"; then
|
||||
close_all
|
||||
else
|
||||
close_all
|
||||
[[ $name == calendar ]] && e update cal_offset=0 cal_day=""
|
||||
e update open_panel="$name"
|
||||
e open closer
|
||||
e open "$name" --pos="$(position "$name")"
|
||||
setsid -f sxhkd -c "$cfg/panel-keys" >/dev/null 2>&1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
79
bin/rice-power
Executable file
79
bin/rice-power
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-power - system stats and power actions for the eww panel and power menu.
|
||||
#
|
||||
# rice-power info JSON: CPU/GPU load and temperature, memory, uptime, profile
|
||||
# rice-power profile <name> power-saver | balanced | performance
|
||||
# rice-power lock|suspend|logout|reboot|shutdown
|
||||
# logout, reboot and shutdown ask first
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
|
||||
# CPU load since the previous call (the panel polls every few seconds).
|
||||
cpu_load() {
|
||||
local now prev idle total pidle ptotal
|
||||
read -r _ user nice system idle iowait irq softirq steal _ < /proc/stat
|
||||
total=$((user + nice + system + idle + iowait + irq + softirq + steal))
|
||||
idle=$((idle + iowait))
|
||||
if [[ -f $run/cpu.prev ]]; then
|
||||
read -r pidle ptotal < "$run/cpu.prev"
|
||||
(( total > ptotal )) && echo $(( 100 * ((total - ptotal) - (idle - pidle)) / (total - ptotal) )) || echo 0
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
echo "$idle $total" > "$run/cpu.prev"
|
||||
}
|
||||
|
||||
info() {
|
||||
local sensors gpu_dev profile profiles
|
||||
sensors=$(sensors -j 2>/dev/null || echo '{}')
|
||||
gpu_dev=$(ls -d /sys/class/drm/card*/device 2>/dev/null | while read -r d; do [[ -f $d/gpu_busy_percent ]] && echo "$d" && break; done)
|
||||
profile=$(powerprofilesctl get 2>/dev/null || echo "")
|
||||
profiles=$(powerprofilesctl list 2>/dev/null | sed -n 's/^[* ]*\([a-z-]*\):$/\1/p' | jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||
|
||||
jq -cn \
|
||||
--argjson sensors "$sensors" \
|
||||
--argjson cpu "$(cpu_load)" \
|
||||
--arg gpubusy "$( [[ -n $gpu_dev ]] && cat "$gpu_dev/gpu_busy_percent" 2>/dev/null)" \
|
||||
--arg vramused "$( [[ -n $gpu_dev ]] && cat "$gpu_dev/mem_info_vram_used" 2>/dev/null)" \
|
||||
--arg vramtotal "$( [[ -n $gpu_dev ]] && cat "$gpu_dev/mem_info_vram_total" 2>/dev/null)" \
|
||||
--arg mem "$(free -b | awk '/^Mem:/ { print $3 " " $2 }')" \
|
||||
--arg uptime "$(uptime -p | sed 's/^up //; s/ hours\?/h/; s/ minutes\?/m/; s/ days\?/d/; s/,//g')" \
|
||||
--arg profile "$profile" --argjson profiles "${profiles:-[]}" '
|
||||
def temp($pat): [$sensors | to_entries[] | select(.key | test($pat)) | .value | to_entries[]
|
||||
| select(.key | test("^(Tctl|Tdie|edge|Package id 0|temp1)$")) | .value
|
||||
| to_entries[] | select(.key | endswith("_input")) | .value] | first // null;
|
||||
def gib: (. / 1073741824 * 10 | round / 10 | tostring) + "G";
|
||||
($mem | split(" ") | map(tonumber)) as [$mused, $mtotal] |
|
||||
{
|
||||
cpu: {load: $cpu, temp: (temp("k10temp|coretemp|zenpower") | if . then round else null end)},
|
||||
gpu: {load: ($gpubusy | tonumber? // null),
|
||||
temp: (temp("amdgpu|radeon|nouveau") | if . then round else null end),
|
||||
vram: (if $vramtotal != "" then (($vramused | tonumber | gib) + " / " + ($vramtotal | tonumber | gib)) else "" end)},
|
||||
memory: {used: ($mused | gib), total: ($mtotal | gib), percent: ($mused * 100 / $mtotal | round)},
|
||||
uptime: $uptime,
|
||||
profile: $profile,
|
||||
profiles: [ $profiles[] | {name: ., label: ({"power-saver": "saver", "balanced": "balanced", "performance": "performance"}[.] // .), active: (. == $profile)} ]
|
||||
}'
|
||||
}
|
||||
|
||||
confirm() {
|
||||
local answer
|
||||
answer=$(printf '%s\nCancel\n' "$1" | rofi -dmenu -theme "$HOME/.config/rofi/menu.rasi" -p "$2" -mesg "Are you sure?") || return 1
|
||||
[[ $answer == "$1" ]]
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
info) info ;;
|
||||
profile)
|
||||
[[ ${2:-} =~ ^(power-saver|balanced|performance)$ ]] || { echo "rice-power: unknown profile '${2:-}'" >&2; exit 2; }
|
||||
powerprofilesctl set "$2" && rice-notify send "Power profile" "$2" -i power-profile-"$2"-symbolic
|
||||
;;
|
||||
lock) rice-panel close; rice-lock ;;
|
||||
suspend) rice-panel close; systemctl suspend ;;
|
||||
logout) rice-panel close; confirm " Log out" "Log out" && bspc quit ;;
|
||||
reboot) rice-panel close; confirm " Reboot" "Reboot" && systemctl reboot ;;
|
||||
shutdown) rice-panel close; confirm " Shut down" "Shut down" && systemctl poweroff ;;
|
||||
*) sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
42
bin/rice-preview
Executable file
42
bin/rice-preview
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-preview <file> - show a file in the terminal. Pictures and video frames
|
||||
# are drawn as real images (kitty's protocol in kitty, sixel in st), folders as
|
||||
# a tree, and everything else through bat. Used by `ff` (fzf) and `img`.
|
||||
set -uo pipefail
|
||||
|
||||
file="${1:?usage: rice-preview <file>}"
|
||||
[[ -e $file ]] || exit 0
|
||||
|
||||
cols="${FZF_PREVIEW_COLUMNS:-$(tput cols)}"
|
||||
lines="${FZF_PREVIEW_LINES:-$(( $(tput lines) - 2 ))}"
|
||||
cache="${XDG_CACHE_HOME:-$HOME/.cache}/rice/preview"
|
||||
|
||||
picture() {
|
||||
if [[ -n ${KITTY_WINDOW_ID:-} ]]; then
|
||||
kitty +kitten icat --clear --transfer-mode=memory --stdin=no --place="${cols}x${lines}@0x0" -- "$1"
|
||||
elif [[ $TERM == st* ]]; then
|
||||
chafa --format sixel --size "${cols}x${lines}" -- "$1"
|
||||
else
|
||||
chafa --format symbols --size "${cols}x${lines}" -- "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -d $file ]]; then
|
||||
eza --tree --level=2 --icons=always --color=always -- "$file" 2>/dev/null || ls -la -- "$file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$(file -b --mime-type -- "$file")" in
|
||||
image/*)
|
||||
picture "$file"
|
||||
;;
|
||||
video/*)
|
||||
mkdir -p "$cache"
|
||||
frame="$cache/$(printf '%s:%s' "$(realpath -- "$file")" "$(stat -c %Y -- "$file")" | sha1sum | cut -c1-16).jpg"
|
||||
[[ -s $frame ]] || ffmpeg -loglevel error -y -ss 3 -i "$file" -frames:v 1 -vf scale=640:-1 "$frame" 2>/dev/null
|
||||
[[ -s $frame ]] && picture "$frame"
|
||||
;;
|
||||
*)
|
||||
bat --style=numbers --color=always --line-range :300 -- "$file" 2>/dev/null || head -n 300 -- "$file"
|
||||
;;
|
||||
esac
|
||||
107
bin/rice-reminder
Executable file
107
bin/rice-reminder
Executable file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-reminder - desktop reminders backed by systemd user timers.
|
||||
#
|
||||
# rice-reminder new ask in rofi
|
||||
# rice-reminder add <when> <text> when: 20m | 1h30m | 15:30 | "tomorrow 9:00" | "2026-09-15 09:00"
|
||||
# rice-reminder list upcoming reminders as a notification
|
||||
# rice-reminder json upcoming reminders as JSON (calendar panel)
|
||||
# rice-reminder cancel <id> | clear
|
||||
#
|
||||
# In rofi, type "20m tea", "15:30 standup" or "tomorrow 9:00 | dentist".
|
||||
# Reminders use app name "rice", so they still show during do-not-disturb.
|
||||
set -uo pipefail
|
||||
|
||||
self="$(readlink -f "$0")"
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
poke() { date +%s%N > "$run/reminder.rev"; }
|
||||
say() { notify-send -a rice -u low -i alarm-symbolic "$@"; }
|
||||
|
||||
# Resolve a "when" into epoch seconds, or fail.
|
||||
resolve() {
|
||||
local when="$1" now t
|
||||
now=$(date +%s)
|
||||
if [[ $when =~ ^([0-9]+[smhd])+$ ]]; then
|
||||
local secs=0 part
|
||||
for part in $(grep -oE '[0-9]+[smhd]' <<<"$when"); do
|
||||
case "${part: -1}" in
|
||||
s) secs=$((secs + ${part%?})) ;;
|
||||
m) secs=$((secs + ${part%?} * 60)) ;;
|
||||
h) secs=$((secs + ${part%?} * 3600)) ;;
|
||||
d) secs=$((secs + ${part%?} * 86400)) ;;
|
||||
esac
|
||||
done
|
||||
echo $((now + secs)); return
|
||||
fi
|
||||
if [[ $when =~ ^[0-9]{1,2}:[0-9]{2}$ ]]; then
|
||||
t=$(date -d "today $when" +%s 2>/dev/null) || return 1
|
||||
(( t <= now )) && t=$(date -d "tomorrow $when" +%s)
|
||||
echo "$t"; return
|
||||
fi
|
||||
t=$(date -d "$when" +%s 2>/dev/null) || return 1
|
||||
(( t > now )) || return 1
|
||||
echo "$t"
|
||||
}
|
||||
|
||||
add() {
|
||||
local when="$1" text="${2:-Reminder}" at unit
|
||||
at=$(resolve "$when") || { say "Reminder" "Couldn't read \"$when\" as a time"; return 1; }
|
||||
unit="rice-reminder-$(date +%s%N)"
|
||||
systemd-run --user --quiet --collect --unit="$unit" --description="$text" \
|
||||
--on-calendar="$(date -d "@$at" '+%Y-%m-%d %H:%M:%S')" \
|
||||
--timer-property=AccuracySec=1s \
|
||||
-- "$self" fire "$text" || { say "Reminder" "Could not schedule it"; return 1; }
|
||||
say "Reminder set" "$text · $(date -d "@$at" '+%a %H:%M')"
|
||||
poke
|
||||
}
|
||||
|
||||
upcoming() {
|
||||
local unit next
|
||||
systemctl --user list-timers 'rice-reminder-*' --all -o json 2>/dev/null |
|
||||
jq -r '.[] | [.unit, (.next / 1000000 | floor)] | @tsv' |
|
||||
while IFS=$'\t' read -r unit next; do
|
||||
(( next > 0 )) || continue
|
||||
printf '%s\t%s\t%s\n' "${unit%.timer}" "$next" "$(systemctl --user show -p Description --value "$unit")"
|
||||
done | sort -t$'\t' -k2,2n
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
new)
|
||||
input=$(rofi -dmenu -theme "$HOME/.config/rofi/prompt.rasi" -p "Reminder" \
|
||||
-mesg "20m tea · 15:30 standup · tomorrow 9:00 | dentist") || exit 0
|
||||
[[ -n $input ]] || exit 0
|
||||
if [[ $input == *"|"* ]]; then
|
||||
when="${input%%|*}"; text="${input#*|}"
|
||||
else
|
||||
when="${input%% *}"; text="${input#"$when"}"
|
||||
fi
|
||||
when="$(sed 's/^ *//; s/ *$//' <<<"$when")"; text="$(sed 's/^ *//; s/ *$//' <<<"$text")"
|
||||
add "$when" "${text:-Reminder}"
|
||||
;;
|
||||
add) add "${2:?when}" "${3:-Reminder}" ;;
|
||||
fire)
|
||||
notify-send -a rice -u critical -i alarm-symbolic "Reminder" "${2:-Reminder}"
|
||||
command -v paplay >/dev/null && [[ -f /usr/share/sounds/freedesktop/stereo/complete.oga ]] &&
|
||||
paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null
|
||||
poke
|
||||
;;
|
||||
list)
|
||||
body=$(upcoming | while IFS=$'\t' read -r _ next text; do printf '%s %s\n' "$(date -d "@$next" '+%a %H:%M')" "$text"; done)
|
||||
say "Upcoming reminders" "${body:-Nothing scheduled}"
|
||||
;;
|
||||
json)
|
||||
upcoming | jq -R -s -c 'split("\n") | map(select(length > 0) | split("\t")
|
||||
| {id: .[0], at: (.[1] | tonumber), time: (.[1] | tonumber | strflocaltime("%H:%M")),
|
||||
date: (.[1] | tonumber | strflocaltime("%Y-%m-%d")), text: .[2]})'
|
||||
;;
|
||||
cancel)
|
||||
[[ ${2:-} =~ ^rice-reminder-[0-9]+$ ]] || { echo "rice-reminder: cancel takes a reminder id" >&2; exit 2; }
|
||||
systemctl --user stop "$2.timer" && poke
|
||||
;;
|
||||
clear)
|
||||
systemctl --user list-timers 'rice-reminder-*' --all --no-legend 2>/dev/null | awk '{ for (i = 1; i <= NF; i++) if ($i ~ /\.timer$/) print $i }' |
|
||||
xargs -r systemctl --user stop
|
||||
say "Reminders" "All cleared"; poke
|
||||
;;
|
||||
*) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
6
bin/rice-scratchpad
Executable file
6
bin/rice-scratchpad
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-scratchpad - drop-down terminal on super + `. It is a persistent tmux
|
||||
# session, so whatever runs in it survives being hidden or closed.
|
||||
command -v tdrop >/dev/null || { rice-notify send "Drop-down terminal" "Install tdrop to use it"; exit 1; }
|
||||
exec tdrop -a -w 70% -h 45% -x 15% -y 45 --class rice-scratchpad \
|
||||
st -c rice-scratchpad -e tmux new-session -A -s scratchpad
|
||||
53
bin/rice-screensaver
Executable file
53
bin/rice-screensaver
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-screensaver - classic xscreensaver hacks, full screen.
|
||||
#
|
||||
# rice-screensaver start [hack] random hack from the list (rice-idle runs this)
|
||||
# rice-screensaver stop
|
||||
# rice-screensaver now [hack] start by hand; press q or Escape in it to stop
|
||||
# rice-screensaver list hacks available on this machine
|
||||
#
|
||||
# Pick your own set by listing hack names, one per line, in
|
||||
# ~/.config/rice/screensavers.
|
||||
set -uo pipefail
|
||||
|
||||
hackdir=/usr/lib/xscreensaver
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
pidfile="$run/screensaver.pid"
|
||||
mkdir -p "$run"
|
||||
|
||||
defaults="glmatrix flurry pipes fireworkx galaxy atlantis lament gears starfish xmatrix boxfit"
|
||||
user_list="${XDG_CONFIG_HOME:-$HOME/.config}/rice/screensavers"
|
||||
|
||||
available() {
|
||||
local names h
|
||||
if [[ -f $user_list ]]; then names=$(grep -v '^\s*#' "$user_list"); else names=$defaults; fi
|
||||
for h in $names; do [[ -x $hackdir/$h ]] && echo "$h"; done
|
||||
}
|
||||
|
||||
stop() {
|
||||
[[ -f $pidfile ]] && kill "$(cat "$pidfile")" 2>/dev/null
|
||||
rm -f "$pidfile"
|
||||
}
|
||||
|
||||
start() {
|
||||
local hack="${1:-}" pid wid
|
||||
stop
|
||||
if [[ -z $hack ]]; then hack=$(available | shuf -n1); fi
|
||||
[[ -n $hack && -x $hackdir/$hack ]] || { echo "rice-screensaver: no hack available (install xscreensaver)" >&2; exit 1; }
|
||||
[[ $hack =~ ^[a-z0-9_-]+$ ]] || exit 2
|
||||
|
||||
"$hackdir/$hack" >/dev/null 2>&1 &
|
||||
pid=$!
|
||||
echo "$pid" > "$pidfile"
|
||||
# Hacks open an ordinary window; hand it to bspwm as a fullscreen node.
|
||||
wid=$(xdotool search --sync --onlyvisible --pid "$pid" 2>/dev/null | head -n1)
|
||||
[[ -n $wid ]] && bspc node "$wid" -t fullscreen -f 2>/dev/null
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
start) start "${2:-}" ;;
|
||||
now) start "${2:-}" ;;
|
||||
stop) stop ;;
|
||||
list) available ;;
|
||||
*) sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
81
bin/rice-session
Executable file
81
bin/rice-session
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-session - start everything the desktop needs, exactly once.
|
||||
#
|
||||
# rice-session start called by bspwmrc (safe to call again on bspwm restart)
|
||||
# rice-session restart stop and start the rice daemons
|
||||
#
|
||||
# Output goes to ~/.local/state/rice/session.log.
|
||||
set -u
|
||||
|
||||
state="${XDG_STATE_HOME:-$HOME/.local/state}/rice"
|
||||
theme="$state/theme"
|
||||
mkdir -p "$state"
|
||||
exec >>"$state/session.log" 2>&1
|
||||
|
||||
running() { pgrep -u "$UID" -x "$1" >/dev/null; }
|
||||
|
||||
# start <process-name> <command...>: launch detached unless already running
|
||||
launch() {
|
||||
local name="$1"; shift
|
||||
if running "$name"; then return 0; fi
|
||||
command -v "$1" >/dev/null || { echo "missing: $1"; return 0; }
|
||||
setsid -f "$@" >/dev/null 2>&1 && echo "started $name"
|
||||
}
|
||||
|
||||
daemons=(xsettingsd picom sxhkd dunst greenclip flameshot eww polybar xidlehook xss-lock)
|
||||
|
||||
start() {
|
||||
echo "== $(date -Is) session start"
|
||||
systemctl --user import-environment DISPLAY XAUTHORITY XDG_CURRENT_DESKTOP PATH QT_QPA_PLATFORMTHEME GTK_THEME
|
||||
dbus-update-activation-environment --systemd DISPLAY XAUTHORITY XDG_CURRENT_DESKTOP
|
||||
|
||||
[[ -d $theme ]] || rice-theme render
|
||||
[[ -f $theme/Xresources ]] && xrdb -merge "$theme/Xresources"
|
||||
|
||||
# Daemons left over from another setup keep their old config files, so a
|
||||
# bspwm restart alone would never switch them. Replace those.
|
||||
local pid args
|
||||
for pid in $(pgrep -u "$UID" -x sxhkd); do
|
||||
args=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)
|
||||
if [[ $args == *" -c "* && $args != *panel-keys* ]]; then kill "$pid" && echo "replaced: $args"; fi
|
||||
done
|
||||
for pid in $(pgrep -u "$UID" -x picom); do
|
||||
args=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)
|
||||
if [[ $args != *"$HOME/.config/picom/picom.conf"* ]]; then kill "$pid" && echo "replaced: $args"; fi
|
||||
done
|
||||
for _ in {1..20}; do
|
||||
pgrep -u "$UID" -f 'sxhkd -c .*bspwm|picom --config .*bspwm' >/dev/null || break
|
||||
sleep 0.1
|
||||
done
|
||||
pgrep -u "$UID" -x dunst >/dev/null && dunstctl reload
|
||||
|
||||
launch xsettingsd xsettingsd -c "$theme/xsettingsd.conf"
|
||||
launch picom picom --config "$HOME/.config/picom/picom.conf"
|
||||
launch sxhkd sxhkd
|
||||
launch dunst dunst
|
||||
launch greenclip greenclip daemon
|
||||
launch flameshot flameshot
|
||||
launch eww eww daemon
|
||||
|
||||
if ! pgrep -u "$UID" -f polkit-gnome-authentication-agent-1 >/dev/null; then
|
||||
setsid -f /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
rice-wallpaper restore
|
||||
rice-bar start
|
||||
rice-idle start
|
||||
|
||||
(sleep 2 && rice-hook post-boot) &
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo "== $(date -Is) session stop"
|
||||
for d in "${daemons[@]}"; do pkill -u "$UID" -x "$d"; done
|
||||
}
|
||||
|
||||
case "${1:-start}" in
|
||||
start) start ;;
|
||||
stop) stop ;;
|
||||
restart) stop; sleep 0.5; start ;;
|
||||
*) echo "usage: rice-session start|stop|restart" >&2; exit 2 ;;
|
||||
esac
|
||||
102
bin/rice-status
Executable file
102
bin/rice-status
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-status <module> - live text for polybar's rice modules (tail = true).
|
||||
# Modules: net bt vol notif indicators media. Each prints a line whenever its
|
||||
# state changes, in the same "LABEL value" style as the cpu/ram/gpu modules.
|
||||
set -uo pipefail
|
||||
|
||||
run="${XDG_RUNTIME_DIR:-/tmp}/rice"
|
||||
mkdir -p "$run"
|
||||
|
||||
accent=$(rice-theme get accent) dim=$(rice-theme get dim)
|
||||
urgent=$(rice-theme get urgent) green=$(rice-theme get green)
|
||||
yellow=$(rice-theme get yellow) magenta=$(rice-theme get magenta)
|
||||
|
||||
# label <colour> <LABEL> <value>
|
||||
label() { printf '%%{F%s}%s%%{F-} %s\n' "$1" "$2" "$3"; }
|
||||
short() { local s="$1" n="$2"; (( ${#s} > n )) && s="${s:0:n-1}…"; printf '%s' "$s"; }
|
||||
# wait for anything under $run to change, or give up after $1 seconds
|
||||
settle() { inotifywait -qq -t "$1" -e close_write,create,delete "$run" 2>/dev/null; return 0; }
|
||||
|
||||
net() {
|
||||
show() {
|
||||
local type conn
|
||||
IFS=: read -r type conn < <(nmcli -t -f TYPE,STATE,CONNECTION device status 2>/dev/null |
|
||||
awk -F: '$2 == "connected" && ($1 == "wifi" || $1 == "ethernet") { print $1 ":" $3; exit }')
|
||||
case "${type:-}" in
|
||||
wifi) label "$accent" NET "$(short "$conn" 16)" ;;
|
||||
ethernet) label "$accent" NET eth ;;
|
||||
*) label "$urgent" NET off ;;
|
||||
esac
|
||||
}
|
||||
show
|
||||
nmcli monitor 2>/dev/null | while read -r _; do show; done
|
||||
}
|
||||
|
||||
bt() {
|
||||
while :; do
|
||||
if ! bluetoothctl show 2>/dev/null | grep -q 'Powered: yes'; then
|
||||
label "$dim" BT off
|
||||
else
|
||||
local dev
|
||||
dev=$(bluetoothctl devices Connected 2>/dev/null | head -n1 | cut -d' ' -f3-)
|
||||
label "$accent" BT "$(short "${dev:-on}" 14)"
|
||||
fi
|
||||
settle 10
|
||||
done
|
||||
}
|
||||
|
||||
vol() {
|
||||
show() {
|
||||
local v m
|
||||
read -r _ v m < <(wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null)
|
||||
if [[ -n ${m:-} ]]; then label "$urgent" VOL muted
|
||||
else label "$green" VOL "$(awk -v v="${v:-0}" 'BEGIN { printf "%d%%", v * 100 + 0.5 }')"; fi
|
||||
}
|
||||
show
|
||||
pactl subscribe 2>/dev/null | grep --line-buffered -E "'change' on (sink|server)" | while read -r _; do show; done
|
||||
}
|
||||
|
||||
notif() {
|
||||
while :; do
|
||||
local n level
|
||||
n=$(dunstctl count history 2>/dev/null || echo 0)
|
||||
level=$(dunstctl get-pause-level 2>/dev/null || echo 0)
|
||||
if (( level > 0 )); then printf '%%{F%s}%%{F-}\n' "$yellow"
|
||||
elif (( n > 0 )); then printf '%%{F%s}%%{F-} %s\n' "$accent" "$n"
|
||||
else printf '%%{F%s}%%{F-}\n' "$dim"; fi
|
||||
settle 5
|
||||
done
|
||||
}
|
||||
|
||||
indicators() {
|
||||
while :; do
|
||||
local out="" r
|
||||
[[ -f $run/stay-awake ]] && out+="%{A1:rice-idle toggle:}%{F$yellow}%{F-}%{A} "
|
||||
pgrep -x gpu-screen-reco >/dev/null && out+="%{A1:rice-capture record:}%{F$urgent} REC%{F-}%{A} "
|
||||
pgrep -f 'nerd-dictation begin' >/dev/null && out+="%{A1:rice-dictate toggle:}%{F$magenta}%{F-}%{A} "
|
||||
r=$(systemctl --user list-timers 'rice-reminder-*' --no-legend 2>/dev/null | grep -c .)
|
||||
(( r > 0 )) && out+="%{A1:rice-reminder list:}%{F$accent} $r%{F-}%{A} "
|
||||
printf '%s\n' "${out% }"
|
||||
settle 5
|
||||
done
|
||||
}
|
||||
|
||||
media() {
|
||||
while :; do
|
||||
echo ""
|
||||
playerctl --follow metadata --format '{{status}}|{{artist}}|{{title}}' 2>/dev/null |
|
||||
while IFS='|' read -r status artist title; do
|
||||
case "$status" in
|
||||
Playing) printf '%%{F%s}%%{F-} %s\n' "$magenta" "$(short "${artist:+$artist - }$title" 40)" ;;
|
||||
Paused) printf '%%{F%s} %s%%{F-}\n' "$dim" "$(short "$title" 30)" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
done
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
net|bt|vol|notif|indicators|media) "$1" ;;
|
||||
*) echo "usage: rice-status net|bt|vol|notif|indicators|media" >&2; exit 2 ;;
|
||||
esac
|
||||
162
bin/rice-sysinfo
Executable file
162
bin/rice-sysinfo
Executable file
|
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Default values
|
||||
MODE=""
|
||||
NORMAL_COLOR=""
|
||||
CRITICAL_COLOR=""
|
||||
CRITICAL_THRESHOLD=80
|
||||
CLICK_ACTION=false
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--cpu)
|
||||
MODE="cpu"
|
||||
shift
|
||||
;;
|
||||
--ram)
|
||||
MODE="ram"
|
||||
shift
|
||||
;;
|
||||
--gpu)
|
||||
MODE="gpu"
|
||||
shift
|
||||
;;
|
||||
--normal-color)
|
||||
NORMAL_COLOR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--critical-color)
|
||||
CRITICAL_COLOR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--click)
|
||||
CLICK_ACTION=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Function to get CPU usage
|
||||
get_cpu_usage() {
|
||||
# Using top to get CPU usage
|
||||
local cpu_usage=$(top -bn2 -d 0.5 | grep "Cpu(s)" | tail -n1 | awk '{print $2}' | cut -d'%' -f1)
|
||||
|
||||
# If top doesn't work, try mpstat
|
||||
if [ -z "$cpu_usage" ]; then
|
||||
if command -v mpstat >/dev/null 2>&1; then
|
||||
cpu_usage=$(mpstat 1 1 | awk '/Average:/ {print 100 - $NF}')
|
||||
fi
|
||||
fi
|
||||
|
||||
# Round to integer
|
||||
printf "%.0f" "$cpu_usage"
|
||||
}
|
||||
|
||||
# Function to get RAM usage
|
||||
get_ram_usage() {
|
||||
local mem_info=$(free | grep Mem)
|
||||
local total=$(echo "$mem_info" | awk '{print $2}')
|
||||
local used=$(echo "$mem_info" | awk '{print $3}')
|
||||
local percentage=$(awk "BEGIN {printf \"%.0f\", ($used/$total)*100}")
|
||||
echo "$percentage"
|
||||
}
|
||||
|
||||
# Function to get GPU usage (NVIDIA)
|
||||
get_gpu_usage() {
|
||||
# amdgpu (and i915 on newer kernels) expose load directly; reading it is
|
||||
# free, unlike radeontop, which spawns a sampling process on every tick.
|
||||
local busy
|
||||
busy=$(cat /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | head -n1)
|
||||
if [[ -n $busy ]]; then
|
||||
echo "$busy"
|
||||
elif command -v nvidia-smi >/dev/null 2>&1; then
|
||||
local gpu_usage=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits | head -n1)
|
||||
echo "$gpu_usage"
|
||||
elif command -v intel_gpu_top >/dev/null 2>&1; then
|
||||
# For Intel GPUs - approximate method
|
||||
echo "0"
|
||||
else
|
||||
echo "N/A"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to format output with color
|
||||
format_output() {
|
||||
local value="$1"
|
||||
local normal_color="$2"
|
||||
local critical_color="$3"
|
||||
|
||||
# Check if value is a number
|
||||
if [[ "$value" =~ ^[0-9]+$ ]]; then
|
||||
if [ "$value" -ge "$CRITICAL_THRESHOLD" ] && [ -n "$critical_color" ]; then
|
||||
echo "%{F${critical_color}}${value}%%{F-}"
|
||||
elif [ -n "$normal_color" ]; then
|
||||
echo "%{F${normal_color}}${value}%%{F-}"
|
||||
else
|
||||
echo "${value}%"
|
||||
fi
|
||||
else
|
||||
# Not a number (like N/A)
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
# Handle click actions
|
||||
handle_click() {
|
||||
case $MODE in
|
||||
cpu)
|
||||
# Open system monitor or htop
|
||||
if command -v htop >/dev/null 2>&1; then
|
||||
alacritty -e htop &
|
||||
elif command -v gnome-system-monitor >/dev/null 2>&1; then
|
||||
gnome-system-monitor &
|
||||
elif command -v xfce4-taskmanager >/dev/null 2>&1; then
|
||||
xfce4-taskmanager &
|
||||
else
|
||||
notify-send "System Info" "No system monitor found"
|
||||
fi
|
||||
;;
|
||||
gpu)
|
||||
# Open nvidia-smi or GPU monitor
|
||||
if command -v nvidia-smi >/dev/null 2>&1; then
|
||||
alacritty -e watch -n 1 nvidia-smi &
|
||||
else
|
||||
notify-send "System Info" "No GPU monitor available"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Main logic
|
||||
main() {
|
||||
if [ "$CLICK_ACTION" = true ]; then
|
||||
handle_click
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case $MODE in
|
||||
cpu)
|
||||
usage=$(get_cpu_usage)
|
||||
format_output "$usage" "$NORMAL_COLOR" "$CRITICAL_COLOR"
|
||||
;;
|
||||
ram)
|
||||
usage=$(get_ram_usage)
|
||||
format_output "$usage" "$NORMAL_COLOR" "$CRITICAL_COLOR"
|
||||
;;
|
||||
gpu)
|
||||
usage=$(get_gpu_usage)
|
||||
format_output "$usage" "$NORMAL_COLOR" "$CRITICAL_COLOR"
|
||||
;;
|
||||
*)
|
||||
echo "Error: Specify --cpu, --ram, or --gpu"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main
|
||||
170
bin/rice-theme
Executable file
170
bin/rice-theme
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-theme - render and apply a colour theme to every surface of the rice.
|
||||
#
|
||||
# rice-theme set <name> render themes/<name> and reload everything
|
||||
# rice-theme reload re-render the current theme (after editing a template)
|
||||
# rice-theme list available themes
|
||||
# rice-theme current name of the active theme
|
||||
# rice-theme get <key> one value from the active colors.toml (for scripts)
|
||||
#
|
||||
# Templates live in $RICE_DIR/templates/*.tpl and ~/.config/rice/templates/*.tpl
|
||||
# (yours win). Placeholders, for a key like accent = "#92b9ea":
|
||||
# {{ accent }} #92b9ea
|
||||
# {{ accent_strip }} 92b9ea
|
||||
# {{ accent_rgb }} 146,185,234
|
||||
# A theme can ship a finished file (e.g. themes/foo/btop.theme); it is used
|
||||
# as-is instead of the template with the same output name.
|
||||
set -euo pipefail
|
||||
|
||||
RICE_DIR="${RICE_DIR:-$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)}"
|
||||
STATE="${XDG_STATE_HOME:-$HOME/.local/state}/rice"
|
||||
CURRENT="$STATE/theme"
|
||||
USER_TEMPLATES="${XDG_CONFIG_HOME:-$HOME/.config}/rice/templates"
|
||||
HOOKS="${XDG_CONFIG_HOME:-$HOME/.config}/rice/hooks/theme-set.d"
|
||||
OVERRIDE="${XDG_CONFIG_HOME:-$HOME/.config}/rice/theme.override.toml"
|
||||
|
||||
die() { printf 'rice-theme: %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
theme_dir() {
|
||||
local user="${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes/$1"
|
||||
if [[ -d $user ]]; then echo "$user"
|
||||
elif [[ -d $RICE_DIR/themes/$1 ]]; then echo "$RICE_DIR/themes/$1"
|
||||
else return 1; fi
|
||||
}
|
||||
|
||||
# Emit "key<TAB>value" for every key = value line of a colors.toml.
|
||||
# Quoted values are taken whole (a colour's # is not a comment); bare values
|
||||
# stop at a trailing # comment.
|
||||
parse_colors() {
|
||||
sed -n -E \
|
||||
-e 's/^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]*=[[:space:]]*"([^"]*)".*$/\1\t\2/p' \
|
||||
-e 't' \
|
||||
-e 's/^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]*=[[:space:]]*([^"#[:space:]]([^#]*[^#[:space:]])?)[[:space:]]*(#.*)?$/\1\t\2/p' \
|
||||
"$1"
|
||||
}
|
||||
|
||||
# Build a sed program that replaces every placeholder form for every key.
|
||||
sed_program() {
|
||||
local key value esc r g b
|
||||
while IFS=$'\t' read -r key value; do
|
||||
esc=$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g')
|
||||
printf 's|{{ *%s *}}|%s|g\n' "$key" "$esc"
|
||||
if [[ $value =~ ^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$ ]]; then
|
||||
r=$((16#${BASH_REMATCH[1]})); g=$((16#${BASH_REMATCH[2]})); b=$((16#${BASH_REMATCH[3]}))
|
||||
printf 's|{{ *%s_strip *}}|%s|g\n' "$key" "${value#\#}"
|
||||
printf 's|{{ *%s_rgb *}}|%s,%s,%s|g\n' "$key" "$r" "$g" "$b"
|
||||
fi
|
||||
done < <(parse_colors "$1" | tac | awk -F'\t' '!seen[$1]++') # a later line wins (overrides)
|
||||
}
|
||||
|
||||
render() {
|
||||
local name="$1" src out next prog tpl base
|
||||
src=$(theme_dir "$name") || die "no theme called '$name' (try: rice-theme list)"
|
||||
[[ -f $src/colors.toml ]] || die "$src has no colors.toml"
|
||||
|
||||
mkdir -p "$STATE"
|
||||
next=$(mktemp -d "$STATE/.theme.XXXXXX")
|
||||
cp -a "$src/." "$next/"
|
||||
# Your overrides (the font from rice-font, or any colour) apply on top of every theme.
|
||||
if [[ -f $OVERRIDE ]]; then
|
||||
printf '\n# --- from %s ---\n' "$OVERRIDE" >> "$next/colors.toml"
|
||||
cat "$OVERRIDE" >> "$next/colors.toml"
|
||||
fi
|
||||
prog="$next/.render.sed"
|
||||
sed_program "$next/colors.toml" > "$prog"
|
||||
|
||||
# User templates first, so a same-named built-in is skipped.
|
||||
for tpl in "$USER_TEMPLATES"/*.tpl "$RICE_DIR"/templates/*.tpl; do
|
||||
[[ -f $tpl ]] || continue
|
||||
base=$(basename "$tpl" .tpl)
|
||||
out="$next/$base"
|
||||
[[ -e $out ]] && continue
|
||||
sed -f "$prog" "$tpl" > "$out"
|
||||
if grep -q '{{' "$out"; then
|
||||
printf 'rice-theme: %s has unknown placeholders: %s\n' "$base" \
|
||||
"$(grep -o '{{[^}]*}}' "$out" | sort -u | tr '\n' ' ')" >&2
|
||||
fi
|
||||
done
|
||||
rm -f "$prog"
|
||||
|
||||
rm -rf "$CURRENT.old"
|
||||
[[ -d $CURRENT ]] && mv "$CURRENT" "$CURRENT.old"
|
||||
mv "$next" "$CURRENT"
|
||||
rm -rf "$CURRENT.old"
|
||||
printf '%s\n' "$name" > "$STATE/theme.name"
|
||||
}
|
||||
|
||||
# signal_if_caught <SIGNAL> <process-name>: send the signal only to processes
|
||||
# whose SigCgt mask says they install a handler for it.
|
||||
signal_if_caught() {
|
||||
local sig="$1" name="$2" num pid mask
|
||||
num=$(kill -l "$sig")
|
||||
for pid in $(pgrep -u "$UID" -x "$name"); do
|
||||
mask=$(awk '/^SigCgt:/ { print $2 }' "/proc/$pid/status" 2>/dev/null)
|
||||
[[ -n $mask ]] && (( (16#$mask >> (num - 1)) & 1 )) && kill -"$sig" "$pid"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Push the freshly rendered files into running programs. Each step is
|
||||
# best-effort: a program that isn't running simply has nothing to reload.
|
||||
apply() {
|
||||
local t="$CURRENT"
|
||||
[[ -f $t/bspwm-colors.sh ]] && pgrep -x bspwm >/dev/null && bash "$t/bspwm-colors.sh"
|
||||
[[ -f $t/Xresources ]] && command -v xrdb >/dev/null && xrdb -merge "$t/Xresources"
|
||||
# One cursor for X, GTK and Qt apps: the default cursor theme follows the rice's.
|
||||
local cursor; cursor=$("$0" get cursor_theme 2>/dev/null)
|
||||
if [[ -n $cursor ]]; then
|
||||
mkdir -p "$HOME/.icons/default"
|
||||
printf '[Icon Theme]\nInherits=%s\n' "$cursor" > "$HOME/.icons/default/index.theme"
|
||||
fi
|
||||
pgrep -x polybar >/dev/null && polybar-msg cmd restart >/dev/null 2>&1 || true
|
||||
pgrep -x dunst >/dev/null && dunstctl reload >/dev/null 2>&1 || true
|
||||
pgrep -x eww >/dev/null && eww reload >/dev/null 2>&1 || true
|
||||
# USR1 makes kitty and the rice's st re-read their colours, but it kills a
|
||||
# process that doesn't handle it (an older st build), so check first.
|
||||
signal_if_caught USR1 kitty
|
||||
signal_if_caught USR1 st
|
||||
pgrep -x xsettingsd >/dev/null && pkill -HUP -x xsettingsd || true
|
||||
if command -v flameshot >/dev/null; then
|
||||
flameshot config --maincolor "$("$0" get accent)" --contrastcolor "$("$0" get bg)" \
|
||||
--showhelp false --trayicon false >/dev/null 2>&1 || true
|
||||
fi
|
||||
if [[ -d $HOOKS ]]; then
|
||||
for h in "$HOOKS"/*; do [[ -x $h ]] && "$h" "$(cat "$STATE/theme.name")" || true; done
|
||||
fi
|
||||
}
|
||||
|
||||
cmd="${1:-}"
|
||||
case "$cmd" in
|
||||
set)
|
||||
[[ -n ${2:-} ]] || die "usage: rice-theme set <name>"
|
||||
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/rice-theme.lock"; flock 9
|
||||
render "$2"; apply
|
||||
# Only a theme change swaps the wallpaper; `reload` keeps the one you picked.
|
||||
command -v rice-wallpaper >/dev/null && { rice-wallpaper theme-default >/dev/null 2>&1 || true; }
|
||||
command -v notify-send >/dev/null && notify-send -a rice -u low "Theme" "$(grep -m1 '^name' "$CURRENT/colors.toml" | cut -d'"' -f2)" || true
|
||||
;;
|
||||
render)
|
||||
# Render only, touch no running program (used by install.sh and for testing).
|
||||
render "${2:-$(cat "$STATE/theme.name" 2>/dev/null || echo sakura-line)}"
|
||||
;;
|
||||
reload)
|
||||
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/rice-theme.lock"; flock 9
|
||||
render "$(cat "$STATE/theme.name" 2>/dev/null || echo sakura-line)"; apply
|
||||
;;
|
||||
list)
|
||||
{ ls -1 "$RICE_DIR/themes"; ls -1 "${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes" 2>/dev/null; } | sort -u
|
||||
;;
|
||||
current)
|
||||
cat "$STATE/theme.name" 2>/dev/null || echo sakura-line
|
||||
;;
|
||||
get)
|
||||
[[ -n ${2:-} ]] || die "usage: rice-theme get <key>"
|
||||
parse_colors "$CURRENT/colors.toml" | awk -F'\t' -v k="$2" '$1 == k { v = $2; found = 1 } END { if (found) print v; exit !found }'
|
||||
;;
|
||||
*)
|
||||
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
||||
[[ -z $cmd ]] || exit 2
|
||||
;;
|
||||
esac
|
||||
28
bin/rice-volume
Executable file
28
bin/rice-volume
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-volume up|down|mute|mic-mute|set <percent> - volume with an on-screen display.
|
||||
set -uo pipefail
|
||||
|
||||
sink=@DEFAULT_AUDIO_SINK@
|
||||
src=@DEFAULT_AUDIO_SOURCE@
|
||||
step=5
|
||||
|
||||
# prints "<percent> <muted|on>"
|
||||
level() { wpctl get-volume "$1" | awk '{ printf "%d %s\n", $2 * 100 + 0.5, ($3 == "[MUTED]") ? "muted" : "on" }'; }
|
||||
|
||||
case "${1:-}" in
|
||||
up) wpctl set-mute "$sink" 0; wpctl set-volume -l 1.0 "$sink" "${step}%+" ;;
|
||||
down) wpctl set-volume "$sink" "${step}%-" ;;
|
||||
mute) wpctl set-mute "$sink" toggle ;;
|
||||
# set comes from the panel slider, which already shows the level: no OSD.
|
||||
set) wpctl set-volume -l 1.0 "$sink" "${2:?usage: rice-volume set <percent>}%"; exit 0 ;;
|
||||
mic-mute)
|
||||
wpctl set-mute "$src" toggle
|
||||
read -r v s < <(level "$src")
|
||||
if [[ $s == muted ]]; then rice-osd Microphone muted; else rice-osd Microphone "$v"; fi
|
||||
exit 0
|
||||
;;
|
||||
*) echo "usage: rice-volume up|down|mute|mic-mute|set <percent>" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
read -r v s < <(level "$sink")
|
||||
if [[ $s == muted ]]; then rice-osd Volume muted; else rice-osd Volume "$v"; fi
|
||||
56
bin/rice-wallpaper
Executable file
56
bin/rice-wallpaper
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-wallpaper - the desktop background.
|
||||
#
|
||||
# rice-wallpaper set <image> set it and remember it
|
||||
# rice-wallpaper restore set the remembered one (session start)
|
||||
# rice-wallpaper theme-default set the active theme's own wallpaper
|
||||
# rice-wallpaper next the next image in the list
|
||||
# rice-wallpaper list images from the rice, ~/.config/rice/wallpapers and ~/Pictures/Wallpapers
|
||||
# rice-wallpaper thumb <image> a cached 400x250 thumbnail for the pickers
|
||||
set -uo pipefail
|
||||
|
||||
RICE_DIR="${RICE_DIR:-$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)}"
|
||||
state="${XDG_STATE_HOME:-$HOME/.local/state}/rice"
|
||||
remembered="$state/wallpaper"
|
||||
thumbs="${XDG_CACHE_HOME:-$HOME/.cache}/rice/thumbs"
|
||||
mkdir -p "$state" "$thumbs"
|
||||
|
||||
list() {
|
||||
find -L "$RICE_DIR/wallpapers" "${XDG_CONFIG_HOME:-$HOME/.config}/rice/wallpapers" "$HOME/Pictures/Wallpapers" \
|
||||
-maxdepth 1 -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' -o -iname '*.webp' \) 2>/dev/null | sort
|
||||
}
|
||||
|
||||
apply() {
|
||||
[[ -f $1 ]] || { echo "rice-wallpaper: no such image: $1" >&2; return 1; }
|
||||
feh --no-fehbg --bg-fill "$1" && realpath -- "$1" > "$remembered"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
set) apply "${2:?usage: rice-wallpaper set <image>}" ;;
|
||||
restore)
|
||||
if [[ -f $remembered && -f $(cat "$remembered") ]]; then apply "$(cat "$remembered")"; else "$0" theme-default; fi
|
||||
;;
|
||||
theme-default)
|
||||
name=$(rice-theme get wallpaper 2>/dev/null)
|
||||
[[ -n $name && -f $RICE_DIR/wallpapers/$name ]] && apply "$RICE_DIR/wallpapers/$name"
|
||||
;;
|
||||
next)
|
||||
mapfile -t all < <(list)
|
||||
(( ${#all[@]} > 0 )) || exit 1
|
||||
current=$(cat "$remembered" 2>/dev/null)
|
||||
for i in "${!all[@]}"; do
|
||||
if [[ ${all[$i]} == "$current" ]]; then apply "${all[$(( (i + 1) % ${#all[@]} ))]}"; exit; fi
|
||||
done
|
||||
apply "${all[0]}"
|
||||
;;
|
||||
list) list ;;
|
||||
thumb)
|
||||
img="${2:?usage: rice-wallpaper thumb <image>}"
|
||||
out="$thumbs/$(printf '%s:%s' "$img" "$(stat -c %Y -- "$img" 2>/dev/null)" | sha1sum | cut -c1-16).jpg"
|
||||
if [[ ! -s $out ]] && command -v magick >/dev/null; then
|
||||
magick "${img}[0]" -thumbnail '400x250^' -gravity center -extent 400x250 -quality 85 "$out" 2>/dev/null
|
||||
fi
|
||||
if [[ -s $out ]]; then echo "$out"; else echo "$img"; fi
|
||||
;;
|
||||
*) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
5
bin/rice-zoom
Executable file
5
bin/rice-zoom
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
# rice-zoom - zoom into the screen. Scroll to zoom, drag to pan, q or Escape to leave.
|
||||
command -v boomer >/dev/null || { rice-notify send "Zoom" "Install boomer-git to zoom"; exit 1; }
|
||||
pgrep -x boomer >/dev/null && exit 0
|
||||
exec boomer
|
||||
BIN
docs/old-screenshot.webp
Normal file
BIN
docs/old-screenshot.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 368 KiB |
16
home/.bash_profile
Normal file
16
home/.bash_profile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# ~/.bash_profile
|
||||
[[ -f ~/.bashrc ]] && . ~/.bashrc
|
||||
|
||||
# Auto-start the graphical session, but ONLY in the exact situation where it is
|
||||
# safe to do so. Any one of these guards failing leaves you at a normal shell.
|
||||
# - not already inside X
|
||||
# - not an SSH session (never hijack a remote login)
|
||||
# - physically on VT 1
|
||||
# - no Xorg already running for this user
|
||||
# NOTE: deliberately NOT `exec startx`. If X dies or fails to start we fall back
|
||||
# to an interactive shell on tty1 instead of a login loop.
|
||||
if [[ -z "$DISPLAY" && -z "$SSH_CONNECTION" && "$XDG_VTNR" == "1" ]]; then
|
||||
if ! pgrep -u "$USER" -x Xorg >/dev/null 2>&1; then
|
||||
startx
|
||||
fi
|
||||
fi
|
||||
47
home/.bashrc
Normal file
47
home/.bashrc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Sakura Line - bash.
|
||||
# Aliases and functions live in ~/.config/rice/bash/. Anything personal or
|
||||
# machine-only (work aliases, SDK paths, tokens) goes in ~/.bashrc.local,
|
||||
# which is not part of the repo.
|
||||
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
[[ $- != *i* ]] && { [[ -f $HOME/.bashrc.local ]] && . "$HOME/.bashrc.local"; return; }
|
||||
|
||||
export EDITOR=nvim
|
||||
export VISUAL=nvim
|
||||
export BAT_THEME=ansi
|
||||
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
|
||||
export MANROFFOPT="-c"
|
||||
|
||||
theme="${XDG_STATE_HOME:-$HOME/.local/state}/rice/theme"
|
||||
|
||||
# --- history ---
|
||||
HISTSIZE=50000
|
||||
HISTFILESIZE=50000
|
||||
HISTCONTROL=ignoreboth:erasedups
|
||||
shopt -s histappend checkwinsize globstar
|
||||
PROMPT_COMMAND="history -a${PROMPT_COMMAND:+; $PROMPT_COMMAND}"
|
||||
|
||||
# --- completion ---
|
||||
[[ -r /usr/share/bash-completion/bash_completion ]] && . /usr/share/bash-completion/bash_completion
|
||||
|
||||
# --- prompt: two lines in the theme's colours ---
|
||||
if command -v starship >/dev/null; then
|
||||
export STARSHIP_CONFIG="$theme/starship.toml"
|
||||
eval "$(starship init bash)"
|
||||
fi
|
||||
|
||||
# --- fzf: Ctrl+R history, Ctrl+T files, Alt+C directories, coloured like rofi ---
|
||||
[[ -f $theme/fzf.sh ]] && . "$theme/fzf.sh"
|
||||
[[ -r /usr/share/fzf/key-bindings.bash ]] && . /usr/share/fzf/key-bindings.bash
|
||||
[[ -r /usr/share/fzf/completion.bash ]] && . /usr/share/fzf/completion.bash
|
||||
|
||||
# --- zoxide: `z` jumps to directories you have been in before ---
|
||||
command -v zoxide >/dev/null && eval "$(zoxide init bash)"
|
||||
|
||||
# --- rice aliases and functions ---
|
||||
for rc in "$HOME"/.config/rice/bash/*.bash; do
|
||||
[[ -r $rc ]] && . "$rc"
|
||||
done
|
||||
unset rc
|
||||
|
||||
[[ -f $HOME/.bashrc.local ]] && . "$HOME/.bashrc.local"
|
||||
68
home/.config/bspwm/bspwmrc
Executable file
68
home/.config/bspwm/bspwmrc
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env bash
|
||||
# Sakura Line - bspwm.
|
||||
# Colours, border width and gaps come from the theme (rice-theme); everything
|
||||
# that runs for the session is started by rice-session. Machine-specific extras
|
||||
# (autostarted apps, per-app desktops) go in ~/.config/rice/local.sh, which is
|
||||
# not part of the repo.
|
||||
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
export XDG_CURRENT_DESKTOP=bspwm
|
||||
export QT_QPA_PLATFORMTHEME=qt6ct
|
||||
export GTK_THEME=Adwaita:dark
|
||||
[[ -f $HOME/.env ]] && . "$HOME/.env"
|
||||
|
||||
theme="${XDG_STATE_HOME:-$HOME/.local/state}/rice/theme"
|
||||
|
||||
# ---------------------------------------------------------------- monitors
|
||||
primary=$(xrandr --query | awk '/ connected primary/ {print $1; exit}')
|
||||
[[ -z $primary ]] && primary=$(bspc query -M --names | head -n1)
|
||||
bspc monitor "$primary" -d 1 2 3 4 5 6
|
||||
|
||||
# Run the primary output at its highest mode and refresh rate.
|
||||
mode=$(xrandr --query | awk -v m="$primary" '$1 == m {getline; print $1; exit}')
|
||||
rate=$(xrandr --query | awk -v m="$primary" '$1 == m {getline; for (i = 2; i <= NF; i++) { r = $i; gsub(/[*+]/, "", r); if (r + 0 > best + 0) best = r }; print best; exit}')
|
||||
[[ -n $mode && -n $rate ]] && xrandr --output "$primary" --mode "$mode" --rate "$rate"
|
||||
|
||||
# ------------------------------------------------------------------ layout
|
||||
bspc config focus_follows_pointer true
|
||||
bspc config borderless_monocle true
|
||||
bspc config gapless_monocle false
|
||||
bspc config split_ratio 0.52
|
||||
bspc config automatic_scheme longest_side
|
||||
bspc config initial_polarity second_child
|
||||
bspc config pointer_modifier mod4
|
||||
bspc config pointer_action1 move
|
||||
bspc config pointer_action2 resize_side
|
||||
bspc config pointer_action3 resize_corner
|
||||
|
||||
# colours, border width, gaps
|
||||
[[ -f $theme/bspwm-colors.sh ]] && . "$theme/bspwm-colors.sh"
|
||||
|
||||
# ------------------------------------------------------------------- rules
|
||||
# Restarting bspwm re-runs this file, so start from a clean rule list.
|
||||
bspc rule -r '*'
|
||||
|
||||
for app in feh imv mpv vlc Yad Zenity Xarchiver ark Blueman-manager; do
|
||||
bspc rule -a "$app" state=floating center=on
|
||||
done
|
||||
bspc rule -a Pavucontrol state=floating center=on rectangle=920x450+0+0
|
||||
bspc rule -a qt5ct state=floating center=on rectangle=900x600+0+0
|
||||
bspc rule -a qt6ct state=floating center=on rectangle=900x600+0+0
|
||||
bspc rule -a Gnome-calculator state=floating center=on rectangle=360x500+0+0
|
||||
bspc rule -a loupe state=floating center=on rectangle=1200x800+0+0
|
||||
bspc rule -a '*:sun-awt-X11-XWindowPeer' manage=off
|
||||
|
||||
# rice surfaces
|
||||
bspc rule -a rice-float state=floating center=on rectangle=900x560+0+0
|
||||
bspc rule -a rice-about state=floating center=on rectangle=760x440+0+0
|
||||
bspc rule -a rice-scratchpad state=floating sticky=on layer=above
|
||||
bspc rule -a '*:rice-webcam' state=floating sticky=on layer=above border=off focus=off
|
||||
bspc rule -a boomer state=fullscreen
|
||||
|
||||
# ------------------------------------------------------------------ session
|
||||
xsetroot -cursor_name left_ptr
|
||||
wmname LG3D
|
||||
|
||||
[[ -f $HOME/.config/rice/local.sh ]] && . "$HOME/.config/rice/local.sh"
|
||||
|
||||
rice-session start &
|
||||
286
home/.config/btop/btop.conf
Normal file
286
home/.config/btop/btop.conf
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
#? Config file for btop v.1.4.7
|
||||
|
||||
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
|
||||
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
|
||||
color_theme = "rice"
|
||||
|
||||
#* If the theme set background should be shown, set to False if you want terminal background transparency.
|
||||
theme_background = false
|
||||
|
||||
#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false.
|
||||
truecolor = true
|
||||
|
||||
#* Set to true to force tty mode regardless if a real tty has been detected or not.
|
||||
#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols.
|
||||
force_tty = false
|
||||
|
||||
#* Option to disable presets. Either the default preset, custom presets, or all presets.
|
||||
#* "Off" All presets are enabled.
|
||||
#* "Default" preset is disabled.#* "Custom" presets are disabled.#* "All" presets are disabled.
|
||||
disable_presets = "Off"
|
||||
|
||||
#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.
|
||||
#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box.
|
||||
#* Use whitespace " " as separator between different presets.
|
||||
#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty"
|
||||
presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"
|
||||
|
||||
#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists.
|
||||
#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift.
|
||||
vim_keys = false
|
||||
|
||||
#* Disable all mouse events.
|
||||
disable_mouse = false
|
||||
|
||||
#* Rounded corners on boxes, is ignored if TTY mode is ON.
|
||||
rounded_corners = true
|
||||
|
||||
#* Use terminal synchronized output sequences to reduce flickering on supported terminals.
|
||||
terminal_sync = true
|
||||
|
||||
#* Default symbols to use for graph creation, "braille", "block" or "tty".
|
||||
#* "braille" offers the highest resolution but might not be included in all fonts.
|
||||
#* "block" has half the resolution of braille but uses more common characters.
|
||||
#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY.
|
||||
#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view.
|
||||
graph_symbol = "braille"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_cpu = "default"
|
||||
|
||||
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_gpu = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_mem = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_net = "default"
|
||||
|
||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||
graph_symbol_proc = "default"
|
||||
|
||||
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
|
||||
shown_boxes = "cpu mem net proc"
|
||||
|
||||
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.
|
||||
update_ms = 1000
|
||||
|
||||
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
|
||||
#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
|
||||
proc_sorting = "cpu lazy"
|
||||
|
||||
#* Reverse sorting order, True or False.
|
||||
proc_reversed = false
|
||||
|
||||
#* Show processes as a tree.
|
||||
proc_tree = false
|
||||
|
||||
#* Use the cpu graph colors in the process list.
|
||||
proc_colors = true
|
||||
|
||||
#* Use a darkening gradient in the process list.
|
||||
proc_gradient = true
|
||||
|
||||
#* If process cpu usage should be of the core it's running on or usage of the total available cpu power.
|
||||
proc_per_core = false
|
||||
|
||||
#* Show process memory as bytes instead of percent.
|
||||
proc_mem_bytes = true
|
||||
|
||||
#* Show cpu graph for each process.
|
||||
proc_cpu_graphs = true
|
||||
|
||||
#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
|
||||
proc_info_smaps = false
|
||||
|
||||
#* Show proc box on left side of screen instead of right.
|
||||
proc_left = false
|
||||
|
||||
#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop).
|
||||
proc_filter_kernel = false
|
||||
|
||||
#* Should the process list follow the selected process when detailed view is open.
|
||||
proc_follow_detailed = true
|
||||
|
||||
#* In tree-view, always accumulate child process resources in the parent process.
|
||||
proc_aggregate = false
|
||||
|
||||
#* Should cpu and memory usage display be preserved for dead processes when paused.
|
||||
keep_dead_proc_usage = false
|
||||
|
||||
#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available.
|
||||
#* Select from a list of detected attributes from the options menu.
|
||||
cpu_graph_upper = "total"
|
||||
|
||||
#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available.
|
||||
#* Select from a list of detected attributes from the options menu.
|
||||
cpu_graph_lower = "Auto"
|
||||
|
||||
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
|
||||
show_gpu_info = "Auto"
|
||||
|
||||
#* Toggles if the lower CPU graph should be inverted.
|
||||
cpu_invert_lower = true
|
||||
|
||||
#* Set to True to completely disable the lower CPU graph.
|
||||
cpu_single_graph = false
|
||||
|
||||
#* Show cpu box at bottom of screen instead of top.
|
||||
cpu_bottom = false
|
||||
|
||||
#* Shows the system uptime in the CPU box.
|
||||
show_uptime = true
|
||||
|
||||
#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo.
|
||||
show_cpu_watts = true
|
||||
|
||||
#* Show cpu temperature.
|
||||
check_temp = true
|
||||
|
||||
#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors.
|
||||
cpu_sensor = "Auto"
|
||||
|
||||
#* Show temperatures for cpu cores also if check_temp is True and sensors has been found.
|
||||
show_coretemp = true
|
||||
|
||||
#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.
|
||||
#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.
|
||||
#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.
|
||||
#* Example: "4:0 5:1 6:3"
|
||||
cpu_core_map = ""
|
||||
|
||||
#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine".
|
||||
temp_scale = "celsius"
|
||||
|
||||
#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024.
|
||||
base_10_sizes = false
|
||||
|
||||
#* Show CPU frequency.
|
||||
show_cpu_freq = true
|
||||
|
||||
#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average".
|
||||
freq_mode = "first"
|
||||
|
||||
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
|
||||
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
|
||||
clock_format = "%H:%M"
|
||||
|
||||
#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort.
|
||||
background_update = true
|
||||
|
||||
#* Custom cpu model name, empty string to disable.
|
||||
custom_cpu_name = ""
|
||||
|
||||
#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ".
|
||||
#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user"
|
||||
disks_filter = ""
|
||||
|
||||
#* Show graphs instead of meters for memory values.
|
||||
mem_graphs = true
|
||||
|
||||
#* Show mem box below net box instead of above.
|
||||
mem_below_net = false
|
||||
|
||||
#* Count ZFS ARC in cached and available memory.
|
||||
zfs_arc_cached = true
|
||||
|
||||
#* If swap memory should be shown in memory box.
|
||||
show_swap = true
|
||||
|
||||
#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk.
|
||||
swap_disk = true
|
||||
|
||||
#* If mem box should be split to also show disks info.
|
||||
show_disks = true
|
||||
|
||||
#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar.
|
||||
only_physical = true
|
||||
|
||||
#* Read disks list from /etc/fstab. This also disables only_physical.
|
||||
use_fstab = true
|
||||
|
||||
#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)
|
||||
zfs_hide_datasets = false
|
||||
|
||||
#* Set to true to show available disk space for privileged users.
|
||||
disk_free_priv = false
|
||||
|
||||
#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view.
|
||||
show_io_stat = true
|
||||
|
||||
#* Toggles io mode for disks, showing big graphs for disk read/write speeds.
|
||||
io_mode = false
|
||||
|
||||
#* Set to True to show combined read/write io graphs in io mode.
|
||||
io_graph_combined = false
|
||||
|
||||
#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ".
|
||||
#* Example: "/mnt/media:100 /:20 /boot:1".
|
||||
io_graph_speeds = ""
|
||||
|
||||
#* Swap the positions of the upload and download speed graphs. When true, upload will be on top.
|
||||
swap_upload_download = false
|
||||
|
||||
#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False.
|
||||
net_download = 100
|
||||
|
||||
net_upload = 100
|
||||
|
||||
#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
|
||||
net_auto = true
|
||||
|
||||
#* Sync the auto scaling for download and upload to whichever currently has the highest scale.
|
||||
net_sync = true
|
||||
|
||||
#* Starts with the Network Interface specified here.
|
||||
net_iface = ""
|
||||
|
||||
#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes.
|
||||
base_10_bitrate = "Auto"
|
||||
|
||||
#* Show battery stats in top right if battery is present.
|
||||
show_battery = true
|
||||
|
||||
#* Which battery to use if multiple are present. "Auto" for auto detection.
|
||||
selected_battery = "Auto"
|
||||
|
||||
#* Show power stats of battery next to charge indicator.
|
||||
show_battery_watts = true
|
||||
|
||||
#* Set loglevel for "~/.local/state/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
|
||||
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
|
||||
log_level = "WARNING"
|
||||
|
||||
#* Automatically save current settings to config file on exit.
|
||||
save_config_on_exit = false
|
||||
|
||||
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
|
||||
nvml_measure_pcie_speeds = true
|
||||
|
||||
#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
|
||||
rsmi_measure_pcie_speeds = true
|
||||
|
||||
#* Horizontally mirror the GPU graph.
|
||||
gpu_mirror_graph = true
|
||||
|
||||
#* Set which GPU vendors to show. Available values are "nvidia amd intel apple"
|
||||
shown_gpus = "nvidia amd intel apple"
|
||||
|
||||
#* Custom gpu0 model name, empty string to disable.
|
||||
custom_gpu_name0 = ""
|
||||
|
||||
#* Custom gpu1 model name, empty string to disable.
|
||||
custom_gpu_name1 = ""
|
||||
|
||||
#* Custom gpu2 model name, empty string to disable.
|
||||
custom_gpu_name2 = ""
|
||||
|
||||
#* Custom gpu3 model name, empty string to disable.
|
||||
custom_gpu_name3 = ""
|
||||
|
||||
#* Custom gpu4 model name, empty string to disable.
|
||||
custom_gpu_name4 = ""
|
||||
|
||||
#* Custom gpu5 model name, empty string to disable.
|
||||
custom_gpu_name5 = ""
|
||||
102
home/.config/dunst/dunstrc
Normal file
102
home/.config/dunst/dunstrc
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Sakura Line - dunst behaviour. Colours and fonts come from the theme drop-in
|
||||
# (dunstrc.d/50-theme.conf -> ~/.local/state/rice/theme/dunst.conf).
|
||||
|
||||
[global]
|
||||
monitor = 0
|
||||
follow = mouse
|
||||
origin = top-right
|
||||
# Same outer edges as tiled windows and popups. bspwm draws borders inside the
|
||||
# gap, so that is one gap (10px) in from the right and bar (37px) + gap down.
|
||||
offset = (10, 47)
|
||||
width = (300, 400)
|
||||
height = (0, 300)
|
||||
notification_limit = 5
|
||||
gap_size = 6
|
||||
padding = 12
|
||||
horizontal_padding = 14
|
||||
text_icon_padding = 12
|
||||
frame_width = 1
|
||||
corner_radius = 12
|
||||
progress_bar = true
|
||||
progress_bar_height = 6
|
||||
progress_bar_frame_width = 0
|
||||
progress_bar_min_width = 150
|
||||
progress_bar_max_width = 372
|
||||
progress_bar_corner_radius = 3
|
||||
indicate_hidden = yes
|
||||
sort = urgency_descending
|
||||
idle_threshold = 120
|
||||
line_height = 2
|
||||
markup = full
|
||||
format = "<b>%s</b>\n%b"
|
||||
alignment = left
|
||||
vertical_alignment = center
|
||||
show_age_threshold = 60
|
||||
ellipsize = middle
|
||||
word_wrap = yes
|
||||
ignore_newline = no
|
||||
stack_duplicates = true
|
||||
hide_duplicate_count = false
|
||||
show_indicators = no
|
||||
enable_recursive_icon_lookup = true
|
||||
icon_position = left
|
||||
min_icon_size = 32
|
||||
max_icon_size = 48
|
||||
sticky_history = yes
|
||||
history_length = 50
|
||||
always_run_script = true
|
||||
dmenu = /usr/bin/rofi -dmenu -theme ~/.config/rofi/menu.rasi -p "Actions"
|
||||
browser = /usr/bin/xdg-open
|
||||
title = Dunst
|
||||
class = Dunst
|
||||
default_pause_level = 0
|
||||
mouse_left_click = do_action, close_current
|
||||
mouse_middle_click = close_all
|
||||
mouse_right_click = close_current
|
||||
|
||||
[urgency_low]
|
||||
timeout = 4
|
||||
|
||||
[urgency_normal]
|
||||
timeout = 6
|
||||
|
||||
[urgency_critical]
|
||||
timeout = 0
|
||||
|
||||
# Do-not-disturb (rice-notify dnd) sets pause level 60. Your own confirmations
|
||||
# and critical alerts sent from the command line still get through; chat apps
|
||||
# that mark everything critical use their own app name, so they stay quiet.
|
||||
[rice_feedback]
|
||||
appname = "rice"
|
||||
override_pause_level = 100
|
||||
|
||||
[critical_cli]
|
||||
appname = "notify-send"
|
||||
msg_urgency = critical
|
||||
override_pause_level = 100
|
||||
|
||||
# Volume, brightness and recording progress: replaced in place, never kept.
|
||||
[osd]
|
||||
appname = "osd"
|
||||
format = "<b>%s</b>"
|
||||
icon_position = off
|
||||
timeout = 2
|
||||
history_ignore = yes
|
||||
override_pause_level = 100
|
||||
|
||||
# Every other notification pokes the notification centre so its count and list stay live.
|
||||
[notify_centre]
|
||||
appname = "*"
|
||||
script = ~/.local/bin/rice-notify-hook
|
||||
|
||||
[fullscreen_delay]
|
||||
fullscreen = delay
|
||||
|
||||
[fullscreen_critical]
|
||||
msg_urgency = critical
|
||||
fullscreen = show
|
||||
|
||||
[flameshot_noise]
|
||||
body = "grim's screenshot component is implemented based on wlroots*"
|
||||
skip_display = true
|
||||
history_ignore = yes
|
||||
217
home/.config/eww/eww.scss
Normal file
217
home/.config/eww/eww.scss
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// Sakura Line - eww design system.
|
||||
// The same tokens as polybar, rofi and dunst: the theme's alpha, 1px border,
|
||||
// 12px radius, rows with an 8px radius and a 17% accent tint on hover.
|
||||
// colors.scss is a link to ~/.local/state/rice/theme/eww-colors.scss.
|
||||
@import "colors";
|
||||
|
||||
$tint: rgba($accent, 0.17);
|
||||
|
||||
* {
|
||||
all: unset;
|
||||
font-family: $font, "Symbols Nerd Font";
|
||||
}
|
||||
|
||||
window, .closer { background-color: transparent; }
|
||||
|
||||
tooltip {
|
||||
background-color: rgba($bg, 0.95);
|
||||
border: $border-width solid $border;
|
||||
border-radius: $radius-small;
|
||||
padding: 6px 10px;
|
||||
color: $fg;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ panel
|
||||
.panel {
|
||||
background-color: rgba($bg, $alpha);
|
||||
border: $border-width solid $border;
|
||||
border-radius: $radius;
|
||||
padding: 4px 8px 10px 8px;
|
||||
color: $fg;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.head {
|
||||
padding: 10px 10px 12px 10px;
|
||||
margin-bottom: 6px;
|
||||
border-bottom: $border-width solid $border;
|
||||
}
|
||||
.title { font-weight: bold; font-size: 14px; }
|
||||
|
||||
.head-action {
|
||||
color: $dim;
|
||||
font-size: 15px;
|
||||
padding: 2px 7px;
|
||||
margin-left: 2px;
|
||||
border-radius: $radius-small;
|
||||
}
|
||||
.head-action:hover { color: $fg; background-color: $tint; }
|
||||
.head-action.active { color: $accent; }
|
||||
|
||||
.section {
|
||||
color: $dim;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
padding: 10px 10px 4px 10px;
|
||||
}
|
||||
|
||||
.sep {
|
||||
min-height: 1px;
|
||||
margin: 6px 4px;
|
||||
background-color: $border;
|
||||
}
|
||||
|
||||
.empty { color: $dim; padding: 8px 10px; }
|
||||
|
||||
// -------------------------------------------------------------------- rows
|
||||
.row {
|
||||
padding: 8px 10px;
|
||||
border-radius: $radius-small;
|
||||
}
|
||||
.row:hover { background-color: $tint; }
|
||||
.row.active .row-title, .row.active .glyph { color: $accent; }
|
||||
.row.dimmed .row-title, .row.dimmed .glyph { color: $dim; }
|
||||
|
||||
.glyph { font-size: 15px; min-width: 24px; margin-right: 10px; }
|
||||
.row-title { color: $fg; }
|
||||
.row-sub { color: $dim; font-size: 11px; margin-top: 1px; }
|
||||
.row-value { color: $dim; margin-left: 10px; }
|
||||
.chev { color: $dim; margin-left: 8px; font-size: 15px; }
|
||||
|
||||
.accent { color: $accent; }
|
||||
.urgent { color: $urgent; }
|
||||
.good { color: $green; }
|
||||
.warn { color: $yellow; }
|
||||
.dim { color: $dim; }
|
||||
|
||||
// ---------------------------------------------------------------- controls
|
||||
.slider-row:hover { background-color: transparent; }
|
||||
.slider-row .glyph { padding: 2px 0; border-radius: $radius-small; }
|
||||
.slider-row .glyph:hover { color: $accent; }
|
||||
.slider-value { min-width: 48px; }
|
||||
|
||||
scale trough {
|
||||
min-height: 6px;
|
||||
min-width: 140px;
|
||||
margin: 0 4px;
|
||||
border-radius: 3px;
|
||||
background-color: $selection;
|
||||
}
|
||||
scale highlight {
|
||||
border-radius: 3px;
|
||||
background-color: $accent;
|
||||
}
|
||||
scale slider {
|
||||
min-width: 12px;
|
||||
min-height: 12px;
|
||||
margin: -3px;
|
||||
border-radius: 6px;
|
||||
background-color: $fg;
|
||||
}
|
||||
|
||||
.switch {
|
||||
min-width: 30px;
|
||||
min-height: 16px;
|
||||
padding: 2px;
|
||||
border-radius: 10px;
|
||||
background-color: $selection;
|
||||
}
|
||||
.switch.on { background-color: rgba($accent, 0.55); }
|
||||
.knob {
|
||||
min-width: 12px;
|
||||
min-height: 12px;
|
||||
border-radius: 6px;
|
||||
background-color: $dim;
|
||||
}
|
||||
.switch.on .knob { background-color: $fg; }
|
||||
|
||||
.pill {
|
||||
padding: 4px 10px;
|
||||
margin: 0 3px;
|
||||
border-radius: $radius-small;
|
||||
color: $dim;
|
||||
}
|
||||
.pills { padding: 2px 6px 6px 6px; }
|
||||
.pill:hover { color: $fg; background-color: $tint; }
|
||||
.pill.active { color: $accent; background-color: $tint; }
|
||||
|
||||
.field {
|
||||
padding: 8px 10px;
|
||||
border-radius: $radius-small;
|
||||
background-color: rgba($surface, 0.8);
|
||||
color: $fg;
|
||||
caret-color: $accent;
|
||||
}
|
||||
|
||||
progressbar trough {
|
||||
min-height: 4px;
|
||||
border-radius: 2px;
|
||||
background-color: $selection;
|
||||
}
|
||||
progressbar progress {
|
||||
min-height: 4px;
|
||||
border-radius: 2px;
|
||||
background-color: $accent;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- calendar
|
||||
.cal-title { font-weight: bold; }
|
||||
.cal-grid { margin: 4px 4px 2px 4px; }
|
||||
.cal-dow, .cal-wk { color: $dim; font-size: 10px; font-weight: bold; min-height: 22px; }
|
||||
.cal-wk { min-width: 26px; }
|
||||
.cal-day {
|
||||
min-width: 34px;
|
||||
min-height: 30px;
|
||||
border-radius: $radius-small;
|
||||
color: $fg;
|
||||
}
|
||||
.cal-day:hover { background-color: $tint; }
|
||||
.cal-day.other { color: $muted; }
|
||||
.cal-day.weekend { color: $fg-alt; }
|
||||
.cal-day.selected { background-color: $tint; color: $accent; }
|
||||
.cal-day.today { background-color: $accent; color: $bg; font-weight: bold; }
|
||||
.cal-dot { color: $magenta; font-size: 8px; min-height: 8px; }
|
||||
.cal-day.today .cal-dot { color: $bg; }
|
||||
|
||||
.event-time { color: $accent; min-width: 52px; font-size: 12px; }
|
||||
.event-bar { min-width: 3px; border-radius: 2px; margin-right: 10px; background-color: $magenta; }
|
||||
.event-bar.reminder { background-color: $yellow; }
|
||||
|
||||
// ------------------------------------------------------------------- media
|
||||
.art {
|
||||
min-width: 72px;
|
||||
min-height: 72px;
|
||||
border-radius: $radius-small;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-color: $surface;
|
||||
}
|
||||
.media-title { font-weight: bold; }
|
||||
.media-artist { color: $dim; font-size: 12px; }
|
||||
.media-btn {
|
||||
font-size: 18px;
|
||||
padding: 4px 12px;
|
||||
border-radius: $radius-small;
|
||||
}
|
||||
.media-btn:hover { background-color: $tint; color: $accent; }
|
||||
.media-time { color: $dim; font-size: 10px; }
|
||||
|
||||
// ----------------------------------------------------------- notifications
|
||||
// The event box takes the hover tint; GTK ignores padding on event boxes,
|
||||
// so the box inside it carries the padding.
|
||||
.notif-hit { border-radius: $radius-small; }
|
||||
.notif-hit:hover { background-color: $tint; }
|
||||
.notif { padding: 8px 10px; }
|
||||
.notif.critical .notif-summary { color: $urgent; }
|
||||
.notif-app { color: $dim; font-size: 10px; font-weight: bold; }
|
||||
.notif-summary { font-weight: bold; }
|
||||
.notif-body { color: $fg-alt; font-size: 12px; }
|
||||
.notif-ago { color: $dim; font-size: 10px; }
|
||||
.notif-close {
|
||||
color: $dim;
|
||||
padding: 0 6px;
|
||||
border-radius: $radius-small;
|
||||
font-size: 13px;
|
||||
}
|
||||
.notif-close:hover { color: $urgent; background-color: rgba($urgent, 0.15); }
|
||||
91
home/.config/eww/eww.yuck
Normal file
91
home/.config/eww/eww.yuck
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
;; Sakura Line - eww popups.
|
||||
;; rice-panel opens one window at a time; every panel is built from the
|
||||
;; widgets below so they all look and behave like the rofi menus.
|
||||
|
||||
;; ------------------------------------------------------------------ state
|
||||
(defvar open_panel "") ; set by rice-panel; list polls only run while their panel is open
|
||||
(defvar cal_offset 0) ; months away from the current one
|
||||
(defvar cal_day "") ; selected day as YYYY-MM-DD, "" = today
|
||||
(defvar cal_input "") ; text typed into the calendar's add box
|
||||
|
||||
(deflisten notifs :initial "{}" "rice-notify watch")
|
||||
(deflisten audio :initial "{}" "rice-audio watch")
|
||||
(deflisten media :initial "{}" "rice-media watch")
|
||||
|
||||
(defpoll net :interval "4s" :initial "{}" :run-while {open_panel == "network" || open_panel == "hub"} "rice-net list")
|
||||
(defpoll bt :interval "4s" :initial "{}" :run-while {open_panel == "bluetooth" || open_panel == "hub"} "rice-bt list")
|
||||
(defpoll display :interval "10s" :initial "{}" :run-while {open_panel == "display" || open_panel == "hub"} "rice-display info")
|
||||
(defpoll power :interval "3s" :initial "{}" :run-while {open_panel == "power" || open_panel == "hub"} "rice-power info")
|
||||
;; rice-cal reads cal_offset / cal_day itself and pushes fresh data on every click.
|
||||
(defpoll month :interval "60s" :initial "{}" :run-while {open_panel == "calendar"} "rice-cal month")
|
||||
(defpoll agenda :interval "30s" :initial "{}" :run-while {open_panel == "calendar"} "rice-cal agenda")
|
||||
(defpoll idle :interval "3s" :initial "{}" :run-while {open_panel == "hub"} "rice-idle status")
|
||||
|
||||
;; ---------------------------------------------------------------- windows
|
||||
;; A transparent layer under the open panel: clicking anywhere else closes it.
|
||||
(defwindow closer
|
||||
:monitor 0
|
||||
:geometry (geometry :x "0" :y "0" :width "100%" :height "100%")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
(eventbox :class "closer" :onclick "rice-panel close" :onrightclick "rice-panel close"))
|
||||
|
||||
;; -------------------------------------------------------- shared widgets
|
||||
;; Header: title on the left, optional action buttons on the right.
|
||||
(defwidget head [title]
|
||||
(box :class "head" :space-evenly false
|
||||
(label :class "title" :text title :hexpand true :xalign 0)
|
||||
(children)))
|
||||
|
||||
(defwidget head-action [glyph onclick ?tip ?active]
|
||||
(button :class "head-action ${active == true ? 'active' : ''}" :onclick onclick :tooltip {tip ?: ""} glyph))
|
||||
|
||||
(defwidget section [text]
|
||||
(label :class "section" :xalign 0 :text text))
|
||||
|
||||
(defwidget sep []
|
||||
(box :class "sep"))
|
||||
|
||||
;; One list row: glyph, title and optional subtitle, value on the right.
|
||||
(defwidget row [glyph title ?sub ?value ?onclick ?active ?chev ?dim]
|
||||
(button :class "row ${active == true ? 'active' : ''} ${dim == true ? 'dimmed' : ''}" :onclick {onclick ?: ""}
|
||||
(box :space-evenly false
|
||||
(label :class "glyph" :text glyph)
|
||||
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
|
||||
(label :class "row-title" :xalign 0 :limit-width 30 :text title)
|
||||
(label :class "row-sub" :xalign 0 :limit-width 38 :visible {(sub ?: "") != ""} :text {sub ?: ""}))
|
||||
(label :class "row-value" :visible {(value ?: "") != ""} :text {value ?: ""})
|
||||
(label :class "chev" :visible {chev == true} :text "›"))))
|
||||
|
||||
;; A row with a pill switch.
|
||||
(defwidget switch-row [glyph title on onclick ?sub]
|
||||
(button :class "row" :onclick onclick
|
||||
(box :space-evenly false
|
||||
(label :class "glyph ${on == true ? 'accent' : ''}" :text glyph)
|
||||
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
|
||||
(label :class "row-title" :xalign 0 :text title)
|
||||
(label :class "row-sub" :xalign 0 :visible {(sub ?: "") != ""} :text {sub ?: ""}))
|
||||
(box :class "switch ${on == true ? 'on' : ''}" :valign "center" :space-evenly false
|
||||
(box :class "knob" :hexpand true :halign {on == true ? "end" : "start"})))))
|
||||
|
||||
;; A slider row: clicking the glyph runs onglyph (mute), dragging runs onchange.
|
||||
(defwidget slider-row [glyph value onchange ?onglyph ?muted]
|
||||
(box :class "row slider-row" :space-evenly false
|
||||
(button :class "glyph ${muted == true ? 'urgent' : ''}" :onclick {onglyph ?: ""} glyph)
|
||||
(scale :class "slider" :hexpand true :min 0 :max 101 :value {value ?: 0} :onchange onchange)
|
||||
(label :class "row-value slider-value" :xalign 1 :text {muted == true ? "muted" : "${value ?: 0}%"})))
|
||||
|
||||
;; An empty-state line inside a list.
|
||||
(defwidget empty [text]
|
||||
(label :class "empty" :xalign 0 :text text))
|
||||
|
||||
;; ----------------------------------------------------------------- panels
|
||||
(include "panels/hub.yuck")
|
||||
(include "panels/audio.yuck")
|
||||
(include "panels/network.yuck")
|
||||
(include "panels/bluetooth.yuck")
|
||||
(include "panels/display.yuck")
|
||||
(include "panels/power.yuck")
|
||||
(include "panels/media.yuck")
|
||||
(include "panels/calendar.yuck")
|
||||
(include "panels/notifications.yuck")
|
||||
3
home/.config/eww/panel-keys
Normal file
3
home/.config/eww/panel-keys
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# sxhkd config that rice-panel runs only while a panel is open.
|
||||
Escape
|
||||
rice-panel close
|
||||
45
home/.config/eww/panels/audio.yuck
Normal file
45
home/.config/eww/panels/audio.yuck
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
;; Audio: output and mic levels, device pickers, per-app mixer.
|
||||
(defwindow audio
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Audio"
|
||||
(head-action :glyph "" :tip "Advanced mixer" :onclick "rice-panel close; setsid -f pavucontrol"))
|
||||
|
||||
(slider-row :glyph {audio?.sink?.muted == true ? "" : ""}
|
||||
:value {audio?.sink?.vol ?: 0}
|
||||
:muted {audio?.sink?.muted ?: false}
|
||||
:onglyph "rice-volume mute"
|
||||
:onchange "rice-volume set {}")
|
||||
(slider-row :glyph {audio?.source?.muted == true ? "" : ""}
|
||||
:value {audio?.source?.vol ?: 0}
|
||||
:muted {audio?.source?.muted ?: false}
|
||||
:onglyph "rice-audio mic-mute"
|
||||
:onchange "rice-audio mic {}")
|
||||
|
||||
;; eww appends a loop's items after their siblings, so every loop gets its own box.
|
||||
(section :text "Output")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for s in {audio?.sinks ?: []}
|
||||
(row :glyph "" :title {s.desc} :active {s.default} :onclick "rice-audio sink ${s.index}")))
|
||||
|
||||
(section :text "Input")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for s in {audio?.sources ?: []}
|
||||
(row :glyph "" :title {s.desc} :active {s.default} :onclick "rice-audio source ${s.index}")))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {arraylength(audio?.apps ?: []) > 0}
|
||||
(section :text "Apps")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for a in {audio?.apps ?: []}
|
||||
(box :class "row slider-row" :space-evenly false
|
||||
(button :class "glyph ${a.muted ? 'urgent' : ''}" :tooltip {a.name}
|
||||
:onclick "rice-audio app-mute ${a.id}"
|
||||
{a.muted ? "" : ""})
|
||||
(label :class "row-title" :xalign 0 :width 90 :limit-width 11 :text {a.name})
|
||||
(scale :class "slider" :hexpand true :min 0 :max 101 :value {a.vol}
|
||||
:onchange "rice-audio app-volume ${a.id} {}")
|
||||
(label :class "row-value slider-value" :xalign 1 :text "${a.vol}%")))))))
|
||||
32
home/.config/eww/panels/bluetooth.yuck
Normal file
32
home/.config/eww/panels/bluetooth.yuck
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
;; Bluetooth: power, paired devices with battery, nearby devices to pair.
|
||||
(defwindow bluetooth
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Bluetooth"
|
||||
(head-action :glyph "" :tip "Look for new devices" :active {bt?.discovering == true}
|
||||
:onclick "rice-bt scan"))
|
||||
|
||||
(switch-row :glyph "" :title "Bluetooth" :on {bt?.powered == true}
|
||||
:onclick "rice-bt power toggle"
|
||||
:sub {bt?.powered != true ? "off" : (bt?.connected > 0 ? "${bt.connected} connected" : "on")})
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {bt?.powered == true}
|
||||
(section :text "My devices")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for d in {bt?.paired ?: []}
|
||||
(row :glyph {d.glyph} :title {d.name} :sub {d.sub} :value {d.battery}
|
||||
:active {d.connected} :onclick "rice-bt toggle ${d.mac}")))
|
||||
(box :visible {arraylength(bt?.paired ?: []) == 0}
|
||||
(empty :text "No paired devices"))
|
||||
|
||||
(section :text {bt?.discovering == true ? "Nearby · scanning" : "Nearby"})
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for d in {bt?.nearby ?: []}
|
||||
(row :glyph {d.glyph} :title {d.name} :sub {d.sub} :dim true
|
||||
:onclick "rice-bt toggle ${d.mac}")))
|
||||
(box :visible {arraylength(bt?.nearby ?: []) == 0}
|
||||
(empty :text {bt?.discovering == true ? "Looking…" : "Press to look for devices"})))))
|
||||
47
home/.config/eww/panels/calendar.yuck
Normal file
47
home/.config/eww/panels/calendar.yuck
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
;; Calendar: month grid with ISO weeks, the selected day's events and reminders.
|
||||
(defwindow calendar
|
||||
:monitor 0
|
||||
:geometry (geometry :x "0" :y "47px" :width "360px" :anchor "top center")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title {month?.title ?: "Calendar"}
|
||||
(head-action :glyph "" :tip "Previous month" :onclick "rice-cal shift -1")
|
||||
(head-action :glyph "" :tip "Today" :onclick "rice-cal today")
|
||||
(head-action :glyph "" :tip "Next month" :onclick "rice-cal shift 1"))
|
||||
|
||||
(box :class "cal-grid" :orientation "v" :space-evenly false
|
||||
(box :space-evenly false
|
||||
(label :class "cal-wk" :text "Wk")
|
||||
(for d in {month?.dow ?: []}
|
||||
(label :class "cal-dow" :hexpand true :text d)))
|
||||
(for w in {month?.weeks ?: []}
|
||||
(box :space-evenly false
|
||||
(label :class "cal-wk" :text {w.wk})
|
||||
(for d in {w.days}
|
||||
(button :hexpand true :onclick "rice-cal select ${d.date}"
|
||||
:class "cal-day ${d.other ? 'other' : ''} ${d.weekend ? 'weekend' : ''} ${d.selected ? 'selected' : ''} ${d.today ? 'today' : ''}"
|
||||
(box :orientation "v" :space-evenly false :valign "center"
|
||||
(label :text {d.d})
|
||||
(label :class "cal-dot" :text {d.events > 0 ? "●" : " "})))))))
|
||||
|
||||
(sep)
|
||||
(box :space-evenly false
|
||||
(label :class "section" :hexpand true :xalign 0 :text {agenda?.heading ?: "Today"})
|
||||
(head-action :glyph "" :tip "Add event" :onclick "rice-cal add")
|
||||
(head-action :glyph "" :tip "Add reminder" :onclick "rice-panel close; rice-reminder new"))
|
||||
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for ev in {agenda?.items ?: []}
|
||||
(box :class "row" :space-evenly false
|
||||
(box :class "event-bar ${ev.kind == 'reminder' ? 'reminder' : ''}")
|
||||
(label :class "event-time" :xalign 0 :text {ev.time})
|
||||
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
|
||||
(label :class "row-title" :xalign 0 :limit-width 28 :text {ev.title})
|
||||
(label :class "row-sub" :xalign 0 :limit-width 36 :visible {ev.sub != ""} :text {ev.sub}))
|
||||
(button :class "notif-close" :valign "center" :visible {ev.kind == "reminder"}
|
||||
:tooltip "Cancel reminder" :onclick "rice-reminder cancel ${ev.id}" ""))))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {arraylength(agenda?.items ?: []) == 0}
|
||||
(empty :text {(agenda?.status ?: "") != "" ? agenda.status : "Nothing planned"}))))
|
||||
30
home/.config/eww/panels/display.yuck
Normal file
30
home/.config/eww/panels/display.yuck
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
;; Display: brightness over DDC, resolution, refresh rate, bar, style shortcuts.
|
||||
(defwindow display
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Display"
|
||||
(head-action :glyph "" :tip "Wallpaper" :onclick "rice-panel close; rice-menu wallpaper")
|
||||
(head-action :glyph "" :tip "Theme" :onclick "rice-panel close; rice-menu theme"))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {display?.ddc == true}
|
||||
(slider-row :glyph "" :value {display?.brightness ?: 0}
|
||||
:onchange "rice-display brightness {}"))
|
||||
(box :orientation "v" :space-evenly false :visible {display?.ddc != true}
|
||||
(row :glyph "" :title "Brightness" :sub {display?.ddcHint ?: ""} :dim true))
|
||||
|
||||
(section :text {display?.output ?: "Output"})
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for m in {display?.modes ?: []}
|
||||
(row :glyph "" :title {m.mode} :active {m.current} :onclick "rice-display mode ${m.id}")))
|
||||
|
||||
(section :text "Refresh rate")
|
||||
(box :class "pills" :space-evenly true
|
||||
(for r in {display?.rates ?: []}
|
||||
(button :class "pill ${r.current ? 'active' : ''}" :onclick "rice-display rate ${r.id}" {r.label})))
|
||||
|
||||
(sep)
|
||||
(switch-row :glyph "" :title "Bar" :on {display?.bar == true} :onclick "rice-bar toggle")))
|
||||
50
home/.config/eww/panels/hub.yuck
Normal file
50
home/.config/eww/panels/hub.yuck
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
;; Control centre (super + x): levels, a line per subsystem, quick switches, now playing.
|
||||
(defwindow hub
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "400px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Control Centre"
|
||||
(head-action :glyph "" :tip "Rice menu" :onclick "rice-panel close; rice-menu main")
|
||||
(head-action :glyph "" :tip "Power menu" :onclick "rice-panel close; rice-menu power"))
|
||||
|
||||
(slider-row :glyph {audio?.sink?.muted == true ? "" : ""}
|
||||
:value {audio?.sink?.vol ?: 0} :muted {audio?.sink?.muted ?: false}
|
||||
:onglyph "rice-volume mute" :onchange "rice-volume set {}")
|
||||
(slider-row :glyph {audio?.source?.muted == true ? "" : ""}
|
||||
:value {audio?.source?.vol ?: 0} :muted {audio?.source?.muted ?: false}
|
||||
:onglyph "rice-audio mic-mute" :onchange "rice-audio mic {}")
|
||||
(box :orientation "v" :space-evenly false :visible {display?.ddc == true}
|
||||
(slider-row :glyph "" :value {display?.brightness ?: 0} :onchange "rice-display brightness {}"))
|
||||
|
||||
(sep)
|
||||
(row :glyph {net?.active?.type == "ethernet" ? "" : (net?.active?.type == "wifi" ? "" : "")}
|
||||
:title "Network" :value {net?.active?.name ?: "offline"} :chev true :onclick "rice-panel network")
|
||||
(row :glyph "" :title "Bluetooth" :chev true :onclick "rice-panel bluetooth"
|
||||
:value {bt?.powered != true ? "off" : (bt?.connected > 0 ? "${bt.connected} connected" : "on")})
|
||||
(row :glyph "" :title "Sound" :value {audio?.sink?.name ?: ""} :chev true :onclick "rice-panel audio")
|
||||
(row :glyph "" :title "Display" :chev true :onclick "rice-panel display"
|
||||
:value "${display?.mode ?: ''} · ${round(display?.rate ?: 0, 0)} Hz")
|
||||
(row :glyph "" :title "System" :chev true :onclick "rice-panel power"
|
||||
:value "CPU ${power?.cpu?.load ?: 0}% · Memory ${power?.memory?.percent ?: 0}%")
|
||||
(row :glyph "" :title "Calendar" :value {formattime(EWW_TIME, "%a %-d %b")} :chev true :onclick "rice-panel calendar")
|
||||
|
||||
(sep)
|
||||
(switch-row :glyph "" :title "Do not disturb" :on {notifs?.dnd == true} :onclick "rice-notify dnd toggle"
|
||||
:sub {(notifs?.count ?: 0) > 0 ? "${notifs.count} in the notification centre" : ""})
|
||||
(switch-row :glyph "" :title "Stay awake" :on {idle?.awake == true} :onclick "rice-idle toggle"
|
||||
:sub {idle?.awake == true ? "screensaver and lock paused" : ""})
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {media?.status == "Playing" || media?.status == "Paused"}
|
||||
(sep)
|
||||
(box :class "row" :space-evenly false
|
||||
(label :class "glyph accent" :text "")
|
||||
(eventbox :hexpand true :onclick "rice-panel media"
|
||||
(box :orientation "v" :space-evenly false :valign "center"
|
||||
(label :class "row-title" :xalign 0 :limit-width 22 :text {media?.title ?: ""})
|
||||
(label :class "row-sub" :xalign 0 :limit-width 26 :text {media?.artist ?: ""})))
|
||||
(button :class "media-btn" :onclick "playerctl previous" "")
|
||||
(button :class "media-btn accent" :onclick "playerctl play-pause" {media?.status == "Playing" ? "" : ""})
|
||||
(button :class "media-btn" :onclick "playerctl next" "")))))
|
||||
39
home/.config/eww/panels/media.yuck
Normal file
39
home/.config/eww/panels/media.yuck
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
;; Media: what is playing, a seek bar, and transport controls.
|
||||
(defpoll media_pos :interval "1s" :initial "{}" :run-while {open_panel == "media"} "rice-media position")
|
||||
|
||||
(defwindow media
|
||||
:monitor 0
|
||||
:geometry (geometry :x "0" :y "47px" :width "380px" :anchor "top center")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Media"
|
||||
(label :class "dim" :text {media?.player ?: ""}))
|
||||
|
||||
(box :orientation "v" :space-evenly false
|
||||
:visible {media?.status != "Playing" && media?.status != "Paused"}
|
||||
(empty :text "Nothing is playing"))
|
||||
|
||||
(box :orientation "v" :space-evenly false
|
||||
:visible {media?.status == "Playing" || media?.status == "Paused"}
|
||||
(box :class "row" :space-evenly false
|
||||
(box :class "art" :visible {(media?.art ?: "") != ""}
|
||||
:style "background-image: url('${media?.art ?: ''}');")
|
||||
(box :orientation "v" :space-evenly false :valign "center" :hexpand true
|
||||
(label :class "media-title" :xalign 0 :limit-width 26 :text {media?.title ?: ""})
|
||||
(label :class "media-artist" :xalign 0 :limit-width 32 :text {media?.artist ?: ""})
|
||||
(label :class "media-artist" :xalign 0 :limit-width 32 :visible {(media?.album ?: "") != ""}
|
||||
:text {media?.album ?: ""})))
|
||||
|
||||
(box :class "row slider-row" :space-evenly false :visible {(media?.length ?: 0) > 0}
|
||||
(label :class "media-time" :width 36 :xalign 0 :text {media_pos?.text ?: "0:00"})
|
||||
(scale :class "slider" :hexpand true :min 0 :max {media?.length ?: 1}
|
||||
:value {media_pos?.pos ?: 0} :onchange "rice-media seek {}")
|
||||
(label :class "media-time" :width 36 :xalign 1 :text {media?.lengthText ?: ""}))
|
||||
|
||||
(box :class "row" :space-evenly true
|
||||
(button :class "media-btn" :tooltip "Previous" :onclick "playerctl previous" "")
|
||||
(button :class "media-btn accent" :tooltip "Play / pause" :onclick "playerctl play-pause"
|
||||
{media?.status == "Playing" ? "" : ""})
|
||||
(button :class "media-btn" :tooltip "Next" :onclick "playerctl next" "")))))
|
||||
37
home/.config/eww/panels/network.yuck
Normal file
37
home/.config/eww/panels/network.yuck
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
;; Network: Wi-Fi radio, the active connection, nearby networks and VPNs.
|
||||
(defwindow network
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Network"
|
||||
(head-action :glyph "" :tip "Rescan" :onclick "rice-net rescan")
|
||||
(head-action :glyph "" :tip "Share Wi-Fi as a QR code" :onclick "rice-panel close; rice-net share")
|
||||
(head-action :glyph "" :tip "Connection editor" :onclick "rice-panel close; setsid -f nm-connection-editor"))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {net?.active?.type == "ethernet"}
|
||||
(row :glyph "" :title "Ethernet" :sub {net?.active?.name ?: ""} :active true))
|
||||
|
||||
;; Machines without a Wi-Fi adapter (like a wired desktop) skip this part.
|
||||
(box :orientation "v" :space-evenly false :visible {net?.hasWifi == true}
|
||||
(switch-row :glyph "" :title "Wi-Fi" :on {net?.radio == true}
|
||||
:onclick "rice-net radio toggle"
|
||||
:sub {net?.active?.type == "wifi" ? net.active.name : (net?.radio == true ? "not connected" : "off")}))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {net?.hasWifi == true && net?.radio == true}
|
||||
(section :text "Networks")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for w in {net?.wifi ?: []}
|
||||
(row :glyph {w.glyph} :title {w.ssid} :sub {w.sub} :active {w.active}
|
||||
:onclick {w.active ? "rice-net disconnect" : "rice-net connect ${w.id}"})))
|
||||
(box :visible {arraylength(net?.wifi ?: []) == 0}
|
||||
(empty :text "No networks found yet")))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {arraylength(net?.vpn ?: []) > 0}
|
||||
(section :text "VPN")
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for v in {net?.vpn ?: []}
|
||||
(row :glyph {v.active ? "" : ""} :title {v.name} :sub {v.active ? "connected" : ""}
|
||||
:active {v.active} :onclick "rice-net vpn ${v.id}"))))))
|
||||
37
home/.config/eww/panels/notifications.yuck
Normal file
37
home/.config/eww/panels/notifications.yuck
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
;; Notification centre: recent notifications, dismiss one or all, do-not-disturb.
|
||||
(defwindow notifications
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Notifications"
|
||||
(head-action :glyph {notifs?.dnd == true ? "" : ""} :active {notifs?.dnd == true}
|
||||
:tip "Do not disturb" :onclick "rice-notify dnd toggle")
|
||||
(head-action :glyph "" :tip "Clear all" :onclick "rice-notify clear"))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {notifs?.dnd == true && (notifs?.waiting ?: 0) > 0}
|
||||
(row :glyph "" :title "Held back" :sub "They appear when do-not-disturb is off"
|
||||
:value "${notifs?.waiting ?: 0}" :dim true))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {(notifs?.count ?: 0) == 0}
|
||||
(empty :text "You're all caught up"))
|
||||
|
||||
(box :orientation "v" :space-evenly false
|
||||
(for n in {notifs?.items ?: []}
|
||||
(eventbox :class "notif-hit"
|
||||
:onclick "rice-panel close; rice-notify open ${n.id}"
|
||||
(box :class "notif ${n.urgency == 'CRITICAL' ? 'critical' : ''}" :space-evenly false
|
||||
(box :orientation "v" :hexpand true :space-evenly false
|
||||
(box :space-evenly false
|
||||
(label :class "notif-app" :xalign 0 :hexpand true :limit-width 30 :text {n.app})
|
||||
(label :class "notif-ago" :text {n.ago}))
|
||||
(label :class "notif-summary" :xalign 0 :limit-width 36 :text {n.summary})
|
||||
(label :class "notif-body" :xalign 0 :wrap true :limit-width 140
|
||||
:visible {n.body != ""} :text {n.body}))
|
||||
(button :class "notif-close" :valign "start" :tooltip "Dismiss"
|
||||
:onclick "rice-notify remove ${n.id}" "")))))
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {(notifs?.count ?: 0) > 8}
|
||||
(row :glyph "" :title "${notifs?.count - 8} older" :sub "Clear all to start fresh" :dim true))))
|
||||
31
home/.config/eww/panels/power.yuck
Normal file
31
home/.config/eww/panels/power.yuck
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
;; Power: what the machine is doing, the power profile, and session actions.
|
||||
(defwindow power
|
||||
:monitor 0
|
||||
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
|
||||
:stacking "fg"
|
||||
:wm-ignore true
|
||||
:resizable false
|
||||
(box :class "panel" :orientation "v" :space-evenly false
|
||||
(head :title "Power"
|
||||
(head-action :glyph "" :tip "Activity monitor" :onclick "rice-panel close; setsid -f st -c rice-float -e btop"))
|
||||
|
||||
(row :glyph "" :title "CPU"
|
||||
:value "${power?.cpu?.load ?: 0}%${power?.cpu?.temp != 'null' && power?.cpu?.temp != '' ? ' ' + power.cpu.temp + '°' : ''}")
|
||||
(row :glyph "" :title "GPU" :sub {power?.gpu?.vram ?: ""}
|
||||
:value "${power?.gpu?.load ?: 0}%${power?.gpu?.temp != 'null' && power?.gpu?.temp != '' ? ' ' + power.gpu.temp + '°' : ''}")
|
||||
(row :glyph "" :title "Memory" :value "${power?.memory?.used ?: '-'} / ${power?.memory?.total ?: '-'}")
|
||||
(row :glyph "" :title "Uptime" :value {power?.uptime ?: ""})
|
||||
|
||||
(box :orientation "v" :space-evenly false :visible {arraylength(power?.profiles ?: []) > 0}
|
||||
(section :text "Power profile")
|
||||
(box :class "row" :space-evenly true
|
||||
(for p in {power?.profiles ?: []}
|
||||
(button :class "pill ${p.active ? 'active' : ''}" :onclick "rice-power profile ${p.name}" {p.label}))))
|
||||
|
||||
(sep)
|
||||
(box :space-evenly true :class "row"
|
||||
(button :class "media-btn" :tooltip "Lock" :onclick "rice-power lock" "")
|
||||
(button :class "media-btn" :tooltip "Suspend" :onclick "rice-power suspend" "")
|
||||
(button :class "media-btn" :tooltip "Log out" :onclick "rice-power logout" "")
|
||||
(button :class "media-btn" :tooltip "Reboot" :onclick "rice-power reboot" "")
|
||||
(button :class "media-btn urgent" :tooltip "Shut down" :onclick "rice-power shutdown" ""))))
|
||||
42
home/.config/fastfetch/config.jsonc
Executable file
42
home/.config/fastfetch/config.jsonc
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json",
|
||||
"logo": null,
|
||||
"display": {
|
||||
"separator": " › "
|
||||
},
|
||||
"modules": [
|
||||
"break",
|
||||
{
|
||||
"type": "os",
|
||||
"key": "OS ",
|
||||
"keyColor": "31", // = color1
|
||||
},
|
||||
{
|
||||
"type": "kernel",
|
||||
"key": "KER ",
|
||||
"keyColor": "32",
|
||||
},
|
||||
{
|
||||
"type": "packages",
|
||||
"format": "{} (pacman)",
|
||||
"key": "PKG ",
|
||||
"keyColor": "33",
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"key": "SH ",
|
||||
"keyColor": "34",
|
||||
},
|
||||
{
|
||||
"type": "terminal",
|
||||
"key": "TER ",
|
||||
"keyColor": "35",
|
||||
},
|
||||
{
|
||||
"type": "wm",
|
||||
"key": "WM ",
|
||||
"keyColor": "36",
|
||||
},
|
||||
"break",
|
||||
]
|
||||
}
|
||||
16
home/.config/fastfetch/linuximaginary.txt
Normal file
16
home/.config/fastfetch/linuximaginary.txt
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.d8 .8uI
|
||||
88R m888
|
||||
88R %d88"
|
||||
88R 88R
|
||||
88R 88R
|
||||
88R 88R
|
||||
88R 88R
|
||||
88R 88R
|
||||
88R 88R
|
||||
88B 88B
|
||||
888 88*
|
||||
.88u
|
||||
%88*
|
||||
8I*
|
||||
J"*
|
||||
|
||||
7
home/.config/fastfetch/linuximaginary_small.txt
Normal file
7
home/.config/fastfetch/linuximaginary_small.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.d8 .8u
|
||||
88R 888
|
||||
88R d8"
|
||||
88R 88R
|
||||
88B 88*
|
||||
.88u 8I*
|
||||
88R *IL
|
||||
28
home/.config/git/config
Normal file
28
home/.config/git/config
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Sakura Line - git defaults. Your name, email and credentials stay in
|
||||
# ~/.gitconfig, which git reads after this file, so it always wins.
|
||||
[init]
|
||||
defaultBranch = main
|
||||
[pull]
|
||||
rebase = true
|
||||
[push]
|
||||
autoSetupRemote = true
|
||||
[diff]
|
||||
algorithm = histogram
|
||||
colorMoved = plain
|
||||
mnemonicPrefix = true
|
||||
[commit]
|
||||
verbose = true
|
||||
[column]
|
||||
ui = auto
|
||||
[branch]
|
||||
sort = -committerdate
|
||||
[tag]
|
||||
sort = -version:refname
|
||||
[rerere]
|
||||
enabled = true
|
||||
autoupdate = true
|
||||
[alias]
|
||||
co = checkout
|
||||
br = branch
|
||||
ci = commit
|
||||
st = status
|
||||
11
home/.config/kitty/kitty.conf
Normal file
11
home/.config/kitty/kitty.conf
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Sakura Line - kitty. Font and colours come from the theme (rice-theme).
|
||||
font_size 11.0
|
||||
hide_window_decorations yes
|
||||
confirm_os_window_close 0
|
||||
window_padding_width 10
|
||||
enable_audio_bell no
|
||||
cursor_shape block
|
||||
cursor_blink_interval 0
|
||||
scrollback_lines 10000
|
||||
|
||||
include ~/.local/state/rice/theme/kitty.conf
|
||||
33
home/.config/particles.conf
Normal file
33
home/.config/particles.conf
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# particles - wallpaper layer
|
||||
# Tuned for bspwm/wallpaper/wallpaper.jpg: a bright, busy image, so the field
|
||||
# is white and fairly opaque. Preview any change without touching the desktop:
|
||||
# particles --snapshot /tmp/p.png --snapshot-bg ~/.config/bspwm/wallpaper/wallpaper.png
|
||||
# Reload a running instance after editing: pkill -HUP particles
|
||||
|
||||
density = 55
|
||||
radius = 2.4
|
||||
glow = 4.0
|
||||
opacity = 1.0
|
||||
color = ffffff
|
||||
|
||||
link-distance = 140
|
||||
link-width = 1.6
|
||||
link-opacity = 0.85
|
||||
link-color = ffffff
|
||||
|
||||
speed = 24
|
||||
edges = bounce
|
||||
|
||||
cursor = grab,repel
|
||||
cursor-distance = 180
|
||||
cursor-strength = 1.0
|
||||
cursor-response = 9
|
||||
|
||||
fps = 60
|
||||
keep-below = true
|
||||
pause-when-covered = true
|
||||
|
||||
mode = jigsaw
|
||||
jigsaw-cols = 800
|
||||
jigsaw-rows = 500
|
||||
jigsaw-loose = 5
|
||||
60
home/.config/picom/picom.conf
Normal file
60
home/.config/picom/picom.conf
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Sakura Line - picom 13.
|
||||
# One motion language for everything: windows, rofi, eww popups and dunst
|
||||
# all appear with the same short scale-and-fade.
|
||||
|
||||
backend = "glx";
|
||||
vsync = true;
|
||||
use-damage = true;
|
||||
detect-client-opacity = true;
|
||||
detect-transient = true;
|
||||
unredir-if-possible = true;
|
||||
log-level = "warn";
|
||||
|
||||
shadow = false;
|
||||
blur-background = false;
|
||||
fading = true;
|
||||
fade-in-step = 0.08;
|
||||
fade-out-step = 0.08;
|
||||
fade-delta = 8;
|
||||
corner-radius = 12;
|
||||
|
||||
animations = (
|
||||
{ triggers = [ "open", "show" ]; preset = "appear"; scale = 0.94; duration = 0.16; },
|
||||
{ triggers = [ "close", "hide" ]; preset = "disappear"; scale = 0.96; duration = 0.12; }
|
||||
);
|
||||
|
||||
rules = (
|
||||
# The bar and desktop layer draw their own shape and never animate.
|
||||
{ match = "window_type = 'dock' || window_type = 'desktop'"; corner-radius = 0; fade = false; animations = (); },
|
||||
|
||||
# Fullscreen video and games: square, and let them unredirect.
|
||||
{ match = "fullscreen"; corner-radius = 0; },
|
||||
|
||||
# Toolkit menus and tooltips pop instantly.
|
||||
{ match = "window_type = 'tooltip' || window_type = 'popup_menu' || window_type = 'dropdown_menu' || window_type = 'menu'"; corner-radius = 0; animations = (); },
|
||||
|
||||
# dunst rounds its own corners; picom rounding on top leaves a halo.
|
||||
{ match = "class_g = 'Dunst'"; corner-radius = 0; },
|
||||
|
||||
# Menus and popups share a quicker, flatter version of the window motion.
|
||||
{ match = "class_g = 'Rofi' || class_g ~= '^eww'";
|
||||
animations = (
|
||||
{ triggers = [ "open", "show" ]; preset = "appear"; scale = 0.98; duration = 0.10; },
|
||||
{ triggers = [ "close", "hide" ]; preset = "disappear"; scale = 0.98; duration = 0.08; }
|
||||
);
|
||||
},
|
||||
|
||||
# The drop-down terminal slides from the top edge.
|
||||
{ match = "class_g = 'rice-scratchpad'";
|
||||
animations = (
|
||||
{ triggers = [ "open", "show" ]; preset = "slide-in"; direction = "down"; duration = 0.18; },
|
||||
{ triggers = [ "close", "hide" ]; preset = "slide-out"; direction = "up"; duration = 0.14; }
|
||||
);
|
||||
},
|
||||
|
||||
# Capture overlays must not move or fade while you aim.
|
||||
{ match = "class_g = 'flameshot' || class_g = 'boomer' || class_g = 'slop'"; corner-radius = 0; fade = false; animations = (); },
|
||||
|
||||
# The webcam overlay stays a clean rounded tile.
|
||||
{ match = "class_i = 'rice-webcam'"; corner-radius = 16; }
|
||||
);
|
||||
121
home/.config/polybar/config.ini
Normal file
121
home/.config/polybar/config.ini
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
; Sakura Line - polybar. Colours come from the rendered theme.
|
||||
; Every label opens its panel: left click = panel, right click = quick action.
|
||||
|
||||
include-file = ~/.local/state/rice/theme/polybar-colors.ini
|
||||
include-file = ~/.local/state/rice/theme/polybar-modules.ini
|
||||
|
||||
[bar/top]
|
||||
monitor = ${env:MONITOR:}
|
||||
wm-restack = bspwm
|
||||
enable-ipc = true
|
||||
; Edges line up with tiled windows: bspwm draws borders inside the gap, so their
|
||||
; outer edges sit one gap (10px) in from each side, so 100% minus 20px.
|
||||
width = 100%:-20
|
||||
height = 28
|
||||
radius = 10
|
||||
offset-x = 10
|
||||
offset-y = 5
|
||||
fixed-center = true
|
||||
background = ${colors.bg-trans}
|
||||
foreground = ${colors.text}
|
||||
border-size = 2
|
||||
border-color = ${colors.border}
|
||||
padding = 1
|
||||
module-margin = 0
|
||||
cursor-click = pointer
|
||||
cursor-scroll = ns-resize
|
||||
font-0 = ${fonts.main}
|
||||
font-1 = ${fonts.large}
|
||||
; The clock sits alone in the centre so nothing that comes and goes (the
|
||||
; player, recording or reminder indicators) can push it off centre.
|
||||
modules-left = menu bspwm media
|
||||
modules-center = date
|
||||
modules-right = indicators cpu sep ram sep gpu sep net sep bt sep vol sep notif
|
||||
|
||||
[module/sep]
|
||||
type = custom/text
|
||||
format = "|"
|
||||
format-foreground = ${colors.dim}
|
||||
format-padding = 0
|
||||
|
||||
[module/menu]
|
||||
type = custom/text
|
||||
format = ""
|
||||
format-foreground = ${colors.accent}
|
||||
format-font = 2
|
||||
format-padding = 1
|
||||
click-left = rice-menu main
|
||||
click-right = rice-panel hub
|
||||
|
||||
[module/bspwm]
|
||||
type = internal/bspwm
|
||||
pin-workspaces = true
|
||||
inline-mode = true
|
||||
enable-click = true
|
||||
enable-scroll = true
|
||||
format = <label-state>
|
||||
label-focused = "[%name%]"
|
||||
label-focused-foreground = ${colors.accent}
|
||||
label-focused-padding = 1
|
||||
label-occupied = "%name%"
|
||||
label-occupied-foreground = ${colors.text}
|
||||
label-occupied-padding = 1
|
||||
label-empty = "%name%"
|
||||
label-empty-foreground = ${colors.dim}
|
||||
label-empty-padding = 1
|
||||
label-urgent = "!%name%!"
|
||||
label-urgent-foreground = ${colors.urgent}
|
||||
label-urgent-padding = 1
|
||||
|
||||
[module/indicators]
|
||||
type = custom/script
|
||||
exec = rice-status indicators
|
||||
tail = true
|
||||
format-padding = 1
|
||||
|
||||
[module/media]
|
||||
type = custom/script
|
||||
exec = rice-status media
|
||||
tail = true
|
||||
format-padding = 1
|
||||
click-left = rice-panel media
|
||||
click-right = playerctl play-pause
|
||||
scroll-up = playerctl previous
|
||||
scroll-down = playerctl next
|
||||
|
||||
[module/net]
|
||||
type = custom/script
|
||||
exec = rice-status net
|
||||
tail = true
|
||||
format-padding = 1
|
||||
click-left = rice-panel network
|
||||
|
||||
[module/bt]
|
||||
type = custom/script
|
||||
exec = rice-status bt
|
||||
tail = true
|
||||
format-padding = 1
|
||||
click-left = rice-panel bluetooth
|
||||
click-right = rice-bt power toggle
|
||||
|
||||
[module/vol]
|
||||
type = custom/script
|
||||
exec = rice-status vol
|
||||
tail = true
|
||||
format-padding = 1
|
||||
click-left = rice-panel audio
|
||||
click-right = rice-volume mute
|
||||
scroll-up = rice-volume up
|
||||
scroll-down = rice-volume down
|
||||
|
||||
[module/notif]
|
||||
type = custom/script
|
||||
exec = rice-status notif
|
||||
tail = true
|
||||
format-padding = 1
|
||||
click-left = rice-panel notifications
|
||||
click-middle = rice-notify dnd toggle
|
||||
click-right = rice-notify dismiss-all
|
||||
|
||||
[settings]
|
||||
screenchange-reload = true
|
||||
22
home/.config/rice/bash/aliases.bash
Normal file
22
home/.config/rice/bash/aliases.bash
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Sakura Line - aliases. Personal ones belong in ~/.bashrc.local.
|
||||
|
||||
if command -v eza >/dev/null; then
|
||||
alias ls='eza -lh --group-directories-first --icons=auto'
|
||||
alias lsa='ls -a'
|
||||
alias lt='eza --tree --level=2 --long --icons --git'
|
||||
alias lta='lt -a'
|
||||
fi
|
||||
|
||||
alias ..='cd ..'
|
||||
alias ...='cd ../..'
|
||||
alias ....='cd ../../..'
|
||||
|
||||
alias g='git'
|
||||
alias gs='git status -sb'
|
||||
alias gcm='git commit -m'
|
||||
alias gcam='git commit -a -m'
|
||||
alias gcad='git commit -a --amend'
|
||||
|
||||
alias d='docker'
|
||||
alias t='tmux attach || tmux new -s work'
|
||||
alias decompress='tar -xzf'
|
||||
18
home/.config/rice/bash/functions.bash
Normal file
18
home/.config/rice/bash/functions.bash
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Sakura Line - everyday functions.
|
||||
|
||||
# ff: fuzzy-find a file below the current directory, with a preview (pictures
|
||||
# show inline in st and kitty). eff opens the pick in $EDITOR.
|
||||
ff() { fzf --preview 'rice-preview {}' --preview-window 'right,55%' "$@"; }
|
||||
eff() { local f; f=$(ff) && [[ -n $f ]] && "$EDITOR" "$f"; }
|
||||
|
||||
# img <picture>...: show pictures right in the terminal.
|
||||
img() { local f; for f in "$@"; do rice-preview "$f"; done; }
|
||||
|
||||
# open <file|url>: open with the default app, detached from the terminal.
|
||||
open() { xdg-open "$@" >/dev/null 2>&1 & disown; }
|
||||
|
||||
# n: nvim, on the current directory when given nothing.
|
||||
n() { if (( $# == 0 )); then nvim .; else nvim "$@"; fi; }
|
||||
|
||||
# compress <file|dir>: make <name>.tar.gz next to it (decompress is tar -xzf).
|
||||
compress() { tar -czf "${1%/}.tar.gz" "${1%/}"; }
|
||||
24
home/.config/rice/bash/git.bash
Normal file
24
home/.config/rice/bash/git.bash
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Sakura Line - git worktree helpers.
|
||||
|
||||
# ga <branch>: new branch in its own worktree next to the repository
|
||||
# (../<repo>-<branch>), and cd into it.
|
||||
ga() {
|
||||
local branch="${1:-}" top main dir
|
||||
[[ -n $branch ]] || { echo "usage: ga <branch>"; return 1; }
|
||||
top=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "ga: not inside a git repository"; return 1; }
|
||||
main=$(dirname "$(git -C "$top" rev-parse --path-format=absolute --git-common-dir)")
|
||||
dir="$(dirname "$main")/$(basename "$main")-${branch//\//-}"
|
||||
git -C "$top" worktree add -b "$branch" "$dir" && cd "$dir"
|
||||
}
|
||||
|
||||
# gd: remove the current worktree and its branch (asks first; never the main one).
|
||||
gd() {
|
||||
local top main branch answer
|
||||
top=$(git rev-parse --show-toplevel 2>/dev/null) || { echo "gd: not inside a git repository"; return 1; }
|
||||
main=$(git worktree list --porcelain | awk '/^worktree / { print $2; exit }')
|
||||
[[ $top != "$main" ]] || { echo "gd: this is the main worktree; not removing it"; return 1; }
|
||||
branch=$(git branch --show-current)
|
||||
read -rp "Remove worktree $top and branch $branch? [y/N] " answer
|
||||
[[ $answer == [yY]* ]] || return 1
|
||||
cd "$main" && git worktree remove "$top" && git branch -D "$branch"
|
||||
}
|
||||
34
home/.config/rice/bash/rsync.bash
Normal file
34
home/.config/rice/bash/rsync.bash
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Sakura Line - rsync watchers.
|
||||
|
||||
# rsw <source> <destination>: keep destination in sync with source in the
|
||||
# background, re-syncing on every change. The destination can be remote
|
||||
# (host:path); one SSH connection is reused, so you authenticate once.
|
||||
rsw() {
|
||||
(( $# == 2 )) || { echo "usage: rsw <source> <destination>"; return 1; }
|
||||
local src="${1%/}" dest="$2" sockets="${XDG_RUNTIME_DIR:-$HOME/.ssh}"
|
||||
local rsh="ssh -o ControlMaster=auto -o ControlPath=$sockets/rsw-%r@%h:%p -o ControlPersist=yes"
|
||||
setsid --fork env RSYNC_RSH="$rsh" bash -c '
|
||||
rsync -a "$1/" "$2"
|
||||
while inotifywait -r -q -e modify,create,delete,move "$1" >/dev/null; do rsync -a "$1/" "$2"; done
|
||||
' rsw-watch "$src" "$dest" >/dev/null 2>&1
|
||||
echo "watching $src -> $dest"
|
||||
}
|
||||
|
||||
# lsw: list watchers. dsw: stop them all.
|
||||
lsw() {
|
||||
local pid cmd rest found=0
|
||||
while read -r pid cmd; do
|
||||
rest="${cmd##*rsw-watch }"
|
||||
echo "$pid: ${rest% *} -> ${rest##* }"
|
||||
found=1
|
||||
done < <(pgrep -af 'rsw-watch ')
|
||||
(( found )) || echo "no active watchers"
|
||||
}
|
||||
|
||||
dsw() {
|
||||
local pid found=0
|
||||
for pid in $(pgrep -f 'rsw-watch '); do
|
||||
kill -- -"$pid" 2>/dev/null && echo "stopped $pid" && found=1
|
||||
done
|
||||
(( found )) || echo "no active watchers"
|
||||
}
|
||||
82
home/.config/rice/bash/ssh.bash
Normal file
82
home/.config/rice/bash/ssh.bash
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Sakura Line - ssh helpers.
|
||||
|
||||
# ssh: when an interactive session drops, tidy the terminal and reconnect until
|
||||
# you press Ctrl-C. Only a real dropped shell reconnects: a remote command,
|
||||
# piped input, or a failure within the first 30 seconds (bad host, bad key)
|
||||
# returns normally, so nothing is ever replayed by accident.
|
||||
ssh() {
|
||||
local rc started=$SECONDS
|
||||
command ssh "$@"
|
||||
rc=$?
|
||||
[[ -t 1 ]] || return $rc
|
||||
_ssh_disarm
|
||||
|
||||
if (( rc != 255 )) || [[ ! -t 0 ]] || ! _ssh_interactive "$@" || (( SECONDS - started < 30 )); then
|
||||
return $rc
|
||||
fi
|
||||
|
||||
# A subshell, so Ctrl-C stops both the attempt and the loop.
|
||||
(
|
||||
while :; do
|
||||
echo "Connection lost. Reconnecting (Ctrl-C to stop)..."
|
||||
sleep 2
|
||||
command ssh "$@"
|
||||
rc=$?
|
||||
_ssh_disarm
|
||||
(( rc != 255 )) && exit $rc
|
||||
done
|
||||
)
|
||||
}
|
||||
|
||||
# A connection that dies mid-session leaves the remote tmux or editor's
|
||||
# terminal modes on (mouse reporting floods the prompt); switch them off.
|
||||
_ssh_disarm() {
|
||||
printf '\e[?1000l\e[?1002l\e[?1003l\e[?1006l\e[?1004l\e[?1049l\e[?25h'
|
||||
}
|
||||
|
||||
# True when the arguments open a shell: a destination and no remote command,
|
||||
# counting a RemoteCommand set in ssh_config.
|
||||
_ssh_interactive() {
|
||||
local value_opts="BbcDEeFIiJLlmOoPpQRSWw" argv=("$@") arg letters i dest="" opts_done=""
|
||||
while (( $# )); do
|
||||
arg="$1"; shift
|
||||
if [[ -z $opts_done && $arg == "--" ]]; then
|
||||
opts_done=1
|
||||
elif [[ -z $opts_done && $arg == -?* ]]; then
|
||||
letters="${arg#-}"
|
||||
for (( i = 0; i < ${#letters}; i++ )); do
|
||||
if [[ $value_opts == *"${letters:i:1}"* ]]; then
|
||||
(( i == ${#letters} - 1 )) && shift
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif [[ -z $dest ]]; then
|
||||
dest="$arg"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
[[ -n $dest ]] || return 1
|
||||
local resolved
|
||||
resolved=$(command ssh -G "${argv[@]}" 2>/dev/null) || return 1
|
||||
! grep -i '^remotecommand ' <<<"$resolved" | grep -qvi '^remotecommand none$'
|
||||
}
|
||||
|
||||
# fip <host> <port>...: forward remote ports to localhost (web dev against a remote box).
|
||||
fip() {
|
||||
(( $# >= 2 )) || { echo "usage: fip <host> <port>..."; return 1; }
|
||||
local host="$1" port; shift
|
||||
for port in "$@"; do
|
||||
command ssh -f -N -L "$port:localhost:$port" "$host" && echo "localhost:$port -> $host:$port"
|
||||
done
|
||||
}
|
||||
|
||||
# dip <port>...: stop those forwards. lip: list active forwards.
|
||||
dip() {
|
||||
(( $# >= 1 )) || { echo "usage: dip <port>..."; return 1; }
|
||||
local port
|
||||
for port in "$@"; do
|
||||
pkill -f "ssh.*-L $port:localhost:$port" && echo "stopped $port" || echo "no forward on $port"
|
||||
done
|
||||
}
|
||||
lip() { pgrep -af 'ssh.*-L [0-9]+:localhost:[0-9]+' || echo "no active forwards"; }
|
||||
22
home/.config/rofi/config.rasi
Normal file
22
home/.config/rofi/config.rasi
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* Sakura Line - rofi defaults. Scripts pick a layout with -theme. */
|
||||
configuration {
|
||||
modes: "drun,run,window";
|
||||
show-icons: true;
|
||||
icon-theme: "Papirus-Dark";
|
||||
drun-display-format: "{name}";
|
||||
matching: "fuzzy";
|
||||
sort: true;
|
||||
sorting-method: "fzf";
|
||||
hover-select: true;
|
||||
me-select-entry: "MousePrimary";
|
||||
me-accept-entry: "!MousePrimary";
|
||||
kb-cancel: "Escape,Control+g,Control+bracketleft";
|
||||
|
||||
/* Header titles, one per mode (rofi 2 ignores -display-<mode> for some modes). */
|
||||
drun { display-name: "Apps"; }
|
||||
run { display-name: "Run"; }
|
||||
window { display-name: "Windows"; }
|
||||
emoji { display-name: "Emoji"; }
|
||||
}
|
||||
|
||||
@theme "~/.config/rofi/launcher.rasi"
|
||||
36
home/.config/rofi/grid.rasi
Normal file
36
home/.config/rofi/grid.rasi
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* Image grid: wallpaper and theme pickers. */
|
||||
@import "~/.config/rofi/rice.rasi"
|
||||
|
||||
window {
|
||||
width: 980px;
|
||||
}
|
||||
|
||||
listview {
|
||||
columns: 4;
|
||||
lines: 2;
|
||||
fixed-height: true;
|
||||
flow: horizontal;
|
||||
padding: 14px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
element {
|
||||
orientation: vertical;
|
||||
padding: 10px;
|
||||
spacing: 8px;
|
||||
children: [ element-icon, element-text ];
|
||||
}
|
||||
|
||||
element-icon {
|
||||
size: 190px;
|
||||
horizontal-align: 0.5;
|
||||
}
|
||||
|
||||
element-text {
|
||||
horizontal-align: 0.5;
|
||||
text-color: @dim;
|
||||
}
|
||||
|
||||
element selected.normal element-text {
|
||||
text-color: @fg;
|
||||
}
|
||||
6
home/.config/rofi/launcher.rasi
Normal file
6
home/.config/rofi/launcher.rasi
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* App launcher (super + w). */
|
||||
@import "~/.config/rofi/rice.rasi"
|
||||
|
||||
window { width: 620px; }
|
||||
listview { lines: 9; fixed-height: true; }
|
||||
element-icon { size: 1.6em; }
|
||||
7
home/.config/rofi/menu.rasi
Normal file
7
home/.config/rofi/menu.rasi
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* Text menus: system menu, power, keybindings, clipboard, toggles.
|
||||
Entries carry their own Nerd Font glyph, so rofi's icon column is off. */
|
||||
@import "~/.config/rofi/rice.rasi"
|
||||
|
||||
window { width: 520px; }
|
||||
listview { lines: 12; }
|
||||
element-icon { enabled: false; }
|
||||
17
home/.config/rofi/prompt.rasi
Normal file
17
home/.config/rofi/prompt.rasi
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* Single text input: reminder text, event title, Wi-Fi password. */
|
||||
@import "~/.config/rofi/rice.rasi"
|
||||
|
||||
window { width: 520px; }
|
||||
mainbox { children: [ inputbar, message ]; }
|
||||
|
||||
inputbar {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
entry {
|
||||
placeholder: "";
|
||||
}
|
||||
|
||||
message {
|
||||
border: 1px 0px 0px 0px;
|
||||
}
|
||||
92
home/.config/rofi/rice.rasi
Normal file
92
home/.config/rofi/rice.rasi
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* Sakura Line - shared rofi layout. Every menu imports this, then overrides
|
||||
only size and position. Colours come from the rendered theme. */
|
||||
@import "~/.local/state/rice/theme/rofi-colors.rasi"
|
||||
|
||||
* {
|
||||
background-color: transparent;
|
||||
text-color: @fg;
|
||||
font: @font-main;
|
||||
}
|
||||
|
||||
window {
|
||||
transparency: "real";
|
||||
location: center;
|
||||
anchor: center;
|
||||
width: 560px;
|
||||
background-color: @bg-trans;
|
||||
border: 1px;
|
||||
border-color: @border-col;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
mainbox {
|
||||
spacing: 0px;
|
||||
children: [ inputbar, message, listview ];
|
||||
}
|
||||
|
||||
inputbar {
|
||||
padding: 14px 18px;
|
||||
spacing: 0px;
|
||||
border: 0px 0px 1px 0px;
|
||||
border-color: @border-col;
|
||||
children: [ prompt, entry ];
|
||||
}
|
||||
|
||||
prompt {
|
||||
text-color: @fg;
|
||||
padding: 0px 10px 0px 0px;
|
||||
vertical-align: 0.5;
|
||||
font: @font-bold;
|
||||
}
|
||||
|
||||
entry {
|
||||
placeholder: "Search";
|
||||
placeholder-color: @dim;
|
||||
cursor: text;
|
||||
vertical-align: 0.5;
|
||||
font: @font-entry;
|
||||
}
|
||||
|
||||
message {
|
||||
padding: 10px 18px;
|
||||
border: 0px 0px 1px 0px;
|
||||
border-color: @border-col;
|
||||
}
|
||||
|
||||
textbox {
|
||||
text-color: @dim;
|
||||
markup: true;
|
||||
}
|
||||
|
||||
listview {
|
||||
lines: 10;
|
||||
columns: 1;
|
||||
fixed-height: false;
|
||||
scrollbar: false;
|
||||
padding: 8px;
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
element {
|
||||
padding: 8px 10px;
|
||||
spacing: 10px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
element normal.active, element alternate.active { text-color: @accent; }
|
||||
element normal.urgent, element alternate.urgent { text-color: @urgent; }
|
||||
element selected.normal { background-color: @select-bg; text-color: @select-fg; }
|
||||
element selected.active { background-color: @select-bg; text-color: @accent; }
|
||||
element selected.urgent { background-color: @select-bg; text-color: @urgent; }
|
||||
|
||||
element-icon {
|
||||
size: 1.3em;
|
||||
vertical-align: 0.5;
|
||||
}
|
||||
|
||||
element-text {
|
||||
text-color: inherit;
|
||||
vertical-align: 0.5;
|
||||
}
|
||||
|
||||
mode-switcher { enabled: false; }
|
||||
244
home/.config/sxhkd/sxhkdrc
Normal file
244
home/.config/sxhkd/sxhkdrc
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
# Sakura Line - keybindings.
|
||||
# `rice-keys` builds the cheat sheet (super + k) from this file:
|
||||
# a "## Section" line starts a group, a "# text" line right above a
|
||||
# hotkey is its description. Keep that shape when adding bindings.
|
||||
|
||||
## Apps
|
||||
|
||||
# Terminal
|
||||
super + Return
|
||||
st
|
||||
|
||||
# Drop-down terminal (scratchpad)
|
||||
super + grave
|
||||
rice-scratchpad
|
||||
|
||||
# App launcher
|
||||
super + w
|
||||
rofi -show drun
|
||||
|
||||
# Rice menu
|
||||
super + m
|
||||
rice-menu main
|
||||
|
||||
# Keybinding cheat sheet
|
||||
super + k
|
||||
rice-keys
|
||||
|
||||
# Emoji picker
|
||||
super + ctrl + e
|
||||
rice-emoji
|
||||
|
||||
# Clipboard history
|
||||
super + ctrl + v
|
||||
rice-clipboard
|
||||
|
||||
## Panels
|
||||
|
||||
# Control centre
|
||||
super + a
|
||||
rice-panel hub
|
||||
|
||||
# Audio / network / bluetooth / display / power / media panel
|
||||
super + ctrl + {a,w,b,d,p,m}
|
||||
rice-panel {audio,network,bluetooth,display,power,media}
|
||||
|
||||
# Calendar and reminders
|
||||
super + ctrl + c
|
||||
rice-panel calendar
|
||||
|
||||
# Notification centre
|
||||
super + n
|
||||
rice-panel notifications
|
||||
|
||||
# Toggle the bar
|
||||
super + b
|
||||
rice-bar toggle
|
||||
|
||||
## Notifications
|
||||
|
||||
# Dismiss the newest notification
|
||||
super + comma
|
||||
rice-notify dismiss
|
||||
|
||||
# Dismiss all notifications
|
||||
super + shift + comma
|
||||
rice-notify dismiss-all
|
||||
|
||||
# Do not disturb on/off
|
||||
super + ctrl + comma
|
||||
rice-notify dnd toggle
|
||||
|
||||
# Run the newest notification's action
|
||||
super + alt + comma
|
||||
rice-notify action
|
||||
|
||||
# Bring back the last dismissed notification
|
||||
super + shift + alt + comma
|
||||
rice-notify restore
|
||||
|
||||
## Reminders
|
||||
|
||||
# New reminder
|
||||
super + ctrl + r
|
||||
rice-reminder new
|
||||
|
||||
# Show upcoming reminders
|
||||
super + ctrl + alt + r
|
||||
rice-reminder list
|
||||
|
||||
## Capture
|
||||
|
||||
# Screenshot a region (flameshot)
|
||||
Print
|
||||
rice-capture region
|
||||
|
||||
# Screenshot the whole screen
|
||||
shift + Print
|
||||
rice-capture screen
|
||||
|
||||
# Copy text from a region (OCR)
|
||||
ctrl + Print
|
||||
rice-capture text
|
||||
|
||||
# Start or stop screen recording
|
||||
alt + Print
|
||||
rice-capture record
|
||||
|
||||
# Pick a colour
|
||||
super + shift + Print
|
||||
rice-capture color
|
||||
|
||||
# Capture menu: QR, webcam overlay, transcode and the rest
|
||||
super + Print
|
||||
rice-menu capture
|
||||
|
||||
# Screenshot a region (no PrtSc key)
|
||||
super + alt + p
|
||||
rice-capture region
|
||||
|
||||
# Screenshot the whole screen (no PrtSc key)
|
||||
super + alt + shift + p
|
||||
rice-capture screen
|
||||
|
||||
# Copy text from a region (no PrtSc key)
|
||||
super + alt + t
|
||||
rice-capture text
|
||||
|
||||
# Start or stop screen recording (no PrtSc key)
|
||||
super + alt + v
|
||||
rice-capture record
|
||||
|
||||
# Pick a colour (no PrtSc key)
|
||||
super + alt + c
|
||||
rice-capture color
|
||||
|
||||
# Capture menu (no PrtSc key)
|
||||
super + alt + s
|
||||
rice-menu capture
|
||||
|
||||
# Zoom into the screen
|
||||
super + ctrl + z
|
||||
rice-zoom
|
||||
|
||||
# Dictation on/off
|
||||
super + ctrl + x
|
||||
rice-dictate toggle
|
||||
|
||||
## Clipboard
|
||||
|
||||
# Copy (works in terminals too)
|
||||
super + @c
|
||||
rice-clip copy
|
||||
|
||||
# Paste (works in terminals too)
|
||||
super + @v
|
||||
rice-clip paste
|
||||
|
||||
## Style
|
||||
|
||||
# Wallpaper picker
|
||||
super + ctrl + space
|
||||
rice-menu wallpaper
|
||||
|
||||
# Theme picker
|
||||
super + shift + ctrl + space
|
||||
rice-menu theme
|
||||
|
||||
# Quick toggles
|
||||
super + ctrl + o
|
||||
rice-menu toggles
|
||||
|
||||
## Session
|
||||
|
||||
# Power menu
|
||||
super + Escape
|
||||
rice-menu power
|
||||
|
||||
# Lock
|
||||
super + ctrl + l
|
||||
rice-lock
|
||||
|
||||
# Stay awake on/off
|
||||
super + ctrl + i
|
||||
rice-idle toggle
|
||||
|
||||
# Reload keybindings
|
||||
super + alt + Escape
|
||||
pkill -USR1 -x sxhkd && rice-notify send "Keybindings" "Reloaded"
|
||||
|
||||
# Restart bspwm
|
||||
super + alt + r
|
||||
bspc wm -r
|
||||
|
||||
# Quit bspwm
|
||||
super + Delete
|
||||
bspc quit
|
||||
|
||||
## Windows
|
||||
|
||||
# Close window
|
||||
super + q
|
||||
bspc node -c
|
||||
|
||||
# Float / tile
|
||||
super + space
|
||||
bspc node -t "~"{floating,tiled}
|
||||
|
||||
# Fullscreen
|
||||
super + f
|
||||
bspc node -t \~fullscreen
|
||||
|
||||
# Focus / swap window in a direction
|
||||
super + {_,shift + }{Left,Down,Up,Right}
|
||||
bspc node -{f,s} {west,south,north,east}
|
||||
|
||||
# Previous window
|
||||
alt + Tab
|
||||
bspc node -f last
|
||||
|
||||
# Go to desktop / send window to desktop
|
||||
super + {_,shift + }{1-6}
|
||||
bspc {desktop -f,node -d} '^{1-6}'
|
||||
|
||||
# Previous / next desktop
|
||||
super + ctrl + {Left,Right}
|
||||
bspc desktop -f {prev,next}.local
|
||||
|
||||
## Media
|
||||
|
||||
# Volume up / down / mute
|
||||
XF86Audio{RaiseVolume,LowerVolume,Mute}
|
||||
rice-volume {up,down,mute}
|
||||
|
||||
# Microphone mute
|
||||
XF86AudioMicMute
|
||||
rice-volume mic-mute
|
||||
|
||||
# Play-pause / next / previous / stop
|
||||
XF86Audio{Play,Pause,Next,Prev,Stop}
|
||||
playerctl {play-pause,play-pause,next,previous,stop}
|
||||
|
||||
# Play-pause / next / previous without media keys
|
||||
ctrl + alt + {c,x,z}
|
||||
playerctl {play-pause,next,previous}
|
||||
24
home/.inputrc
Normal file
24
home/.inputrc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Sakura Line - readline.
|
||||
$include /etc/inputrc
|
||||
|
||||
set completion-ignore-case on
|
||||
set completion-prefix-display-length 2
|
||||
set show-all-if-ambiguous on
|
||||
set show-all-if-unmodified on
|
||||
set menu-complete-display-prefix on
|
||||
set mark-symlinked-directories on
|
||||
set match-hidden-files off
|
||||
set page-completions off
|
||||
set completion-query-items 200
|
||||
set visible-stats on
|
||||
set colored-stats on
|
||||
set colored-completion-prefix on
|
||||
set skip-completed-text on
|
||||
|
||||
# Up and Down search the history for what is already typed.
|
||||
"\e[A": history-search-backward
|
||||
"\e[B": history-search-forward
|
||||
|
||||
# Tab cycles through completions; Shift+Tab goes back.
|
||||
TAB: menu-complete
|
||||
"\e[Z": menu-complete-backward
|
||||
11
home/.xinitrc
Normal file
11
home/.xinitrc
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
# Sakura Line - X session: pick up the system's session setup (D-Bus and the
|
||||
# systemd user environment), then hand over to bspwm.
|
||||
if [ -d /etc/X11/xinit/xinitrc.d ]; then
|
||||
for f in /etc/X11/xinit/xinitrc.d/?*.sh; do
|
||||
[ -x "$f" ] && . "$f"
|
||||
done
|
||||
unset f
|
||||
fi
|
||||
|
||||
exec bspwm
|
||||
285
install.sh
Executable file
285
install.sh
Executable file
|
|
@ -0,0 +1,285 @@
|
|||
#!/usr/bin/env bash
|
||||
# Sakura Line - install the rice on Arch Linux.
|
||||
#
|
||||
# curl -fsSL https://rice.srtk.in/install.sh | bash
|
||||
# ~/rice/install.sh full install (safe to run again)
|
||||
# ~/rice/install.sh --update pull, re-link, re-render, rebuild what changed; asks nothing
|
||||
# ~/rice/install.sh --dry-run show what would happen, change nothing
|
||||
# ~/rice/install.sh --skip-packages everything is installed already; no pacman
|
||||
#
|
||||
# Installs the packages (pacman, plus paru for the AUR), links every file in
|
||||
# home/ into $HOME and every script in bin/ into ~/.local/bin, renders the
|
||||
# theme, builds st with sixel images, and offers calendar sync and a boot
|
||||
# splash. Anything it would replace is moved to ~/.rice-backup-<date>/ first.
|
||||
set -uo pipefail
|
||||
|
||||
RICE_REMOTE="${RICE_REMOTE:-https://git.srtk.in/sarthak/dotfiles.git}"
|
||||
RICE_HOME="${RICE_HOME:-$HOME/rice}"
|
||||
STATE="${XDG_STATE_HOME:-$HOME/.local/state}/rice"
|
||||
BACKUP="$HOME/.rice-backup-$(date +%Y%m%d-%H%M%S)"
|
||||
DRY=0
|
||||
UPDATE=0
|
||||
SKIP_PACKAGES=0
|
||||
FAILED=()
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--update) UPDATE=1 ;;
|
||||
--dry-run|-n) DRY=1 ;;
|
||||
--skip-packages) SKIP_PACKAGES=1 ;;
|
||||
-h|--help) sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "unknown option: $arg (try --help)"; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
c_hd=$'\e[1;38;2;146;185;234m'; c_ok=$'\e[38;2;143;174;116m'; c_wa=$'\e[38;2;216;168;124m'
|
||||
c_er=$'\e[38;2;207;106;128m'; c_dim=$'\e[38;2;127;119;150m'; c_off=$'\e[0m'
|
||||
say() { printf '\n%s>>%s %s\n' "$c_hd" "$c_off" "$*"; }
|
||||
ok() { printf ' %s+%s %s\n' "$c_ok" "$c_off" "$*"; }
|
||||
warn() { printf ' %s!%s %s\n' "$c_wa" "$c_off" "$*"; }
|
||||
fail() { printf ' %sx%s %s\n' "$c_er" "$c_off" "$*"; FAILED+=("$*"); }
|
||||
run() { if (( DRY )); then printf ' %s$ %s%s\n' "$c_dim" "$*" "$c_off"; else "$@"; fi; }
|
||||
ask() { (( UPDATE )) && return 1; local a=""; { read -rp " $1 [y/N] " a </dev/tty; } 2>/dev/null || return 1; [[ $a == [yY]* ]]; }
|
||||
|
||||
(( $(id -u) != 0 )) || { echo "Run this as your normal user; it asks for sudo when it needs it."; exit 1; }
|
||||
command -v pacman >/dev/null || { echo "This rice installs on Arch Linux (pacman not found)."; exit 1; }
|
||||
|
||||
# ------------------------------------------------------------ find the repo
|
||||
# Piped from curl there is no checkout yet: clone it, then run its own copy.
|
||||
self_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd)"
|
||||
if [[ -f $self_dir/packages/pacman.txt && -d $self_dir/home ]]; then
|
||||
RICE_DIR="$self_dir"
|
||||
else
|
||||
command -v git >/dev/null || sudo pacman -S --needed --noconfirm git
|
||||
if [[ -d $RICE_HOME/.git ]]; then
|
||||
git -C "$RICE_HOME" pull --ff-only
|
||||
else
|
||||
git clone "$RICE_REMOTE" "$RICE_HOME"
|
||||
fi
|
||||
exec bash "$RICE_HOME/install.sh" "$@" </dev/tty
|
||||
fi
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
# ---------------------------------------------------------------- steps
|
||||
update_repo() {
|
||||
(( UPDATE )) || return 0
|
||||
say "updating the rice"
|
||||
if [[ -n $(git -C "$RICE_DIR" status --porcelain) ]]; then
|
||||
warn "you have local changes in $RICE_DIR, so it was not pulled"
|
||||
else
|
||||
run git -C "$RICE_DIR" pull --ff-only || fail "git pull"
|
||||
fi
|
||||
}
|
||||
|
||||
packages() {
|
||||
say "packages"
|
||||
local repo aur helper tmp
|
||||
mapfile -t repo < <(sed 's/#.*//' "$RICE_DIR/packages/pacman.txt" | awk 'NF { print $1 }')
|
||||
mapfile -t aur < <(sed 's/#.*//' "$RICE_DIR/packages/aur.txt" | awk 'NF { print $1 }')
|
||||
|
||||
run sudo pacman -Syu --needed --noconfirm "${repo[@]}" || fail "pacman could not install everything"
|
||||
|
||||
helper=$(command -v paru || command -v yay || true)
|
||||
if [[ -z $helper ]]; then
|
||||
warn "no AUR helper; building paru"
|
||||
tmp=$(mktemp -d)
|
||||
if run git clone --depth 1 https://aur.archlinux.org/paru-bin.git "$tmp/paru-bin" &&
|
||||
(cd "$tmp/paru-bin" && run makepkg -si --noconfirm); then
|
||||
helper=$(command -v paru || true)
|
||||
else
|
||||
fail "could not build paru; AUR packages skipped"
|
||||
fi
|
||||
rm -rf "$tmp"
|
||||
fi
|
||||
[[ -n $helper ]] && { run "$helper" -S --needed --noconfirm "${aur[@]}" || fail "some AUR packages failed"; }
|
||||
ok "packages done"
|
||||
}
|
||||
|
||||
# link_one <source> <target>: symlink, moving whatever was there into the backup.
|
||||
link_one() {
|
||||
local src="$1" dst="$2" rel
|
||||
[[ -L $dst && $(readlink "$dst") == "$src" ]] && return 0
|
||||
if [[ -e $dst || -L $dst ]]; then
|
||||
rel="${dst#"$HOME"/}"
|
||||
run mkdir -p "$BACKUP/$(dirname "$rel")"
|
||||
run mv "$dst" "$BACKUP/$rel"
|
||||
warn "moved your ~/$rel to the backup"
|
||||
fi
|
||||
run mkdir -p "$(dirname "$dst")"
|
||||
run ln -s "$src" "$dst"
|
||||
}
|
||||
|
||||
# A previous dotfiles setup kept its own installer in ~/.config/rice, which is
|
||||
# where this rice keeps your hooks and local settings. Move those files aside.
|
||||
legacy_rice() {
|
||||
local old="$HOME/.config/rice" f moved=0
|
||||
for f in install.sh README.md packages.txt screenshot.webp particles st; do
|
||||
[[ -e $old/$f && ! -L $old/$f ]] || continue
|
||||
run mkdir -p "$BACKUP/.config/rice"
|
||||
run mv "$old/$f" "$BACKUP/.config/rice/$f"
|
||||
moved=1
|
||||
done
|
||||
(( moved )) && warn "moved the previous rice installer out of ~/.config/rice into the backup"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Swap the running session over: stop the old bar, notifications, compositor
|
||||
# and key daemon, start the rice's, and re-run bspwmrc. Windows stay open.
|
||||
apply_now() {
|
||||
[[ -n ${DISPLAY:-} ]] && pgrep -x bspwm >/dev/null || return 1
|
||||
ask "Apply it to this session now? The bar, notifications, picom and sxhkd restart; windows stay open." || return 1
|
||||
say "applying to this session"
|
||||
run rice-session restart && run bspc wm -r && ok "applied" || fail "could not restart the session daemons"
|
||||
}
|
||||
|
||||
link_home() {
|
||||
say "linking configs and scripts"
|
||||
local f t="$STATE/theme"
|
||||
# bspwm runs bspwmrc as a program; without the executable bit it silently
|
||||
# starts with no settings, rules or session.
|
||||
run chmod +x "$RICE_DIR/home/.config/bspwm/bspwmrc" "$RICE_DIR"/bin/*
|
||||
while IFS= read -r -d '' f; do
|
||||
link_one "$f" "$HOME/${f#"$RICE_DIR"/home/}"
|
||||
done < <(find "$RICE_DIR/home" \( -type f -o -type l \) -print0)
|
||||
for f in "$RICE_DIR"/bin/*; do
|
||||
link_one "$f" "$HOME/.local/bin/$(basename "$f")"
|
||||
done
|
||||
# These point at the rendered theme, which rice-theme rewrites on every change.
|
||||
link_one "$t/dunst.conf" "$HOME/.config/dunst/dunstrc.d/50-theme.conf"
|
||||
link_one "$t/eww-colors.scss" "$HOME/.config/eww/colors.scss"
|
||||
link_one "$t/gtk.css" "$HOME/.config/gtk-3.0/gtk.css"
|
||||
link_one "$t/gtk.css" "$HOME/.config/gtk-4.0/gtk.css"
|
||||
link_one "$t/btop.theme" "$HOME/.config/btop/themes/rice.theme"
|
||||
link_one "$t/imv-config" "$HOME/.config/imv/config"
|
||||
[[ -d $BACKUP ]] && warn "replaced files are in $BACKUP"
|
||||
ok "linked"
|
||||
}
|
||||
|
||||
local_files() {
|
||||
say "your own settings"
|
||||
local hooks="$HOME/.config/rice/hooks" e
|
||||
for e in post-boot theme-set font-set wallpaper-set lock unlock update; do run mkdir -p "$hooks/$e.d"; done
|
||||
if [[ ! -f $HOME/.bashrc.local ]] && (( ! DRY )); then
|
||||
printf '%s\n' "# Personal shell settings: aliases, exports, SDK paths. Not part of the rice repo." > "$HOME/.bashrc.local"
|
||||
ok "created ~/.bashrc.local"
|
||||
fi
|
||||
if [[ ! -f $HOME/.config/rice/local.sh ]] && (( ! DRY )); then
|
||||
printf '%s\n' "# Machine-specific bspwm extras, sourced by bspwmrc: apps to autostart," \
|
||||
"# per-app desktop rules, monitor layouts. Not part of the rice repo." > "$HOME/.config/rice/local.sh"
|
||||
ok "created ~/.config/rice/local.sh"
|
||||
fi
|
||||
ok "hooks live in ~/.config/rice/hooks/<event>.d/"
|
||||
}
|
||||
|
||||
theme() {
|
||||
say "theme"
|
||||
local name; name=$(rice-theme current)
|
||||
if [[ -n ${DISPLAY:-} ]] && pgrep -x bspwm >/dev/null; then
|
||||
run rice-theme reload && ok "re-rendered $name and reloaded running apps" || fail "theme reload"
|
||||
else
|
||||
run rice-theme render "$name" && ok "rendered $name" || fail "theme render"
|
||||
fi
|
||||
}
|
||||
|
||||
build_st() {
|
||||
say "st, with sixel images"
|
||||
local stamp="$STATE/st.built" want
|
||||
want=$(cat "$RICE_DIR"/st/build.sh "$RICE_DIR"/st/patches.list "$RICE_DIR"/st/config.sed | sha1sum | cut -c1-16)
|
||||
if [[ -f $stamp && $(cat "$stamp") == "$want" ]] && command -v st >/dev/null; then
|
||||
ok "st is up to date"; return
|
||||
fi
|
||||
if run bash "$RICE_DIR/st/build.sh" --install; then
|
||||
(( DRY )) || { mkdir -p "$STATE"; echo "$want" > "$stamp"; }
|
||||
ok "st installed"
|
||||
else
|
||||
fail "st build"
|
||||
fi
|
||||
}
|
||||
|
||||
build_particles() {
|
||||
say "particles wallpaper layer"
|
||||
if run make -s -C "$RICE_DIR/particles" && run sudo make -s -C "$RICE_DIR/particles" install; then
|
||||
ok "particles installed"
|
||||
else
|
||||
fail "particles build"
|
||||
fi
|
||||
}
|
||||
|
||||
system_setup() {
|
||||
say "services and hardware"
|
||||
run sudo systemctl enable --now NetworkManager.service bluetooth.service power-profiles-daemon.service ||
|
||||
warn "some services did not start (normal without that hardware)"
|
||||
if [[ ! -f /etc/modules-load.d/i2c-dev.conf ]]; then
|
||||
run sudo sh -c 'echo i2c-dev > /etc/modules-load.d/i2c-dev.conf' && run sudo modprobe i2c-dev &&
|
||||
ok "i2c-dev loads at boot (monitor brightness over DDC)"
|
||||
fi
|
||||
if getent group i2c >/dev/null && ! id -nG | grep -qw i2c; then
|
||||
run sudo usermod -aG i2c "$USER" && warn "added you to the i2c group; it applies at your next login"
|
||||
fi
|
||||
}
|
||||
|
||||
calendar() {
|
||||
say "calendar"
|
||||
if [[ -f ${XDG_CONFIG_HOME:-$HOME/.config}/khal/config ]]; then ok "already set up"; return; fi
|
||||
if ask "Sync the calendar with a CalDAV server (Baikal, Nextcloud, Fastmail, ...)?"; then
|
||||
run rice-cal setup || fail "calendar setup"
|
||||
else
|
||||
warn "skipped; run 'rice-cal setup' whenever you like"
|
||||
fi
|
||||
}
|
||||
|
||||
boot_splash() {
|
||||
say "boot splash"
|
||||
if grep -Eq '^HOOKS=.*\bplymouth\b' /etc/mkinitcpio.conf 2>/dev/null; then ok "plymouth is already in the initramfs"; return; fi
|
||||
ask "Show Plymouth's stock splash at boot? This edits mkinitcpio.conf and the kernel options." || { warn "skipped"; return; }
|
||||
|
||||
run sudo cp /etc/mkinitcpio.conf /etc/mkinitcpio.conf.rice-backup
|
||||
run sudo sed -i -E '/^HOOKS=/ s/\b(systemd|udev)\b/\1 plymouth/' /etc/mkinitcpio.conf
|
||||
|
||||
if [[ -f /etc/default/grub ]]; then
|
||||
run sudo cp /etc/default/grub /etc/default/grub.rice-backup
|
||||
grep -q 'splash' /etc/default/grub ||
|
||||
run sudo sed -i -E 's/^(GRUB_CMDLINE_LINUX_DEFAULT="[^"]*)"/\1 quiet splash"/' /etc/default/grub
|
||||
run sudo grub-mkconfig -o /boot/grub/grub.cfg >/dev/null || fail "grub-mkconfig"
|
||||
elif compgen -G "/boot/loader/entries/*.conf" >/dev/null; then
|
||||
local entry
|
||||
for entry in /boot/loader/entries/*.conf; do
|
||||
grep -q splash "$entry" || run sudo sed -i -E '/^options / s/$/ quiet splash/' "$entry"
|
||||
done
|
||||
else
|
||||
warn "add 'quiet splash' to your kernel command line by hand"
|
||||
fi
|
||||
run sudo mkinitcpio -P >/dev/null && ok "plymouth enabled from the next boot" || fail "mkinitcpio"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------- main
|
||||
printf '\n%s Sakura Line%s rice installer · %s\n' "$c_hd" "$c_off" "$RICE_DIR"
|
||||
(( DRY )) && warn "dry run: nothing will be changed"
|
||||
|
||||
update_repo
|
||||
(( SKIP_PACKAGES )) || packages
|
||||
legacy_rice
|
||||
link_home
|
||||
local_files
|
||||
theme
|
||||
build_st
|
||||
build_particles
|
||||
system_setup
|
||||
calendar
|
||||
boot_splash
|
||||
(( UPDATE )) && run rice-hook update
|
||||
|
||||
printf '\n'
|
||||
if (( ${#FAILED[@]} == 0 )); then
|
||||
say "done"
|
||||
else
|
||||
say "finished with ${#FAILED[@]} problem(s):"
|
||||
for f in "${FAILED[@]}"; do printf ' %s- %s%s\n' "$c_er" "$f" "$c_off"; done
|
||||
fi
|
||||
if apply_now; then
|
||||
printf '\n %ssuper + k%s lists every shortcut.\n\n' "$c_hd" "$c_off"
|
||||
elif [[ -n ${DISPLAY:-} ]] && pgrep -x bspwm >/dev/null; then
|
||||
printf ' To switch this session over later: %srice-session restart && bspc wm -r%s\n\n' "$c_hd" "$c_off"
|
||||
else
|
||||
printf ' Log in on tty1 and the desktop starts; %ssuper + k%s lists every shortcut.\n\n' "$c_hd" "$c_off"
|
||||
fi
|
||||
14
packages/aur.txt
Normal file
14
packages/aur.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Sakura Line - AUR packages (installed with paru or yay).
|
||||
# One package per line; text after # is ignored.
|
||||
|
||||
eww # popups: calendar, control panels, notification centre
|
||||
xidlehook # idle chain that skips fullscreen video and playing audio
|
||||
tdrop # drop-down scratchpad terminal
|
||||
boomer-git # screen zoom
|
||||
rofi-greenclip # clipboard history
|
||||
zscroll-git # scrolling media title
|
||||
nerd-dictation-git # offline voice dictation (vosk)
|
||||
bibata-cursor-theme
|
||||
cbonsai
|
||||
pipes.sh
|
||||
tty-clock
|
||||
126
packages/pacman.txt
Normal file
126
packages/pacman.txt
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Sakura Line - official repo packages (pacman -S --needed).
|
||||
# One package per line; text after # is ignored.
|
||||
|
||||
# --- X session / window manager ---
|
||||
xorg-server
|
||||
xorg-xinit
|
||||
xorg-xrandr
|
||||
xorg-xprop
|
||||
xorg-xrdb
|
||||
xorg-xset
|
||||
xorg-xsetroot
|
||||
xorg-xwininfo
|
||||
bspwm
|
||||
sxhkd
|
||||
polybar
|
||||
picom
|
||||
rofi
|
||||
rofi-emoji
|
||||
dunst
|
||||
libnotify
|
||||
feh
|
||||
xdotool
|
||||
xclip
|
||||
xsettingsd
|
||||
wmname
|
||||
polkit-gnome
|
||||
xdg-utils
|
||||
|
||||
# --- build toolchain (st, particles) ---
|
||||
base-devel
|
||||
git
|
||||
pkgconf
|
||||
libx11
|
||||
libxext
|
||||
libxft
|
||||
libxrandr
|
||||
libxfixes
|
||||
libxinerama
|
||||
cairo
|
||||
harfbuzz
|
||||
fontconfig
|
||||
freetype2
|
||||
gd
|
||||
imlib2
|
||||
|
||||
# --- fonts / theming ---
|
||||
ttf-jetbrains-mono-nerd
|
||||
noto-fonts
|
||||
noto-fonts-cjk
|
||||
noto-fonts-emoji
|
||||
papirus-icon-theme
|
||||
gnome-themes-extra
|
||||
qt6ct
|
||||
|
||||
# --- panels: audio, network, bluetooth, display, power, media ---
|
||||
pipewire
|
||||
pipewire-pulse
|
||||
wireplumber
|
||||
pamixer
|
||||
playerctl
|
||||
networkmanager
|
||||
bluez
|
||||
bluez-utils
|
||||
ddcutil
|
||||
power-profiles-daemon
|
||||
lm_sensors
|
||||
qrencode
|
||||
|
||||
# --- calendar + reminders (Baikal CalDAV) ---
|
||||
khal
|
||||
vdirsyncer
|
||||
|
||||
# --- idle, screensaver, lock ---
|
||||
xss-lock
|
||||
xsecurelock
|
||||
xscreensaver
|
||||
|
||||
# --- capture: screenshot, recording, OCR, QR, colour, transcode ---
|
||||
flameshot
|
||||
maim
|
||||
slop
|
||||
gpu-screen-recorder
|
||||
ffmpeg
|
||||
imagemagick
|
||||
mpv
|
||||
v4l-utils
|
||||
tesseract
|
||||
tesseract-data-eng
|
||||
zbar
|
||||
xcolor
|
||||
|
||||
# --- shell ---
|
||||
bash-completion
|
||||
starship
|
||||
fzf
|
||||
eza
|
||||
bat
|
||||
zoxide
|
||||
fd
|
||||
ripgrep
|
||||
tldr
|
||||
jq
|
||||
inotify-tools
|
||||
socat
|
||||
|
||||
# --- terminal images ---
|
||||
chafa
|
||||
ueberzugpp
|
||||
yazi
|
||||
imv
|
||||
|
||||
# --- boot splash (stock Arch theme, no branding) ---
|
||||
plymouth
|
||||
|
||||
# --- apps ---
|
||||
kitty
|
||||
neovim
|
||||
thunar
|
||||
fastfetch
|
||||
btop
|
||||
cava
|
||||
|
||||
# --- terminal toys ---
|
||||
cmatrix
|
||||
asciiquarium
|
||||
onefetch
|
||||
21
particles/LICENSE
Normal file
21
particles/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
23
particles/Makefile
Normal file
23
particles/Makefile
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
|
||||
PKGS := cairo x11 xext xfixes xrender xrandr
|
||||
CFLAGS ?= -O2 -Wall -Wextra
|
||||
CFLAGS += $(shell pkg-config --cflags $(PKGS))
|
||||
LDLIBS := $(shell pkg-config --libs $(PKGS)) -lm
|
||||
|
||||
all: particles
|
||||
|
||||
particles: particles_wall.c
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LDLIBS)
|
||||
|
||||
install: particles
|
||||
install -Dm755 particles $(DESTDIR)$(BINDIR)/particles
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(BINDIR)/particles
|
||||
|
||||
clean:
|
||||
rm -f particles
|
||||
|
||||
.PHONY: all install uninstall clean
|
||||
1744
particles/particles_wall.c
Normal file
1744
particles/particles_wall.c
Normal file
File diff suppressed because it is too large
Load diff
61
st/build.sh
Executable file
61
st/build.sh
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env bash
|
||||
# st/build.sh - build st from st-flexipatch with the rice's patches and settings.
|
||||
#
|
||||
# st/build.sh [--install] [--src DIR]
|
||||
#
|
||||
# Clones st-flexipatch (pinned below) into ~/.local/src/st-flexipatch, or reuses
|
||||
# --src, turns on the patches in st/patches.list, applies st/config.sed to
|
||||
# upstream's config.def.h, enables the libraries those patches need in
|
||||
# config.mk, and runs make. --install then runs `sudo make install`.
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)"
|
||||
repo="https://github.com/bakkeby/st-flexipatch"
|
||||
commit="aa56259" # pinned: bump on purpose, after checking patches.list and config.sed still apply
|
||||
src="${ST_SRC:-$HOME/.local/src/st-flexipatch}"
|
||||
install=0
|
||||
|
||||
while (( $# )); do
|
||||
case "$1" in
|
||||
--install) install=1 ;;
|
||||
--src) src="${2:?--src needs a directory}"; shift ;;
|
||||
*) echo "usage: st/build.sh [--install] [--src DIR]" >&2; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ ! -d $src/.git ]]; then
|
||||
mkdir -p "$(dirname "$src")"
|
||||
git clone --quiet "$repo" "$src"
|
||||
fi
|
||||
if [[ $(git -C "$src" rev-parse --is-shallow-repository) == true ]]; then
|
||||
git -C "$src" fetch --quiet --unshallow origin
|
||||
fi
|
||||
git -C "$src" checkout --quiet --force "$commit"
|
||||
|
||||
cd "$src"
|
||||
|
||||
# patches.h: upstream defaults with our list switched on.
|
||||
enabled=$(sed 's/#.*//' "$here/patches.list" | awk 'NF { print $1 }')
|
||||
cp patches.def.h patches.h
|
||||
for p in $enabled; do
|
||||
grep -q "^#define ${p}_PATCH " patches.h || { echo "st/build.sh: no patch called ${p}_PATCH" >&2; exit 1; }
|
||||
sed -i "s/^#define ${p}_PATCH 0$/#define ${p}_PATCH 1/" patches.h
|
||||
done
|
||||
|
||||
# config.h: upstream defaults plus our values and shortcuts.
|
||||
sed -f "$here/config.sed" config.def.h > config.h
|
||||
|
||||
# config.mk: link the libraries the enabled patches need.
|
||||
has() { grep -qx "$1" <<<"$enabled"; }
|
||||
has ALPHA && sed -i -E 's/^#(XRENDER = )/\1/' config.mk
|
||||
has LIGATURES && sed -i -E 's/^#(LIGATURES_(C|H|INC|LIBS) = )/\1/' config.mk
|
||||
has SIXEL && sed -i -E 's/^#(SIXEL_(C|LIBS) = )/\1/' config.mk
|
||||
|
||||
make clean >/dev/null
|
||||
make -j"$(nproc)"
|
||||
|
||||
if (( install )); then
|
||||
sudo make install
|
||||
fi
|
||||
echo "st built in $src"
|
||||
24
st/config.sed
Normal file
24
st/config.sed
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# st/config.sed - the rice's changes to st-flexipatch's config.def.h.
|
||||
# The font and colours here are only fallbacks: rice-theme sets both through
|
||||
# Xresources, and st re-reads them on USR1.
|
||||
|
||||
s|^static char \*font = .*|static char *font = "JetBrainsMono Nerd Font:pixelsize=14:antialias=true:autohint=true";|
|
||||
s|^static int borderpx = .*|static int borderpx = 15;|
|
||||
s|^float alpha = .*|float alpha = 0.75;|
|
||||
s|^static unsigned int cursorshape = .*|static unsigned int cursorshape = 2;|
|
||||
|
||||
# Alt-based zoom, scrolling and transparency, as in the previous st build.
|
||||
/^static Shortcut shortcuts\[\] = {/a\
|
||||
{ MODKEY, XK_comma, zoom, {.f = +1} },\
|
||||
{ MODKEY, XK_period, zoom, {.f = -1} },\
|
||||
{ MODKEY, XK_g, zoomreset, {.f = 0} },\
|
||||
{ MODKEY, XK_Page_Up, kscrollup, {.i = -1}, S_PRI },\
|
||||
{ MODKEY, XK_Page_Down, kscrolldown, {.i = -1}, S_PRI },\
|
||||
{ MODKEY, XK_k, kscrollup, {.i = 1}, S_PRI },\
|
||||
{ MODKEY, XK_j, kscrolldown, {.i = 1}, S_PRI },\
|
||||
{ MODKEY, XK_Up, kscrollup, {.i = 1}, S_PRI },\
|
||||
{ MODKEY, XK_Down, kscrolldown, {.i = 1}, S_PRI },\
|
||||
{ MODKEY, XK_u, kscrollup, {.i = -1}, S_PRI },\
|
||||
{ MODKEY, XK_d, kscrolldown, {.i = -1}, S_PRI },\
|
||||
{ MODKEY, XK_a, changealpha, {.f = +0.05} },\
|
||||
{ MODKEY, XK_s, changealpha, {.f = -0.05} },
|
||||
575
st/config.siduck.h
Normal file
575
st/config.siduck.h
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
/* See LICENSE file for copyright and license details. */
|
||||
|
||||
/*
|
||||
* appearance
|
||||
*
|
||||
* font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html
|
||||
*/
|
||||
static char *font = "JetBrainsMono Nerd Font :pixelsize=14:antialias=true:autohint=true";
|
||||
static char *font2[] = { "JetBrainsMono Nerd Font :pixelsize=14:antialias=true:autohint=true" };
|
||||
static int borderpx = 15;
|
||||
|
||||
/*
|
||||
* What program is execed by st depends of these precedence rules:
|
||||
* 1: program passed with -e
|
||||
* 2: utmp option
|
||||
* 3: SHELL environment variable
|
||||
* 4: value of shell in /etc/passwd
|
||||
* 5: value of shell in config.h
|
||||
*/
|
||||
static char *shell = "/bin/sh";
|
||||
char *utmp = NULL;
|
||||
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
|
||||
|
||||
/* identification sequence returned in DA and DECID */
|
||||
char *vtiden = "\033[?6c";
|
||||
|
||||
/* Kerning / character bounding-box multipliers */
|
||||
static float cwscale = 1.0;
|
||||
static float chscale = 1.0;
|
||||
|
||||
/*
|
||||
* word delimiter string
|
||||
*
|
||||
* More advanced example: L" `'\"()[]{}"
|
||||
*/
|
||||
wchar_t *worddelimiters = L" ";
|
||||
|
||||
/* selection timeouts (in milliseconds) */
|
||||
static unsigned int doubleclicktimeout = 300;
|
||||
static unsigned int tripleclicktimeout = 600;
|
||||
|
||||
/* alt screens */
|
||||
int allowaltscreen = 1;
|
||||
|
||||
/*
|
||||
* draw latency range in ms - from new content/keypress/etc until drawing.
|
||||
* within this range, st draws when content stops arriving (idle). mostly it's
|
||||
* near minlatency, but it waits longer for slow updates to avoid partial draw.
|
||||
* low minlatency will tear/flicker more, as it can "detect" idle too early.
|
||||
*/
|
||||
static double minlatency = 8;
|
||||
static double maxlatency = 33;
|
||||
|
||||
/*
|
||||
* Synchronized-Update timeout in ms
|
||||
* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec
|
||||
*/
|
||||
static uint su_timeout = 200;
|
||||
|
||||
/*
|
||||
* blinking timeout (set to 0 to disable blinking) for the terminal blinking
|
||||
* attribute.
|
||||
*/
|
||||
static unsigned int blinktimeout = 800;
|
||||
|
||||
/*
|
||||
* interval (in milliseconds) between each successive call to ximspot. This
|
||||
* improves terminal performance while not reducing functionality to those
|
||||
* whom need XIM support.
|
||||
*/
|
||||
int ximspot_update_interval = 1000;
|
||||
|
||||
/*
|
||||
* thickness of underline and bar cursors
|
||||
*/
|
||||
static unsigned int cursorthickness = 2;
|
||||
|
||||
/*
|
||||
* 1: render most of the lines/blocks characters without using the font for
|
||||
* perfect alignment between cells (U2500 - U259F except dashes/diagonals).
|
||||
* Bold affects lines thickness if boxdraw_bold is not 0. Italic is ignored.
|
||||
* 0: disable (render all U25XX glyphs normally from the font).
|
||||
*/
|
||||
const int boxdraw = 1;
|
||||
const int boxdraw_bold = 1;
|
||||
|
||||
/* braille (U28XX): 1: render as adjacent "pixels", 0: use font */
|
||||
const int boxdraw_braille = 1;
|
||||
|
||||
/*
|
||||
* bell volume. It must be a value between -100 and 100. Use 0 for disabling
|
||||
* it
|
||||
*/
|
||||
static int bellvolume = 0;
|
||||
|
||||
/* default TERM value */
|
||||
char *termname = "st-256color";
|
||||
|
||||
/*
|
||||
* spaces per tab
|
||||
*
|
||||
* When you are changing this value, don't forget to adapt the »it« value in
|
||||
* the st.info and appropriately install the st.info in the environment where
|
||||
* you use this st version.
|
||||
*
|
||||
* it#$tabspaces,
|
||||
*
|
||||
* Secondly make sure your kernel is not expanding tabs. When running `stty
|
||||
* -a` »tab0« should appear. You can tell the terminal to not expand tabs by
|
||||
* running following command:
|
||||
*
|
||||
* stty tabs
|
||||
*/
|
||||
unsigned int tabspaces = 8;
|
||||
|
||||
/* bg opacity */
|
||||
float alpha = 0.72;
|
||||
|
||||
/* Terminal colors (16 first used in escape sequence) */
|
||||
static const char *colorname[] = {
|
||||
/* Sakura Line - sampled from bspwm/wallpaper/wallpaper.jpg */
|
||||
/* 8 normal colors */
|
||||
"#0f0d17", /* black */
|
||||
"#cf6a80", /* red */
|
||||
"#8fae74", /* green */
|
||||
"#d8a87c", /* yellow */
|
||||
"#7c9ede", /* blue */
|
||||
"#b483c0", /* magenta */
|
||||
"#8ab6c4", /* cyan */
|
||||
"#c9d1e2", /* white */
|
||||
|
||||
/* 8 bright colors */
|
||||
"#3a3350", /* black */
|
||||
"#e0798f", /* red */
|
||||
"#a8c68d", /* green */
|
||||
"#e9c79b", /* yellow */
|
||||
"#92b9ea", /* blue */
|
||||
"#d0a4d8", /* magenta */
|
||||
"#a3c0cb", /* cyan */
|
||||
"#e9eefb", /* white */
|
||||
};
|
||||
|
||||
/*
|
||||
* Default colors (colorname index)
|
||||
* foreground, background, cursor, reverse cursor
|
||||
*/
|
||||
unsigned int defaultfg = 15; // #e9eefb
|
||||
unsigned int defaultbg = 0; // #0f0d17
|
||||
unsigned int defaultcs = 12; // cursor: #92b9ea (sky)
|
||||
unsigned int defaultrcs = 0;
|
||||
|
||||
/*
|
||||
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81
|
||||
* Default style of cursor
|
||||
* 0: Blinking block
|
||||
* 1: Blinking block (default)
|
||||
* 2: Steady block ("█")
|
||||
* 3: Blinking underline
|
||||
* 4: Steady underline ("_")
|
||||
* 5: Blinking bar
|
||||
* 6: Steady bar ("|")
|
||||
* 7: Blinking st cursor
|
||||
* 8: Steady st cursor
|
||||
*/
|
||||
static unsigned int cursorshape = 1;
|
||||
|
||||
/*
|
||||
* Default columns and rows numbers
|
||||
*/
|
||||
|
||||
static unsigned int cols = 80;
|
||||
static unsigned int rows = 24;
|
||||
|
||||
/*
|
||||
* Default colour and shape of the mouse cursor
|
||||
*/
|
||||
static unsigned int mouseshape = XC_xterm;
|
||||
static unsigned int mousefg = 7;
|
||||
static unsigned int mousebg = 0;
|
||||
|
||||
/*
|
||||
* Color used to display font attributes when fontconfig selected a font which
|
||||
* doesn't match the ones requested.
|
||||
*/
|
||||
static unsigned int defaultattr = 11;
|
||||
|
||||
/*
|
||||
* Xresources preferences to load at startup
|
||||
*/
|
||||
ResourcePref resources[] = {
|
||||
{ "font", STRING, &font },
|
||||
{ "fontalt0", STRING, &font2[0] },
|
||||
{ "color0", STRING, &colorname[0] },
|
||||
{ "color1", STRING, &colorname[1] },
|
||||
{ "color2", STRING, &colorname[2] },
|
||||
{ "color3", STRING, &colorname[3] },
|
||||
{ "color4", STRING, &colorname[4] },
|
||||
{ "color5", STRING, &colorname[5] },
|
||||
{ "color6", STRING, &colorname[6] },
|
||||
{ "color7", STRING, &colorname[7] },
|
||||
{ "color8", STRING, &colorname[8] },
|
||||
{ "color9", STRING, &colorname[9] },
|
||||
{ "color10", STRING, &colorname[10] },
|
||||
{ "color11", STRING, &colorname[11] },
|
||||
{ "color12", STRING, &colorname[12] },
|
||||
{ "color13", STRING, &colorname[13] },
|
||||
{ "color14", STRING, &colorname[14] },
|
||||
{ "color15", STRING, &colorname[15] },
|
||||
{ "background", STRING, &colorname[258] },
|
||||
{ "foreground", STRING, &colorname[259] },
|
||||
{ "cursorColor", STRING, &colorname[256] },
|
||||
{ "termname", STRING, &termname },
|
||||
{ "shell", STRING, &shell },
|
||||
{ "blinktimeout", INTEGER, &blinktimeout },
|
||||
{ "bellvolume", INTEGER, &bellvolume },
|
||||
{ "tabspaces", INTEGER, &tabspaces },
|
||||
{ "borderpx", INTEGER, &borderpx },
|
||||
{ "cwscale", FLOAT, &cwscale },
|
||||
{ "chscale", FLOAT, &chscale },
|
||||
{ "alpha", FLOAT, &alpha },
|
||||
{ "ximspot_update_interval", INTEGER, &ximspot_update_interval },
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal mouse shortcuts.
|
||||
* Beware that overloading Button1 will disable the selection.
|
||||
*/
|
||||
const unsigned int mousescrollincrement = 3;
|
||||
static MouseShortcut mshortcuts[] = {
|
||||
/* button mask string */
|
||||
{ Button4, XK_NO_MOD, "\031" },
|
||||
{ Button5, XK_NO_MOD, "\005" },
|
||||
};
|
||||
|
||||
/* Internal keyboard shortcuts. */
|
||||
#define MODKEY Mod1Mask
|
||||
#define TERMMOD (Mod4Mask|ShiftMask)
|
||||
|
||||
MouseKey mkeys[] = {
|
||||
/* button mask function argument */
|
||||
{ Button4, XK_NO_MOD, kscrollup, {.i = mousescrollincrement} },
|
||||
{ Button5, XK_NO_MOD, kscrolldown, {.i = mousescrollincrement} },
|
||||
{ Button4, Mod4Mask, zoom, {.f = +1} },
|
||||
{ Button5, Mod4Mask, zoom, {.f = -1} },
|
||||
};
|
||||
|
||||
static char *openurlcmd[] = { "/bin/sh", "-c", "st-urlhandler", "externalpipe", NULL };
|
||||
|
||||
static char *copyurlcmd[] = { "/bin/sh", "-c",
|
||||
"tmp=$(sed 's/.*│//g' | tr -d '\n' | grep -aEo '(((http|https|gopher|gemini|ftp|ftps|git)://|www\\.)[a-zA-Z0-9.]*[:]?[a-zA-Z0-9./@$&%?$#=_-~]*)|((magnet:\\?xt=urn:btih:)[a-zA-Z0-9]*)' | uniq | sed 's/^www./http:\\/\\/www\\./g' ); IFS=; [ ! -z $tmp ] && echo $tmp | dmenu -i -p 'Copy which url?' -l 10 | tr -d '\n' | xclip -selection clipboard",
|
||||
"externalpipe", NULL };
|
||||
|
||||
static char *copyoutput[] = { "/bin/sh", "-c", "st-copyout", "externalpipe", NULL };
|
||||
|
||||
static Shortcut shortcuts[] = {
|
||||
/* mask keysym function argument */
|
||||
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
|
||||
{ ControlMask, XK_Print, toggleprinter, {.i = 0} },
|
||||
{ ShiftMask, XK_Print, printscreen, {.i = 0} },
|
||||
{ XK_ANY_MOD, XK_Print, printsel, {.i = 0} },
|
||||
{ MODKEY, XK_comma, zoom, {.f = +1} },
|
||||
{ MODKEY, XK_period, zoom, {.f = -1} },
|
||||
{ MODKEY, XK_g, zoomreset, {.f = 0} },
|
||||
{ ControlMask | ShiftMask, XK_C, clipcopy, {.i = 0} },
|
||||
{ ShiftMask, XK_Insert, clippaste, {.i = 0} },
|
||||
{ ControlMask | ShiftMask, XK_V, clippaste, {.i = 0} },
|
||||
{ XK_ANY_MOD, Button2, selpaste, {.i = 0} },
|
||||
{ MODKEY, XK_Num_Lock, numlock, {.i = 0} },
|
||||
{ ControlMask | ShiftMask, XK_U, iso14755, {.i = 0} },
|
||||
{ ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
|
||||
{ ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
|
||||
{ MODKEY, XK_Page_Up, kscrollup, {.i = -1} },
|
||||
{ MODKEY, XK_Page_Down, kscrolldown, {.i = -1} },
|
||||
{ MODKEY, XK_k, kscrollup, {.i = 1} },
|
||||
{ MODKEY, XK_j, kscrolldown, {.i = 1} },
|
||||
{ MODKEY, XK_Up, kscrollup, {.i = 1} },
|
||||
{ MODKEY, XK_Down, kscrolldown, {.i = 1} },
|
||||
{ MODKEY, XK_u, kscrollup, {.i = -1} },
|
||||
{ MODKEY, XK_d, kscrolldown, {.i = -1} },
|
||||
{ MODKEY, XK_s, changealpha, {.f = -0.05} },
|
||||
{ MODKEY, XK_a, changealpha, {.f = +0.05} },
|
||||
{ MODKEY, XK_m, changealpha, {.f = +2.00} },
|
||||
{ TERMMOD, XK_Up, zoom, {.f = +1} },
|
||||
{ TERMMOD, XK_Down, zoom, {.f = -1} },
|
||||
{ TERMMOD, XK_K, zoom, {.f = +1} },
|
||||
{ TERMMOD, XK_J, zoom, {.f = -1} },
|
||||
{ TERMMOD, XK_U, zoom, {.f = +2} },
|
||||
{ TERMMOD, XK_D, zoom, {.f = -2} },
|
||||
{ MODKEY, XK_l, externalpipe, {.v = openurlcmd } },
|
||||
{ MODKEY, XK_y, externalpipe, {.v = copyurlcmd } },
|
||||
{ MODKEY, XK_o, externalpipe, {.v = copyoutput } },
|
||||
{ TERMMOD, XK_Return, newterm, {.i = 0} },
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* Special keys (change & recompile st.info accordingly)
|
||||
*
|
||||
* Mask value:
|
||||
* * Use XK_ANY_MOD to match the key no matter modifiers state
|
||||
* * Use XK_NO_MOD to match the key alone (no modifiers)
|
||||
* appkey value:
|
||||
* * 0: no value
|
||||
* * > 0: keypad application mode enabled
|
||||
* * = 2: term.numlock = 1
|
||||
* * < 0: keypad application mode disabled
|
||||
* appcursor value:
|
||||
* * 0: no value
|
||||
* * > 0: cursor application mode enabled
|
||||
* * < 0: cursor application mode disabled
|
||||
* crlf value
|
||||
* * 0: no value
|
||||
* * > 0: crlf mode is enabled
|
||||
* * < 0: crlf mode is disabled
|
||||
*
|
||||
* Be careful with the order of the definitions because st searches in
|
||||
* this table sequentially, so any XK_ANY_MOD must be in the last
|
||||
* position for a key.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If you want keys other than the X11 function keys (0xFD00 - 0xFFFF)
|
||||
* to be mapped below, add them to this array.
|
||||
*/
|
||||
static KeySym mappedkeys[] = { -1 };
|
||||
|
||||
/*
|
||||
* State bits to ignore when matching key or button events. By default,
|
||||
* numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored.
|
||||
*/
|
||||
static uint ignoremod = Mod2Mask|XK_SWITCH_MOD;
|
||||
|
||||
/*
|
||||
* Override mouse-select while mask is active (when MODE_MOUSE is set).
|
||||
* Note that if you want to use ShiftMask with selmasks, set this to an other
|
||||
* modifier, set to 0 to not use it.
|
||||
*/
|
||||
static uint forceselmod = ShiftMask;
|
||||
|
||||
/*
|
||||
* This is the huge key array which defines all compatibility to the Linux
|
||||
* world. Please decide about changes wisely.
|
||||
*/
|
||||
static Key key[] = {
|
||||
/* keysym mask string appkey appcursor */
|
||||
{ XK_KP_Home, ShiftMask, "\033[2J", 0, -1},
|
||||
{ XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1},
|
||||
{ XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1},
|
||||
{ XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1},
|
||||
{ XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0},
|
||||
{ XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1},
|
||||
{ XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1},
|
||||
{ XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0},
|
||||
{ XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1},
|
||||
{ XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1},
|
||||
{ XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0},
|
||||
{ XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1},
|
||||
{ XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1},
|
||||
{ XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0},
|
||||
{ XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1},
|
||||
{ XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1},
|
||||
{ XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0},
|
||||
{ XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
|
||||
{ XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0},
|
||||
{ XK_KP_End, ControlMask, "\033[J", -1, 0},
|
||||
{ XK_KP_End, ControlMask, "\033[1;5F", +1, 0},
|
||||
{ XK_KP_End, ShiftMask, "\033[K", -1, 0},
|
||||
{ XK_KP_End, ShiftMask, "\033[1;2F", +1, 0},
|
||||
{ XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0},
|
||||
{ XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0},
|
||||
{ XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0},
|
||||
{ XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0},
|
||||
{ XK_KP_Insert, ShiftMask, "\033[4l", -1, 0},
|
||||
{ XK_KP_Insert, ControlMask, "\033[L", -1, 0},
|
||||
{ XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0},
|
||||
{ XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
|
||||
{ XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
|
||||
{ XK_KP_Delete, ControlMask, "\033[M", -1, 0},
|
||||
{ XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0},
|
||||
{ XK_KP_Delete, ShiftMask, "\033[2K", -1, 0},
|
||||
{ XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0},
|
||||
{ XK_KP_Delete, XK_ANY_MOD, "\033[3~", -1, 0},
|
||||
{ XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
|
||||
{ XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0},
|
||||
{ XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0},
|
||||
{ XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0},
|
||||
{ XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0},
|
||||
{ XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0},
|
||||
{ XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0},
|
||||
{ XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0},
|
||||
{ XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0},
|
||||
{ XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0},
|
||||
{ XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0},
|
||||
{ XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0},
|
||||
{ XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0},
|
||||
{ XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0},
|
||||
{ XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0},
|
||||
{ XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0},
|
||||
{ XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0},
|
||||
{ XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0},
|
||||
{ XK_Up, ShiftMask, "\033[1;2A", 0, 0},
|
||||
{ XK_Up, Mod1Mask, "\033[1;3A", 0, 0},
|
||||
{ XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0},
|
||||
{ XK_Up, ControlMask, "\033[1;5A", 0, 0},
|
||||
{ XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0},
|
||||
{ XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0},
|
||||
{ XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0},
|
||||
{ XK_Up, XK_ANY_MOD, "\033[A", 0, -1},
|
||||
{ XK_Up, XK_ANY_MOD, "\033OA", 0, +1},
|
||||
{ XK_Down, ShiftMask, "\033[1;2B", 0, 0},
|
||||
{ XK_Down, Mod1Mask, "\033[1;3B", 0, 0},
|
||||
{ XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0},
|
||||
{ XK_Down, ControlMask, "\033[1;5B", 0, 0},
|
||||
{ XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0},
|
||||
{ XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0},
|
||||
{ XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0},
|
||||
{ XK_Down, XK_ANY_MOD, "\033[B", 0, -1},
|
||||
{ XK_Down, XK_ANY_MOD, "\033OB", 0, +1},
|
||||
{ XK_Left, ShiftMask, "\033[1;2D", 0, 0},
|
||||
{ XK_Left, Mod1Mask, "\033[1;3D", 0, 0},
|
||||
{ XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0},
|
||||
{ XK_Left, ControlMask, "\033[1;5D", 0, 0},
|
||||
{ XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0},
|
||||
{ XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0},
|
||||
{ XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0},
|
||||
{ XK_Left, XK_ANY_MOD, "\033[D", 0, -1},
|
||||
{ XK_Left, XK_ANY_MOD, "\033OD", 0, +1},
|
||||
{ XK_Right, ShiftMask, "\033[1;2C", 0, 0},
|
||||
{ XK_Right, Mod1Mask, "\033[1;3C", 0, 0},
|
||||
{ XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0},
|
||||
{ XK_Right, ControlMask, "\033[1;5C", 0, 0},
|
||||
{ XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0},
|
||||
{ XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0},
|
||||
{ XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0},
|
||||
{ XK_Right, XK_ANY_MOD, "\033[C", 0, -1},
|
||||
{ XK_Right, XK_ANY_MOD, "\033OC", 0, +1},
|
||||
{ XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0},
|
||||
{ XK_Return, Mod1Mask, "\033\r", 0, 0},
|
||||
{ XK_Return, XK_ANY_MOD, "\r", 0, 0},
|
||||
{ XK_Insert, ShiftMask, "\033[4l", -1, 0},
|
||||
{ XK_Insert, ShiftMask, "\033[2;2~", +1, 0},
|
||||
{ XK_Insert, ControlMask, "\033[L", -1, 0},
|
||||
{ XK_Insert, ControlMask, "\033[2;5~", +1, 0},
|
||||
{ XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0},
|
||||
{ XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0},
|
||||
{ XK_Delete, ControlMask, "\033[M", -1, 0},
|
||||
{ XK_Delete, ControlMask, "\033[3;5~", +1, 0},
|
||||
{ XK_Delete, ShiftMask, "\033[2K", -1, 0},
|
||||
{ XK_Delete, ShiftMask, "\033[3;2~", +1, 0},
|
||||
{ XK_Delete, XK_ANY_MOD, "\033[3~", -1, 0},
|
||||
{ XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0},
|
||||
{ XK_BackSpace, XK_NO_MOD, "\177", 0, 0},
|
||||
{ XK_BackSpace, Mod1Mask, "\033\177", 0, 0},
|
||||
{ XK_Home, ShiftMask, "\033[2J", 0, -1},
|
||||
{ XK_Home, ShiftMask, "\033[1;2H", 0, +1},
|
||||
{ XK_Home, XK_ANY_MOD, "\033[H", 0, -1},
|
||||
{ XK_Home, XK_ANY_MOD, "\033[1~", 0, +1},
|
||||
{ XK_End, ControlMask, "\033[J", -1, 0},
|
||||
{ XK_End, ControlMask, "\033[1;5F", +1, 0},
|
||||
{ XK_End, ShiftMask, "\033[K", -1, 0},
|
||||
{ XK_End, ShiftMask, "\033[1;2F", +1, 0},
|
||||
{ XK_End, XK_ANY_MOD, "\033[4~", 0, 0},
|
||||
{ XK_Prior, ControlMask, "\033[5;5~", 0, 0},
|
||||
{ XK_Prior, ShiftMask, "\033[5;2~", 0, 0},
|
||||
{ XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0},
|
||||
{ XK_Next, ControlMask, "\033[6;5~", 0, 0},
|
||||
{ XK_Next, ShiftMask, "\033[6;2~", 0, 0},
|
||||
{ XK_Next, XK_ANY_MOD, "\033[6~", 0, 0},
|
||||
{ XK_F1, XK_NO_MOD, "\033OP" , 0, 0},
|
||||
{ XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0},
|
||||
{ XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0},
|
||||
{ XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0},
|
||||
{ XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0},
|
||||
{ XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0},
|
||||
{ XK_F2, XK_NO_MOD, "\033OQ" , 0, 0},
|
||||
{ XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0},
|
||||
{ XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0},
|
||||
{ XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0},
|
||||
{ XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0},
|
||||
{ XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0},
|
||||
{ XK_F3, XK_NO_MOD, "\033OR" , 0, 0},
|
||||
{ XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0},
|
||||
{ XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0},
|
||||
{ XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0},
|
||||
{ XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0},
|
||||
{ XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0},
|
||||
{ XK_F4, XK_NO_MOD, "\033OS" , 0, 0},
|
||||
{ XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0},
|
||||
{ XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0},
|
||||
{ XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0},
|
||||
{ XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0},
|
||||
{ XK_F5, XK_NO_MOD, "\033[15~", 0, 0},
|
||||
{ XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0},
|
||||
{ XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0},
|
||||
{ XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0},
|
||||
{ XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0},
|
||||
{ XK_F6, XK_NO_MOD, "\033[17~", 0, 0},
|
||||
{ XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0},
|
||||
{ XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0},
|
||||
{ XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0},
|
||||
{ XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0},
|
||||
{ XK_F7, XK_NO_MOD, "\033[18~", 0, 0},
|
||||
{ XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0},
|
||||
{ XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0},
|
||||
{ XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0},
|
||||
{ XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0},
|
||||
{ XK_F8, XK_NO_MOD, "\033[19~", 0, 0},
|
||||
{ XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0},
|
||||
{ XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0},
|
||||
{ XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0},
|
||||
{ XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0},
|
||||
{ XK_F9, XK_NO_MOD, "\033[20~", 0, 0},
|
||||
{ XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0},
|
||||
{ XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0},
|
||||
{ XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0},
|
||||
{ XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0},
|
||||
{ XK_F10, XK_NO_MOD, "\033[21~", 0, 0},
|
||||
{ XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0},
|
||||
{ XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0},
|
||||
{ XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0},
|
||||
{ XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0},
|
||||
{ XK_F11, XK_NO_MOD, "\033[23~", 0, 0},
|
||||
{ XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0},
|
||||
{ XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0},
|
||||
{ XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0},
|
||||
{ XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0},
|
||||
{ XK_F12, XK_NO_MOD, "\033[24~", 0, 0},
|
||||
{ XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0},
|
||||
{ XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0},
|
||||
{ XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0},
|
||||
{ XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0},
|
||||
{ XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0},
|
||||
{ XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0},
|
||||
{ XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0},
|
||||
{ XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0},
|
||||
{ XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0},
|
||||
{ XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0},
|
||||
{ XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0},
|
||||
{ XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0},
|
||||
{ XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0},
|
||||
{ XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0},
|
||||
{ XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0},
|
||||
{ XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0},
|
||||
{ XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0},
|
||||
{ XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0},
|
||||
{ XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0},
|
||||
{ XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0},
|
||||
{ XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0},
|
||||
{ XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0},
|
||||
{ XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0},
|
||||
{ XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0},
|
||||
{ XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0},
|
||||
{ XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0},
|
||||
{ XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0},
|
||||
};
|
||||
|
||||
/*
|
||||
* Selection types' masks.
|
||||
* Use the same masks as usual.
|
||||
* Button1Mask is always unset, to make masks match between ButtonPress.
|
||||
* ButtonRelease and MotionNotify.
|
||||
* If no match is found, regular selection is used.
|
||||
*/
|
||||
static uint selmasks[] = {
|
||||
[SEL_RECTANGULAR] = Mod1Mask,
|
||||
};
|
||||
|
||||
/*
|
||||
* Printable characters in ASCII, used to estimate the advance width
|
||||
* of single wide characters.
|
||||
*/
|
||||
static char ascii_printable[] =
|
||||
" !\"#$%&'()*+,-./0123456789:;<=>?"
|
||||
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~";
|
||||
18
st/patches.list
Normal file
18
st/patches.list
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# st-flexipatch patches the rice turns on, one per line, without "_PATCH".
|
||||
# st/build.sh flips each to 1 in patches.h. See patches.def.h upstream for the full list.
|
||||
|
||||
ALPHA # transparency (alt + a / alt + s)
|
||||
BOXDRAW # gapless box-drawing and braille characters
|
||||
CSI_22_23 # save/restore window title (nvim)
|
||||
KEYBOARDSELECT # select text with the keyboard (ctrl + shift + escape)
|
||||
LIGATURES # ligatures via harfbuzz
|
||||
NEWTERM # new terminal in the same directory (ctrl + shift + return)
|
||||
OPENURLONCLICK # ctrl + click opens a link
|
||||
SCROLLBACK # shift/alt + page up/down, alt + j/k/u/d
|
||||
SCROLLBACK_MOUSE # shift + wheel scrolls back
|
||||
SCROLLBACK_MOUSE_ALTSCREEN # plain wheel scrolls back outside full-screen apps
|
||||
SIXEL # images in the terminal (chafa, yazi, fzf previews)
|
||||
UNDERCURL # curly underlines (nvim diagnostics)
|
||||
WIDE_GLYPHS # Nerd Font icons drawn full width
|
||||
XRESOURCES # font and colours from Xresources (rice-theme)
|
||||
XRESOURCES_RELOAD # re-read them on USR1, so theme changes reach open terminals
|
||||
34
templates/Xresources.tpl
Normal file
34
templates/Xresources.tpl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
! Rendered by rice-theme from colors.toml - edit templates/Xresources.tpl instead.
|
||||
! st reads these through its xresources patch; reopen st (or send it USR1) to pick them up.
|
||||
|
||||
*.foreground: {{ fg }}
|
||||
*.background: {{ bg }}
|
||||
*.cursorColor: {{ accent }}
|
||||
|
||||
*.color0: {{ bg }}
|
||||
*.color1: {{ red }}
|
||||
*.color2: {{ green }}
|
||||
*.color3: {{ yellow }}
|
||||
*.color4: {{ blue }}
|
||||
*.color5: {{ magenta }}
|
||||
*.color6: {{ cyan }}
|
||||
*.color7: {{ fg_alt }}
|
||||
*.color8: {{ muted }}
|
||||
*.color9: {{ bright_red }}
|
||||
*.color10: {{ bright_green }}
|
||||
*.color11: {{ bright_yellow }}
|
||||
*.color12: {{ bright_blue }}
|
||||
*.color13: {{ bright_magenta }}
|
||||
*.color14: {{ bright_cyan }}
|
||||
*.color15: {{ fg }}
|
||||
|
||||
st.font: {{ font }}:pixelsize=14:antialias=true:autohint=true
|
||||
st.alpha: {{ alpha }}
|
||||
st.borderpx: 15
|
||||
|
||||
Xcursor.theme: {{ cursor_theme }}
|
||||
Xcursor.size: {{ cursor_size }}
|
||||
Xft.antialias: 1
|
||||
Xft.hinting: 1
|
||||
Xft.hintstyle: hintslight
|
||||
Xft.rgba: rgb
|
||||
8
templates/bspwm-colors.sh.tpl
Normal file
8
templates/bspwm-colors.sh.tpl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
# Rendered by rice-theme from colors.toml - edit templates/bspwm-colors.sh.tpl instead.
|
||||
bspc config focused_border_color "{{ accent }}"
|
||||
bspc config active_border_color "{{ border }}"
|
||||
bspc config normal_border_color "{{ surface }}"
|
||||
bspc config presel_feedback_color "{{ magenta }}"
|
||||
bspc config border_width {{ window_border }}
|
||||
bspc config window_gap {{ gap }}
|
||||
29
templates/dunst.conf.tpl
Normal file
29
templates/dunst.conf.tpl
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/dunst.conf.tpl instead.
|
||||
# Loaded as a drop-in (~/.config/dunst/dunstrc.d/50-theme.conf), so it only
|
||||
# carries colours; layout and behaviour live in dunstrc.
|
||||
|
||||
[global]
|
||||
font = "{{ font }} 10"
|
||||
icon_theme = "{{ icon_theme }}, Adwaita, hicolor"
|
||||
|
||||
[urgency_low]
|
||||
background = "{{ bg }}{{ alpha_hex }}"
|
||||
foreground = "{{ fg_alt }}"
|
||||
frame_color = "{{ border }}"
|
||||
highlight = "{{ dim }}"
|
||||
|
||||
[urgency_normal]
|
||||
background = "{{ bg }}{{ alpha_hex }}"
|
||||
foreground = "{{ fg }}"
|
||||
frame_color = "{{ border }}"
|
||||
highlight = "{{ accent }}"
|
||||
|
||||
[urgency_critical]
|
||||
background = "{{ bg }}{{ alpha_hex }}"
|
||||
foreground = "{{ fg }}"
|
||||
frame_color = "{{ urgent }}"
|
||||
highlight = "{{ urgent }}"
|
||||
|
||||
[osd]
|
||||
frame_color = "{{ accent }}"
|
||||
highlight = "{{ accent }}"
|
||||
30
templates/eww-colors.scss.tpl
Normal file
30
templates/eww-colors.scss.tpl
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// Rendered by rice-theme from colors.toml - edit templates/eww-colors.scss.tpl instead.
|
||||
$bg: {{ bg }};
|
||||
$bg-alt: {{ bg_alt }};
|
||||
$surface: {{ surface }};
|
||||
$overlay: {{ overlay }};
|
||||
$selection: {{ selection }};
|
||||
$border: {{ border }};
|
||||
|
||||
$fg: {{ fg }};
|
||||
$fg-alt: {{ fg_alt }};
|
||||
$dim: {{ dim }};
|
||||
$muted: {{ muted }};
|
||||
|
||||
$accent: {{ accent }};
|
||||
$accent-alt: {{ accent_alt }};
|
||||
$urgent: {{ urgent }};
|
||||
|
||||
$red: {{ red }};
|
||||
$green: {{ green }};
|
||||
$yellow: {{ yellow }};
|
||||
$blue: {{ blue }};
|
||||
$magenta: {{ magenta }};
|
||||
$cyan: {{ cyan }};
|
||||
|
||||
$font: "{{ font }}";
|
||||
$radius: {{ radius }}px;
|
||||
$radius-small: {{ radius_small }}px;
|
||||
$border-width: {{ border_width }}px;
|
||||
$gap: {{ gap }}px;
|
||||
$alpha: {{ alpha }};
|
||||
6
templates/fzf.sh.tpl
Normal file
6
templates/fzf.sh.tpl
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/fzf.sh.tpl instead.
|
||||
# Sourced by ~/.bashrc so fzf, ff and Ctrl+R pickers match rofi.
|
||||
export FZF_DEFAULT_OPTS="\
|
||||
--color=bg:-1,bg+:{{ selection }},fg:{{ fg_alt }},fg+:{{ fg }},hl:{{ accent }},hl+:{{ accent }} \
|
||||
--color=border:{{ border }},header:{{ accent }},info:{{ dim }},marker:{{ green }},pointer:{{ urgent }},prompt:{{ urgent }},spinner:{{ cyan }} \
|
||||
--prompt=' ' --pointer='▌' --marker='+' --border=rounded --layout=reverse --height=60% --info=inline-right"
|
||||
65
templates/gtk.css.tpl
Normal file
65
templates/gtk.css.tpl
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/* Rendered by rice-theme from colors.toml - edit templates/gtk.css.tpl instead.
|
||||
Linked as ~/.config/gtk-3.0/gtk.css and ~/.config/gtk-4.0/gtk.css. */
|
||||
|
||||
/* GTK3 / Adwaita named colours */
|
||||
@define-color theme_bg_color {{ bg_alt }};
|
||||
@define-color theme_fg_color {{ fg }};
|
||||
@define-color theme_base_color {{ bg }};
|
||||
@define-color theme_text_color {{ fg }};
|
||||
@define-color theme_selected_bg_color {{ accent }};
|
||||
@define-color theme_selected_fg_color {{ bg }};
|
||||
@define-color theme_unfocused_bg_color {{ bg_alt }};
|
||||
@define-color theme_unfocused_fg_color {{ fg }};
|
||||
@define-color theme_unfocused_base_color {{ bg }};
|
||||
@define-color theme_unfocused_text_color {{ fg }};
|
||||
@define-color theme_unfocused_selected_bg_color {{ selection }};
|
||||
@define-color theme_unfocused_selected_fg_color {{ fg }};
|
||||
@define-color insensitive_bg_color {{ bg_alt }};
|
||||
@define-color insensitive_fg_color {{ dim }};
|
||||
@define-color insensitive_base_color {{ bg }};
|
||||
@define-color borders {{ border }};
|
||||
@define-color unfocused_borders {{ border }};
|
||||
@define-color wm_bg {{ surface }};
|
||||
@define-color wm_border {{ border }};
|
||||
@define-color wm_title {{ fg }};
|
||||
@define-color wm_unfocused_title {{ dim }};
|
||||
@define-color success_color {{ green }};
|
||||
@define-color warning_color {{ yellow }};
|
||||
@define-color error_color {{ red }};
|
||||
@define-color link_color {{ accent }};
|
||||
@define-color visited_link_color {{ magenta }};
|
||||
@define-color content_view_bg {{ bg }};
|
||||
|
||||
/* libadwaita / GTK4 named colours */
|
||||
@define-color accent_color {{ accent }};
|
||||
@define-color accent_bg_color {{ accent }};
|
||||
@define-color accent_fg_color {{ bg }};
|
||||
@define-color destructive_color {{ red }};
|
||||
@define-color window_bg_color {{ bg_alt }};
|
||||
@define-color window_fg_color {{ fg }};
|
||||
@define-color view_bg_color {{ bg }};
|
||||
@define-color view_fg_color {{ fg }};
|
||||
@define-color headerbar_bg_color {{ surface }};
|
||||
@define-color headerbar_fg_color {{ fg }};
|
||||
@define-color headerbar_border_color {{ border }};
|
||||
@define-color headerbar_backdrop_color {{ bg_alt }};
|
||||
@define-color headerbar_shade_color rgba(0, 0, 0, 0.36);
|
||||
@define-color sidebar_bg_color {{ surface }};
|
||||
@define-color sidebar_fg_color {{ fg }};
|
||||
@define-color sidebar_backdrop_color {{ bg_alt }};
|
||||
@define-color sidebar_border_color {{ border }};
|
||||
@define-color sidebar_shade_color rgba(0, 0, 0, 0.36);
|
||||
@define-color secondary_sidebar_bg_color {{ bg_alt }};
|
||||
@define-color secondary_sidebar_fg_color {{ fg }};
|
||||
@define-color card_bg_color {{ surface }};
|
||||
@define-color card_fg_color {{ fg }};
|
||||
@define-color card_shade_color rgba(0, 0, 0, 0.36);
|
||||
@define-color dialog_bg_color {{ surface }};
|
||||
@define-color dialog_fg_color {{ fg }};
|
||||
@define-color popover_bg_color {{ overlay }};
|
||||
@define-color popover_fg_color {{ fg }};
|
||||
@define-color popover_shade_color rgba(0, 0, 0, 0.36);
|
||||
@define-color thumbnail_bg_color {{ surface }};
|
||||
@define-color thumbnail_fg_color {{ fg }};
|
||||
@define-color shade_color rgba(0, 0, 0, 0.36);
|
||||
@define-color scrollbar_outline_color {{ border }};
|
||||
21
templates/imv-config.tpl
Normal file
21
templates/imv-config.tpl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/imv-config.tpl instead.
|
||||
# Linked as ~/.config/imv/config.
|
||||
[options]
|
||||
background = {{ bg_strip }}
|
||||
overlay_font = {{ font }}:11
|
||||
overlay_text_color = {{ fg_strip }}
|
||||
overlay_background_color = {{ bg_strip }}
|
||||
overlay_background_alpha = cc
|
||||
|
||||
[binds]
|
||||
# Copy the picture to the clipboard
|
||||
<Ctrl+c> = exec sh -c 'xclip -selection clipboard -t "$(file -b --mime-type "$1")" -i "$1"' _ "$imv_current_file"
|
||||
# Make it the wallpaper
|
||||
<Ctrl+w> = exec rice-wallpaper set "$imv_current_file"
|
||||
# Rotate the file on disk by 90 degrees
|
||||
<Ctrl+r> = exec mogrify -rotate 90 "$imv_current_file"
|
||||
# Print it
|
||||
<Ctrl+p> = exec lp "$imv_current_file"
|
||||
# Move to the trash and quit / move to the trash and show the next one
|
||||
<Ctrl+x> = exec gio trash -- "$imv_current_file"; quit
|
||||
<Ctrl+Shift+X> = exec gio trash -- "$imv_current_file"; close
|
||||
30
templates/kitty.conf.tpl
Normal file
30
templates/kitty.conf.tpl
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/kitty.conf.tpl instead.
|
||||
font_family {{ font }}
|
||||
bold_font auto
|
||||
italic_font auto
|
||||
bold_italic_font auto
|
||||
background_opacity {{ alpha }}
|
||||
background {{ bg }}
|
||||
foreground {{ fg }}
|
||||
cursor {{ accent }}
|
||||
cursor_text_color {{ bg }}
|
||||
selection_background {{ selection }}
|
||||
selection_foreground {{ fg }}
|
||||
url_color {{ accent }}
|
||||
|
||||
color0 {{ bg }}
|
||||
color1 {{ red }}
|
||||
color2 {{ green }}
|
||||
color3 {{ yellow }}
|
||||
color4 {{ blue }}
|
||||
color5 {{ magenta }}
|
||||
color6 {{ cyan }}
|
||||
color7 {{ fg_alt }}
|
||||
color8 {{ muted }}
|
||||
color9 {{ bright_red }}
|
||||
color10 {{ bright_green }}
|
||||
color11 {{ bright_yellow }}
|
||||
color12 {{ bright_blue }}
|
||||
color13 {{ bright_magenta }}
|
||||
color14 {{ bright_cyan }}
|
||||
color15 {{ fg }}
|
||||
21
templates/polybar-colors.ini.tpl
Normal file
21
templates/polybar-colors.ini.tpl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
; Rendered by rice-theme from colors.toml - edit templates/polybar-colors.ini.tpl instead.
|
||||
[colors]
|
||||
bg = {{ bg }}
|
||||
bg-trans = #{{ alpha_hex }}{{ bg_strip }}
|
||||
surface = {{ surface }}
|
||||
border = #99{{ border_strip }}
|
||||
text = {{ fg }}
|
||||
dim = {{ dim }}
|
||||
muted = {{ muted }}
|
||||
accent = {{ accent }}
|
||||
urgent = {{ urgent }}
|
||||
red = {{ red }}
|
||||
green = {{ green }}
|
||||
yellow = {{ yellow }}
|
||||
blue = {{ blue }}
|
||||
magenta = {{ magenta }}
|
||||
cyan = {{ cyan }}
|
||||
|
||||
[fonts]
|
||||
main = {{ font }}:style=Bold:pixelsize=10;2
|
||||
large = {{ font }}:style=Bold:pixelsize=12;2
|
||||
35
templates/polybar-modules.ini.tpl
Normal file
35
templates/polybar-modules.ini.tpl
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
; Rendered by rice-theme from colors.toml - edit templates/polybar-modules.ini.tpl instead.
|
||||
; Polybar only resolves ${colors.x} when it is the whole value, so modules that
|
||||
; colour part of their own text live here with the theme's colours written in.
|
||||
|
||||
[module/date]
|
||||
type = internal/date
|
||||
interval = 1
|
||||
date = %a %d
|
||||
time = %H:%M
|
||||
label = "%{A1:rice-panel calendar:}%{A3:rice-reminder new:}%{F{{ accent }}}%{F-} %time% %{F{{ dim }}}|%{F-} %date%%{A}%{A}"
|
||||
format-padding = 1
|
||||
|
||||
[module/cpu]
|
||||
type = custom/script
|
||||
exec = rice-sysinfo --cpu --normal-color '{{ accent }}' --critical-color '{{ urgent }}'
|
||||
click-left = rice-panel power
|
||||
format = "%{F{{ accent }}}CPU%{F-} <label>"
|
||||
format-padding = 1
|
||||
interval = 2
|
||||
|
||||
[module/ram]
|
||||
type = custom/script
|
||||
exec = rice-sysinfo --ram --normal-color '{{ yellow }}' --critical-color '{{ urgent }}'
|
||||
click-left = rice-panel power
|
||||
format = "%{F{{ yellow }}}RAM%{F-} <label>"
|
||||
format-padding = 1
|
||||
interval = 2
|
||||
|
||||
[module/gpu]
|
||||
type = custom/script
|
||||
exec = rice-sysinfo --gpu --normal-color '{{ magenta }}' --critical-color '{{ urgent }}'
|
||||
click-left = rice-panel power
|
||||
format = "%{F{{ magenta }}}GPU%{F-} <label>"
|
||||
format-padding = 1
|
||||
interval = 2
|
||||
18
templates/rofi-colors.rasi.tpl
Normal file
18
templates/rofi-colors.rasi.tpl
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* Rendered by rice-theme from colors.toml - edit templates/rofi-colors.rasi.tpl instead. */
|
||||
* {
|
||||
bg: {{ bg }};
|
||||
bg-trans: {{ bg }}{{ alpha_hex }};
|
||||
surface: {{ surface }};
|
||||
fg: {{ fg }};
|
||||
dim: {{ dim }};
|
||||
accent: {{ accent }};
|
||||
urgent: {{ urgent }};
|
||||
good: {{ green }};
|
||||
warn: {{ yellow }};
|
||||
border-col: {{ border }};
|
||||
select-bg: {{ accent }}2b;
|
||||
select-fg: {{ fg }};
|
||||
font-main: "{{ font }} 11";
|
||||
font-bold: "{{ font }} Bold 12";
|
||||
font-entry: "{{ font }} 12";
|
||||
}
|
||||
76
templates/starship.toml.tpl
Normal file
76
templates/starship.toml.tpl
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/starship.toml.tpl instead.
|
||||
# One line, like bash's user@host prompt with colour and git:
|
||||
# sarthak@arch ~/rice main !? ❯
|
||||
"$schema" = "https://starship.rs/config-schema.json"
|
||||
add_newline = false
|
||||
command_timeout = 300
|
||||
palette = "rice"
|
||||
|
||||
format = "$username$hostname$directory$git_branch$git_state$git_status$jobs$cmd_duration$character"
|
||||
|
||||
[palettes.rice]
|
||||
fg = "{{ fg }}"
|
||||
dim = "{{ dim }}"
|
||||
accent = "{{ accent }}"
|
||||
urgent = "{{ urgent }}"
|
||||
green = "{{ green }}"
|
||||
yellow = "{{ yellow }}"
|
||||
magenta = "{{ magenta }}"
|
||||
|
||||
[username]
|
||||
show_always = true
|
||||
format = "[$user]($style)"
|
||||
style_user = "bold accent"
|
||||
style_root = "bold urgent"
|
||||
|
||||
[hostname]
|
||||
ssh_only = false
|
||||
trim_at = "."
|
||||
format = "[@](dim)[$hostname]($style) "
|
||||
style = "bold magenta"
|
||||
|
||||
[directory]
|
||||
style = "bold fg"
|
||||
truncation_length = 3
|
||||
truncate_to_repo = false
|
||||
truncation_symbol = "…/"
|
||||
read_only = " "
|
||||
read_only_style = "urgent"
|
||||
format = "[$path]($style)[$read_only]($read_only_style) "
|
||||
|
||||
[git_branch]
|
||||
symbol = " "
|
||||
style = "dim"
|
||||
format = "[$symbol$branch]($style) "
|
||||
|
||||
[git_state]
|
||||
style = "yellow"
|
||||
format = "[$state( $progress_current/$progress_total)]($style) "
|
||||
|
||||
[git_status]
|
||||
style = "yellow"
|
||||
format = "([$all_status$ahead_behind]($style) )"
|
||||
conflicted = "="
|
||||
untracked = "?"
|
||||
stashed = ""
|
||||
modified = "!"
|
||||
staged = "+"
|
||||
renamed = "»"
|
||||
deleted = "✘"
|
||||
ahead = "⇡"
|
||||
behind = "⇣"
|
||||
diverged = "⇕"
|
||||
|
||||
[jobs]
|
||||
symbol = "✦"
|
||||
style = "dim"
|
||||
format = "[$symbol$number]($style) "
|
||||
|
||||
[cmd_duration]
|
||||
min_time = 2000
|
||||
format = "[$duration](dim) "
|
||||
|
||||
[character]
|
||||
success_symbol = "[❯](bold accent)"
|
||||
error_symbol = "[❯](bold urgent)"
|
||||
vimcmd_symbol = "[❮](bold green)"
|
||||
15
templates/xsecurelock.env.tpl
Normal file
15
templates/xsecurelock.env.tpl
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Rendered by rice-theme from colors.toml - edit templates/xsecurelock.env.tpl instead.
|
||||
# Sourced by rice-lock before it starts xsecurelock.
|
||||
export XSECURELOCK_FONT="{{ font }}:size=13"
|
||||
export XSECURELOCK_BACKGROUND_COLOR="{{ bg }}"
|
||||
export XSECURELOCK_AUTH_BACKGROUND_COLOR="{{ bg }}"
|
||||
export XSECURELOCK_AUTH_FOREGROUND_COLOR="{{ fg }}"
|
||||
export XSECURELOCK_AUTH_WARNING_COLOR="{{ urgent }}"
|
||||
export XSECURELOCK_PASSWORD_PROMPT="asterisks"
|
||||
export XSECURELOCK_SHOW_DATETIME=1
|
||||
export XSECURELOCK_DATETIME_FORMAT="%A %d %B %H:%M"
|
||||
export XSECURELOCK_SHOW_HOSTNAME=0
|
||||
export XSECURELOCK_SHOW_USERNAME=0
|
||||
export XSECURELOCK_SAVER=saver_xscreensaver
|
||||
export XSECURELOCK_BLANK_TIMEOUT=600
|
||||
export XSECURELOCK_BLANK_DPMS_STATE=off
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue