Compare commits

..

29 Commits

Author SHA1 Message Date
a3aba45b6e Try to fix inhibit-sleep-for-audio.lisp for discord 2025-11-07 22:46:58 -08:00
e0dad7f7dd Fix inhibit-sleep-for-audio.lisp 2025-10-13 14:50:30 -07:00
99f991728b Add inhibit-sleep-for-audio.lisp 2025-10-10 02:32:08 -07:00
5b551a4075 Update for waybar 2025-10-09 22:40:38 -07:00
d7fbed68bf Fix system-sleep-menu.sh 2025-10-08 17:28:08 -07:00
48b6b0afde Fix system-menu.sh 2025-09-24 00:31:52 -07:00
204035cb8c Update tv-power-menu.sh 2025-09-12 23:03:31 -07:00
59e6c14c1b Switch from way-displays to kanshi 2025-08-21 04:46:15 -07:00
2ee4b47086 Fix sleep and wake on lock screen 2025-08-18 19:53:14 -07:00
f763fc1188 Merge branch 'main' of git.zander.im:Zander671/random-scripts 2025-06-13 22:34:22 +09:00
42ea5f1370 Update eww-network 2025-06-13 22:33:27 +09:00
4f0093975b Update formatting in usbguard-notify.py 2025-05-05 09:12:53 +09:00
ad3a00266a Update eww-mu4e-messages 2025-04-30 22:25:45 +09:00
43a8d7f2a7 Add setup-qbittorent.zsh 2025-02-14 20:07:56 -08:00
37a87cc34e Update cl/khal-notify.lisp 2025-02-11 20:26:30 -08:00
ed0b55c3b4 Update eww-battery-monitor 2025-02-06 17:01:40 -08:00
a0d2b83c4f Update river-restart-eww.sh 2025-02-06 16:30:48 -08:00
01fe96b46f Add river-restart-eww.sh 2025-02-06 15:31:51 -08:00
a4106633c8 Add tooltip to eww-battery-monitor 2025-02-06 15:09:37 -08:00
c8657e9378 Fix eww-mu4e-messages 2025-02-05 20:55:14 -08:00
fef223a62f Update sound-control.sh 2025-02-04 22:49:58 -08:00
ee1321e857 Some changes 2025-02-04 21:02:12 -08:00
f40f9f5277 Remove outdated hyprland stuff 2025-01-27 02:49:54 -08:00
d91c7f00ba Fix Xwayland-Game-Wrapper 2025-01-13 19:53:54 -08:00
ebd8d9ef2c Make confirm-logout.sh default to no 2025-01-01 22:59:07 -08:00
3c3782df42 Add confirm-logout.sh 2025-01-01 22:24:54 -08:00
34693cf757 Minor change to xwayland-game-wrapper 2024-12-25 17:05:07 -08:00
477caac066 Fix permissios for sse-nxmhandler-wrapper.sh 2024-11-14 23:51:55 -08:00
342dd058ee Add games/sse-nxmhandler-wrapper.sh 2024-11-14 23:19:25 -08:00
21 changed files with 522 additions and 213 deletions

View File

@ -0,0 +1,133 @@
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload '(:uiop :com.inuoe.jzon)))
(defpackage :inhibit-sleep-for-audio
(:use :cl)
(:local-nicknames (:jzon :com.inuoe.jzon))
(:export :toplevel))
(in-package :inhibit-sleep-for-audio)
(defparameter *debug-output* (progn #+slynk t #-slynk nil) ;; unconfuse emacs
"Whether or not to print debug output.")
(declaim (inline debug-format))
(defun debug-format (control-string &rest args)
"FORMAT to stdout, but only when *debug-output* is non-nil."
(when *debug-output*
(apply 'format t control-string args)))
(defun event-stream-states (event)
"Return a alist mapping ids to stream states."
(loop for obj across event
for id = (gethash "id" obj)
for info = (gethash "info" obj)
for props = (and (hash-table-p info) (gethash "props" info))
for running = (and (hash-table-p props)
(equal (gethash "type" obj) "PipeWire:Interface:Node")
(equal (gethash "state" info) "running")
(gethash "stream.is-live" props))
collect (cons id running)))
(defvar *inhibitor-process* nil
"The systemd-inhibit process object.")
(defun inhibitor-running-p ()
"Return non-nil if the inhibitor is active."
(and *inhibitor-process* (uiop:process-alive-p *inhibitor-process*)))
(defun start-inhibitor ()
"Start the inhibitor process."
(let ((cmd (list "systemd-inhibit"
"--mode=block"
"--what=sleep:idle"
"--who=inhibit-sleep-for-audio"
"--why=PipeWire audio playing or recording"
"sleep"
(princ-to-string most-positive-fixnum))))
(setq *inhibitor-process* (uiop:launch-program cmd :output "/dev/null"))
(debug-format "Started inhibitor process ~S~%" cmd)))
(defun stop-inhibitor ()
"Stop the inhibitor process."
(uiop:terminate-process *inhibitor-process*)
(uiop:wait-process *inhibitor-process*)
(setq *inhibitor-process* nil)
(debug-format "Stopped inhibitor process~%"))
(defvar *running-streams* (make-hash-table :test #'eql)
"Hash table (set) of running streams. This maps ids to the symbol t.")
(defun have-running-streams-p ()
"Return non-nil if there are running streams."
(not (zerop (hash-table-count *running-streams*))))
(defun process-event (event)
"Process one event from pw-dump."
(let ((live-streams (event-stream-states event)))
(dolist (entry live-streams)
(destructuring-bind (id . state) entry
(if state
(progn
(when (not (gethash id *running-streams*))
(debug-format "Stream ~A started~%" id))
(setf (gethash id *running-streams*) t))
(progn
(when (gethash id *running-streams*)
(debug-format "Stream ~A stopped~%" id))
(remhash id *running-streams*)))))
(let ((has-live-stream (have-running-streams-p)))
(cond
((and has-live-stream (not (inhibitor-running-p)))
(start-inhibitor))
((and (not has-live-stream) (inhibitor-running-p))
(stop-inhibitor))))))
(defun print-help-and-exit ()
"Print a help message and then exit."
(format t "usage: ~A [-h|--help] [-d|--debug]
-h|--help print this message, then exit
-d|--debug print debug output as program runs~%"
(or (uiop:argv0) "inhibit-sleep-for-audio.lisp"))
#-slynk (uiop:quit))
(defun handle-cli-args ()
"Process command-line arguments."
(dolist (arg (uiop:command-line-arguments))
(when (or (equal arg "-h") (equal arg "--help"))
(print-help-and-exit))
(when (or (equal arg "-d") (equal arg "--debug"))
(setq *debug-output* t))))
(defun read-next-event (stream)
"Read the next pw-dump event from STREAM."
(jzon:with-parser (parse stream)
(jzon:parse-next-element parse)))
(defun main ()
(handle-cli-args)
(let ((monitor-process (uiop:launch-program '("pw-dump" "-m")
:output :stream)))
(unwind-protect
(progn
(debug-format "Started pw-dump monitor process with pid ~A~%"
(uiop:process-info-pid monitor-process))
(loop with stream = (uiop:process-info-output monitor-process)
while (uiop:process-alive-p monitor-process)
do (process-event (read-next-event stream))))
(when (inhibitor-running-p)
(stop-inhibitor))
(when (uiop:process-alive-p monitor-process)
(uiop:terminate-process monitor-process)
(uiop:wait-process monitor-process)
(debug-format "Terminated pw-dump monitor process...~%")))))
#+sbcl (sb-ext:disable-debugger)
(defun toplevel ()
"Toplevel of the program."
#+sbcl (handler-case
(main)
(sb-sys:interactive-interrupt ()
(format t "Exiting because of keyboard interrupt...~%")
(uiop:quit 1)))
#-sbcl (main))

View File

@ -36,6 +36,16 @@
(subseq str 0 (1- len)) (subseq str 0 (1- len))
str))) str)))
(defun process-escape-sequences (str)
(coerce (loop with escape = nil
for chr across str
when (and (not escape) (eql chr #\\))
do (setq escape t)
else
do (setq escape nil) and
collect chr)
'string))
(defun get-ics-file-alarms (path) (defun get-ics-file-alarms (path)
(with-open-file (stream path :direction :input :if-does-not-exist nil) (with-open-file (stream path :direction :input :if-does-not-exist nil)
(when stream (when stream
@ -49,7 +59,10 @@
while line while line
do (cond do (cond
((and (not in-valarm) (uiop:string-prefix-p "SUMMARY:" line)) ((and (not in-valarm) (uiop:string-prefix-p "SUMMARY:" line))
(setq summary (subseq (remove-trailing-return line) 8))) (setq summary (subseq
(process-escape-sequences
(remove-trailing-return line))
8)))
((uiop:string-prefix-p "BEGIN:VALARM" line) ((uiop:string-prefix-p "BEGIN:VALARM" line)
(setq in-valarm t)) (setq in-valarm t))
((uiop:string-prefix-p "END:VALARM" line) ((uiop:string-prefix-p "END:VALARM" line)
@ -70,7 +83,8 @@
((equal unit "D") (* num 60 60 24)) ((equal unit "D") (* num 60 60 24))
(t 0)))))) (t 0))))))
((and in-valarm (uiop:string-prefix-p "DESCRIPTION:" line)) ((and in-valarm (uiop:string-prefix-p "DESCRIPTION:" line))
(setq current-notice (subseq (remove-trailing-return line) (setq current-notice (subseq (process-escape-sequences
(remove-trailing-return line))
12)))) 12))))
finally (return (mapcar (lambda (alarm) finally (return (mapcar (lambda (alarm)
(list (if (uiop:emptyp (car alarm)) (list (if (uiop:emptyp (car alarm))
@ -115,7 +129,8 @@
:time alarm-time :time alarm-time
:event-title event-title :event-title event-title
:event-end end :event-end end
:uid uid)))) alarms))))))) :uid uid))))
alarms)))))))
(defun build-alarm-list (calendar-dirs &optional exclude-before) (defun build-alarm-list (calendar-dirs &optional exclude-before)
(let* ((output (run-khal "list" (let* ((output (run-khal "list"

46
enable-displays-for-sleep Executable file
View File

@ -0,0 +1,46 @@
#!/usr/bin/env zsh
let just_list=0
local operation="--on"
local jq_filter_neg="| not"
local ignored=()
while getopts 'ldi:' name; do
case "${name}" in
h)
echo 'enable-displays-for-sleep [-l] [-d] [-i]'
echo '-d disable instead of enable'
echo '-l print the list of display that would be changed'
echo '-i ignore display'
echo 'By default, enable displays.'
;;
l)
just_list=1
;;
d)
jq_filter_neg=""
operation="--off"
;;
i)
ignored+="${OPTARG}"
;;
esac
done
SCRIPT=".[] | select(.enabled${jq_filter_neg}) | .name"
set -e
displays=("${(0)$(wlr-randr --json | jq --raw-output0 "${SCRIPT}")}")
set +e
displays=(${displays:|ignored})
if ((just_list)); then
printf '%s\0' ${displays}
else
flags=()
for display in ${displays}; do
flags+=(--output "${display}" "${operation}")
done
(( ${#flags} )) && wlr-randr ${flags}
fi

View File

@ -56,13 +56,24 @@ function warn_if_low() {
function print_state() { function print_state() {
percentage = state_info["percentage"] percentage = state_info["percentage"]
printf "%s%s%d%%\n", tooltip = ""
if (state_info["charging"]) {
tooltip = "Until Full: " (percentage == 100 ? "N/A" : state_info["time to full"])
} else {
tooltip = "Until Empty: " state_info["time to empty"]
}
printf "{\"text\":\"%s%s%d%%\",\"tooltip\":\"%s\"}\n",
get_icon(percentage), get_icon(percentage),
state_info["charging"] ? "󱐋" : "", state_info["charging"] ? "󱐋" : "",
percentage percentage,
tooltip
fflush() fflush()
} }
function string_or(str, def) {
return str == "" ? def : str
}
function parse_record(record, exit_on_absent) { function parse_record(record, exit_on_absent) {
split(record, fields) split(record, fields)
for (i in fields) { for (i in fields) {
@ -77,6 +88,8 @@ function parse_record(record, exit_on_absent) {
} else if ((! BATTERY && props["power supply"] == "yes" && \ } else if ((! BATTERY && props["power supply"] == "yes" && \
props["native-path"] ~ /BAT[0-9]+/) || name == BATTERY) { props["native-path"] ~ /BAT[0-9]+/) || name == BATTERY) {
state_info["percentage"] = props["percentage"] + 0 state_info["percentage"] = props["percentage"] + 0
state_info["time to full"] = string_or(props["time to full"], "Unknown")
state_info["time to empty"] = string_or(props["time to empty"], "Unknown")
return 1 return 1
} else if ((! ADAPTER && props["power supply"] == "yes" && \ } else if ((! ADAPTER && props["power supply"] == "yes" && \
props["native-path"] ~ /ADP[0-9]+/) || name == ADAPTER) { props["native-path"] ~ /ADP[0-9]+/) || name == ADAPTER) {

View File

@ -1,6 +1,61 @@
#!/bin/sh #!/usr/bin/env -S emacs -x
if [ "${#}" -gt 0 ]; then ;; -*- mode: emacs-lisp; lexical-binding: t -*-
config_flags="-c ${1}" (require 'cl-lib)
fi (require 'server)
fcitx5-remote -t (require 'dbus)
eww ${config_flags} update fcitx5-state="$(fcitx5-remote)"
(defun cmdline-for-pid (pid)
"Return the command line arguments passed to PID.
PID can be a string or a number."
(butlast (string-split
(with-temp-buffer
(insert-file-contents-literally
(format "/proc/%s/cmdline" pid))
(buffer-substring-no-properties (point-min)
(point-max)))
"\0")))
(defun current-eww-config-dir ()
"Return the configuration directory for a currently running eww process."
;; This probably only works on Linux
(catch 'found
(dolist (subdir (directory-files "/proc"))
(when (string-match-p (rx bos (+ num) eos) subdir)
(ignore-error permission-denied
(let* ((attrs (file-attributes (format "/proc/%s/exe" subdir)))
(type (file-attribute-type attrs)))
(when (and (stringp type)
(string-match-p (rx (or bos "/") "eww") type))
(cl-maplist (lambda (tail)
(when (equal (car tail) "-c")
(throw 'found (cl-second tail))))
(cmdline-for-pid subdir)))))))))
(defun set-eww-fcitx-state (state)
"Set the Fcitx state for Eww to STATE."
(let ((args (list "update" (format "fcitx5-state=%s" state)))
(cfg-dir (current-eww-config-dir)))
(when cfg-dir
(setq args (nconc (list "-c" cfg-dir) args)))
(apply 'call-process "eww" nil 0 nil args)))
(defun update-waybar ()
(call-process "pkill" nil 0 nil "-RTMIN+1" "waybar"))
(cl-defun has-focused-window-p (&optional (server "server"))
"Return non-nil if SERVER has at least one focused window.
SERVER defaults to \"server\"."
(server-eval-at
server '(cl-some 'frame-focus-state (frame-list))))
(if (has-focused-window-p)
(server-eval-at "server" '(my/global-toggle-mozc))
(dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
"org.fcitx.Fcitx.Controller1" "Toggle")
(let ((state (dbus-call-method :session "org.fcitx.Fcitx5" "/controller"
"org.fcitx.Fcitx.Controller1" "State")))
(set-eww-fcitx-state state)
(update-waybar)))
;; Local Variables:
;; flycheck-disabled-checkers: (emacs-lisp-checkdoc)
;; End:

View File

@ -1,24 +0,0 @@
#!/usr/bin/env zsh
function get-titles-for-monitors {
jq -c --null-input \
--argjson 'monitors' "$(hyprctl -j monitors)" \
--argjson 'workspaces' "$(hyprctl -j workspaces)" \
'[$monitors.[].activeWorkspace.id as $active |
$workspaces.[] | select(.id | IN($active))] |
map({(.monitor): (if .lastwindowtitle == "" then "hyprland" else .lastwindowtitle end)}) | add'
}
function handle {
case "${1}" in
activewindow\>\>*|openwindow*|closewindow*)
get-titles-for-monitors
;;
esac
}
get-titles-for-monitors
socat -U - "unix-connect:/tmp/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" |
while read line; do
handle "${line}"
done

View File

@ -1,70 +0,0 @@
#!/usr/bin/env zsh
function update-workspaces {
if ((${urgent} != 0)); then
jq -e --null-input \
--argjson 'active' "${active}" \
--argjson 'urgent' "${urgent}" \
'any($active.[]; .active == $urgent)' >/dev/null && urgent=0
fi
jq --null-input -c \
--argjson 'existing' "${existing}" \
--argjson 'active_id' "${active}" \
--argjson 'always_show' '[1,2,3,4,5,6,7,8,9]' \
--argjson 'urgent_id' "${urgent}" \
'$existing | map_values([.[], ($always_show.[] |
{id: ., name: . | tostring, windows: 0, monitor: null})] |
unique_by(.name) |
map((.monitor != null and
.id == $active_id.[.monitor].active) as $active |
(.id == $urgent_id and ($active | not) and .monitor != null) as $urgent |
if (((.name | startswith(".")) and ($active | not))
or ((.name == "special") and (.id == -99))) then empty else
.class = (if $active then "hypr-ws-button-active"
else if $urgent then "hypr-ws-button-urgent"
else "hypr-ws-button" end end) |
.changecmd = ((try "~/.config/hypr/scripts/switch-workspace switch \(.name | tonumber)"
catch empty) // "hyprctl dispatch workspace \(.id)") end))'
}
function get-workspaces {
hyprctl -j workspaces | \
jq '[group_by(.monitor).[] | {(.[0].monitor): (. | .[0].monitor as $mon | map({id: .id, windows: .windows, name: .name, monitor: $mon}))}] | add'
}
function get-active {
hyprctl -j monitors | \
jq -c 'map({(.name): {active: .activeWorkspace.id}}) | add'
}
# args: (address: string)
function get-workspace-for-window-address {
hyprctl -j clients | jq --arg 'addr' "0x${1}" \
'.[] | select(.address == $addr) | .workspace.id'
}
function handle {
case "${1}" in
urgent\>\>*)
urgent="$(get-workspace-for-window-address "${1:8}")"
update-workspaces
;;
workspace\>\>*)
active="$(get-active)"
update-workspaces
;;
openwindow*|closewindow*|*workspace*|movewindow*)
existing="$(get-workspaces)"
update-workspaces
;;
esac
}
let urgent=0
local active="$(get-active)"
local existing="$(get-workspaces)"
update-workspaces
socat -U - "unix-connect:/tmp/hypr/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock" |
while read line; do
handle "${line}"
done

View File

@ -1,17 +1,11 @@
#!/usr/bin/env -S emacs -x #!/usr/bin/env zsh
;;; -*- mode: emacs-lisp; lexical-binding: t -*-
(require 'server) let greather_than_zero=0
(princ if [[ "${1}" == '-g' ]]; then
(condition-case _ greather_than_zero=1
(if-let ((modeline-string (server-eval-at "server" '(mu4e--modeline-string))) fi
((string-match "\\([0-9]+\\)\\((\\+[0-9]+)\\)?/[0-9]+ $" modeline-string))
(matched-string (match-string 1 modeline-string))) lines=(${(f)"$(mu find '(maildir:/protonmail/Inbox or maildir:/ucsc-gmail/Inbox) AND flag:unread' 2>/dev/null)"})
(progn if [[ ${#lines} > 0 ]] || ! (( greather_than_zero )); then
(set-text-properties 0 (length matched-string) printf '%d' "${#lines}"
nil fi
matched-string)
matched-string)
"0")
(error
"0")))
(terpri)

View File

@ -1,13 +1,20 @@
#!/usr/bin/env zsh #!/usr/bin/env zsh
let bluetooth=1
if [[ "${1}" == '--no-bluetooth' ]] || [[ "${1}" == '-n' ]]; then
bluetooth=0
fi
if [[ "$(uname)" = 'Linux' ]]; then if [[ "$(uname)" = 'Linux' ]]; then
local active_networks="$(nmcli c s --active)" local active_networks="$(nmcli --fields type c s --active)"
local output='' local output=''
[[ "${active_networks}" = *' wifi '* ]] && output="${output}󰖩 " [[ "${active_networks}" = *'wifi'* ]] && output="${output}󰖩 "
[[ "${active_networks}" = *' ethernet '* ]] && output="${output}󰈁" [[ "${active_networks}" = *'ethernet'* ]] && output="${output}󰈁"
[[ "${active_networks}" = *' wireguard '* ]] && output="${output}󰖂 " [[ "${active_networks}" = *'wireguard'* ]] && output="${output}󰖂 "
(( ${#output} == 0 )) && output='󰈂' (( ${#output} == 0 )) && output='󰈂'
if (( bluetooth )); then
bluetoothctl show | grep 'Powered: yes' >/dev/null && output="${output}  " bluetoothctl show | grep 'Powered: yes' >/dev/null && output="${output}  "
fi
printf '%s\n' "${output}" printf '%s\n' "${output}"
else else
echo "${0}: error: unknown os: \"$(uname)\"" >&2 echo "${0}: error: unknown os: \"$(uname)\"" >&2

View File

@ -5,10 +5,12 @@ function print-volume {
local icon local icon
if [[ "$(pamixer --get-mute)" = "true" ]]; then if [[ "$(pamixer --get-mute)" = "true" ]]; then
icon='󰸈' icon='󰸈'
elif ((${volume} > 50)); then elif (( ${volume} >= 50 )); then
icon='󰕾' icon='󰕾'
elif ((${volume} >= 0)); then elif ((${volume} > 0)); then
icon='󰖀' icon='󰖀'
elif ((${volume} == 0)); then
icon='󰕿'
else else
icon='' icon=''
fi fi

35
games/sse-nxmhandler-wrapper.sh Executable file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env zsh
# WARNING!! This file is not well written. It probably only works on my setup,
# with Lutris thinking it's running Skyrim from GOG, but really running MO2
local conf_file="${HOME}/.config/lutris/games/the_elder_scrolls_v_skyrim_special_edition-1703312623.yml"
local wine_ver="$(cat "${conf_file}" | yq -r ".wine.version")"
local wine_bin
if [[ -d "${HOME}/.local/share/Steam/compatibilitytools.d/${wine_ver}" ]]; then
wine_bin="${HOME}/.local/share/Steam/compatibilitytools.d/${wine_ver}/files/bin"
elif [[ -d "${HOME}/.local/share/lutris/runners/wine/${wine_ver}" ]]; then
wine_bin="${HOME}/.local/share/lutris/runners/wine/${wine_ver}/bin"
fi
local wine="${wine_bin}/wine"
local wineserver="${wine_bin}/wineserver"
let server_pid="$(pgrep -xf "${wineserver}" | head -n1)"
local server_env_arr=(${(0)"$(</proc/${server_pid}/environ)"})
typeset -A server_env
for entry in ${server_env_arr}; do
local parts=(${(ps:=:)entry})
eval "server_env[${(q)parts[1]}]=${(q)${(j:=:)${parts:1}}}"
done
for var in DISPLAY WINEPREFIX WINEFSYNC; do
export "${var}=${server_env[$var]}"
done
"${wine}" "C:\\Modding\\MO2\\nxmhandler.exe" "${@}"

60
setup-qbittorent.zsh Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env zsh
if [[ "${1}" == '-h' ]] || [[ "${1}" == '--help' ]]; then
printf 'usage: %s [-R|-D]\n' "${0}"
printf ' -R|-D: remove rules currently in place (default is to add new rules)\n'
exit
fi
setopt pipe_fail
local save_file="${HOME}/.cache/qBittorrent-iptables-save"
local op='-A'
if [[ "${1}" == '-D' || "${1}" == '-R' ]]; then
if ! [[ -f "${save_file}" ]]; then
echo 'No current rules found!'
exit 1
fi
op="-D"
fi
local iface="$(ip route | grep '^default' | grep -Po '(?<=dev )[^ ]+')"
printf 'Using interface: %s\n' "${iface}"
# <-A|-D> <ex_ip> <in_ip>
function do_rules {
emulate -L zsh
PS4='Run: '
setopt errexit xtrace
doas iptables -t filter "${1}" FORWARD -i "${iface}" -o wg0-mullvad -j ACCEPT
doas iptables -t nat "${1}" PREROUTING -d "${2}"/32 -p tcp -m tcp --dport 62000 \
-j DNAT --to-destination "${3}":62000
doas iptables -t nat "${1}" PREROUTING -d "${2}"/32 -p udp -m udp --dport 62000 \
-j DNAT --to-destination "${3}":62000
doas iptables -t nat "${1}" POSTROUTING -d "${3}"/32 -p tcp -m tcp --sport 62000 \
-j SNAT --to-source "${2}":62000
doas iptables -t nat "${1}" POSTROUTING -d "${3}"/32 -p udp -m udp --sport 62000 \
-j SNAT --to-source "${2}":62000
}
if [[ -f "${save_file}" ]]; then
local content="$(<"${save_file}")"
local lines=("${(@f)content}")
printf 'Old rules found for\nex_ip: %s\nin_ip: %s\n' "${lines[1]}" "${lines[2]}"
printf 'Removing...\n'
do_rules -D "${lines[1]}" "${lines[2]}"
rm -f "${save_file}"
printf 'Done!\n'
[[ "${op}" == '-D' ]] && exit
fi
local ex_ip in_ip
ex_ip="$(curl -4 icanhazip.com)" || { echo 'Could not fetch ip!'; exit 1 }
in_ip="$(ip addr show dev wg0-mullvad | \
awk '/^ *inet [0-9]+/ { print substr($2,0,index($2,"/") - 1) }')" ||
{ echo 'Could not find wireguard iterface address!'; exit 1 }
printf 'Adding rules for:\nex_ip: %s\nin_ip: %s\n' "${ex_ip}" "${in_ip}"
printf '%s\n%s\n' "${ex_ip}" "${in_ip}" >"${save_file}"
do_rules -A "${ex_ip}" "${in_ip}"

View File

@ -24,10 +24,12 @@ function notify_vol {
local icon local icon
if [[ "$(pamixer --get-mute)" == 'true' ]]; then if [[ "$(pamixer --get-mute)" == 'true' ]]; then
icon='󰸈' icon='󰸈'
elif (( ${vol} > 50 )); then elif (( ${vol} >= 50 )); then
icon='󰕾' icon='󰕾'
elif (( ${vol} >= 0 )); then elif ((${vol} > 0)); then
icon='󰖀' icon='󰖀'
elif ((${vol} == 0)); then
icon='󰕿'
else else
icon='' icon=''
fi fi

View File

@ -14,19 +14,27 @@ function has-rootful-xwayland-p {
function run { function run {
# ensure multiple instances do not run # ensure multiple instances do not run
mkdir -p "$(dirname "${LOCKFILE}")" mkdir -p "${LOCKFILE:h}"
exec 4<>"${LOCKFILE}" exec 4<>"${LOCKFILE}"
flock -n 4 || exit 0 flock -n 4 || exit 0
dunstctl set-paused true dunstctl set-paused true
if has-rootful-xwayland-p; then if [[ "${XDG_CURRENT_DESKTOP}" == 'river' ]] && has-rootful-xwayland-p; then
swaylock ${empty_flag} --color '#000000' swaylock ${empty_flag} --color '#000000'
else else
# get the list of currently disabled monitors so we don't touch them
local ignored=("${(0)$(enable-displays-for-sleep -l)}")
local ignored_args=()
for name in ${ignored}; do
# quote so it can be safely passed to swayidle
ignored_args+=(-i "${(q)name}")
done
swayidle -w -C /dev/null \ swayidle -w -C /dev/null \
timeout 15 "${suspend_command}" resume "${wake_command}" & timeout 15 "enable-displays-for-sleep -d ${ignored}" \
resume "enable-displays-for-sleep {$ignored}" &
local swayidle_pid="${!}" local swayidle_pid="${!}"
swaylock ${empty_flag} ${img_flags} swaylock ${empty_flag} ${img_flags}
kill "${swayidle_pid}" kill "${swayidle_pid}"
eval "${wake_command}"
fi fi
dunstctl set-paused false dunstctl set-paused false
fix_eww fix_eww
@ -57,14 +65,6 @@ while [[ "${1}" =~ '^-' ]]; do
fix_eww+="${2}" fix_eww+="${2}"
shift shift
;; ;;
-s)
local suspend_command="${2}"
shift
;;
-w)
local wake_command="${2}"
shift
;;
-e) -e)
empty_flag='-e' empty_flag='-e'
;; ;;
@ -75,18 +75,6 @@ while [[ "${1}" =~ '^-' ]]; do
shift shift
done done
if [[ -z "${suspend_command}" ]]; then
# default to turning off all displays
suspend_command="wlr-randr $(wlr-randr --json |
jq -r '[.[] | .modes = [.modes.[] |
select(.current)]] |
map("--output ''\(.name)'' --off") | join(" ")')"
fi
if [[ -z "${wake_command}" ]]; then
wake_command="wlr-randr $(wlr-randr --json | jq -r '[.[]| select(.enabled) | .modes = [.modes.[] | select(.current)].[]] | map("--output ''\(.name)'' --on --mode ''\(.modes.width)x\(.modes.height)@\(.modes.refresh)Hz'' --pos ''\(.position.x),\(.position.y)'' --transform \(.transform) --scale \(.scale)") | join(" ")')"
fi
(( ${#} != 0 )) && img_flags=(-s fill -i "${1}") (( ${#} != 0 )) && img_flags=(-s fill -i "${1}")
if ${background}; then if ${background}; then

14
system-menu/confirm-logout.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env zsh
local resp="$(printf 'No\nYes\n' | fuzzel --dmenu --prompt 'Really Logout> ')"
if [[ "${resp}" == 'Yes' ]]; then
case "${XDG_CURRENT_DESKTOP}" in
Hyprland)
hyprctl dispatch exit
;;
river)
riverctl exit
;;
esac
fi

View File

@ -0,0 +1,6 @@
#!/usr/bin/env zsh
killall eww 2>/dev/null
pkill -f 'sbcl.+.config/+river/+config/+display-listener.lisp' 2>/dev/null
exec "${HOME}/.config/river/config/display-listener.lisp"

View File

@ -5,9 +5,11 @@ function is-laptop-p {
} }
function is-desktop-p { function is-desktop-p {
! is-laptop ! is-laptop-p
} }
echo $(is-desktop-p)
pgrep swayidle && swayidle_state="Enabled" || swayidle_state="Disabled" pgrep swayidle && swayidle_state="Enabled" || swayidle_state="Disabled"
# Format: label action condition # Format: label action condition
@ -16,7 +18,10 @@ local entries=('Select system sound output' 'select-sound-output.sh' 'true'
'Enable or disable TV' 'tv-power-menu.sh' 'is-desktop-p' 'Enable or disable TV' 'tv-power-menu.sh' 'is-desktop-p'
'Configure USB device access' 'usbguard-menu.py' 'pgrep usbguard-daemon' 'Configure USB device access' 'usbguard-menu.py' 'pgrep usbguard-daemon'
'Power settings (restart and shutdown)' 'system-power-menu.sh' 'true' 'Power settings (restart and shutdown)' 'system-power-menu.sh' 'true'
'Login to captive portal protected WiFi' 'login-to-wifi.sh' 'is-laptop-p') 'Login to captive portal protected WiFi' 'login-to-wifi.sh' 'is-laptop-p'
# I'm not using eww right now
# 'Restart top bar' 'river-restart-eww.sh' '[[ "${XDG_CURRENT_DESKTOP}" == river ]]'
)
local entry_array=() local entry_array=()
local enabled_entries=() local enabled_entries=()

View File

@ -18,11 +18,12 @@ case "${choice}" in
0) 0)
if [[ "${swayidle_state}" == 'Disabled' ]]; then if [[ "${swayidle_state}" == 'Disabled' ]]; then
local cmd_cache=(${(0)"$(<"${CACHE_PATH}")"}) local cmd_cache=(${(0)"$(<"${CACHE_PATH}")"})
( {
set -x set -x
cd "${cmd_cache[1]}" cd "${cmd_cache[1]}"
eval "${cmd_cache[2]}" & exec ${=${cmd_cache[2]}}
) } &
disown
fi fi
;; ;;
1) 1)
@ -36,3 +37,10 @@ case "${choice}" in
fi fi
;; ;;
esac esac
if [[ "${XDG_CURRENT_DESKTOP}" == 'river' ]]; then
eww -c "${HOME}/.config/river/config/" update swayidle="$(( ! ${choice} ))"
pkill -RTMIN+3 waybar
elif [[ "${XDG_CURRENT_DESKTOP}" == 'Hyprland' ]]; then
pkill -SIGRTMIN+1 waybar
fi

View File

@ -1,6 +1,9 @@
#!/usr/bin/env zsh #!/usr/bin/env zsh
choice="$(fuzzel --index -d <<'EOF' # Remember: for the shell's if, 0 is true and anything else is false!
current="$(wlr-randr --json | jq --raw-output 'if .[] | select(.name == "HDMI-A-1").enabled then "Enabled" else "Disabled" end')"
choice="$(fuzzel --index -p "Cur: ${current} > " -d <<'EOF'
Enable Enable
Disable Disable
EOF EOF
@ -10,9 +13,9 @@ if (( ${?} != 0 )) || (( "${choice}" == -1 )); then
fi fi
case "${choice}" in case "${choice}" in
0) 0)
wlr-randr --output DP-3 --mode 1920x1080@60 --scale 1 --on kanshictl switch tv
;; ;;
1) 1)
wlr-randr --output DP-3 --off kanshictl switch no-tv
;; ;;
esac esac

View File

@ -5,19 +5,19 @@ import re
import threading import threading
from subprocess import Popen, run, DEVNULL, PIPE from subprocess import Popen, run, DEVNULL, PIPE
USBGUARD_EXEC_NAME = shutil.which('usbguard') USBGUARD_EXEC_NAME = shutil.which("usbguard")
DUNSTIFY_EXEC_NAME = shutil.which('dunstify') DUNSTIFY_EXEC_NAME = shutil.which("dunstify")
open_notifications = {} open_notifications = {}
def parse_event_type_and_id(stream): def parse_event_type_and_id(stream):
line = stream.readline() line = stream.readline()
if not line.startswith('[device] '): if not line.startswith("[device] "):
return None return None
event_type = re.findall('(?<=\\[device\\] )[a-zA-Z]+', line) event_type = re.findall("(?<=\\[device\\] )[a-zA-Z]+", line)
if len(event_type) == 0: if len(event_type) == 0:
return None return None
event_id = re.findall('(?<=id=)[0-9]+', line) event_id = re.findall("(?<=id=)[0-9]+", line)
if len(event_id) == 0: if len(event_id) == 0:
return None return None
return event_type[0], int(event_id[0]) return event_type[0], int(event_id[0])
@ -28,10 +28,10 @@ def parse_event_properties(stream, count):
for _ in range(count): for _ in range(count):
line = stream.readline() line = stream.readline()
try: try:
sep_ind = line.index('=') sep_ind = line.index("=")
prop_name = line[1:sep_ind] prop_name = line[1:sep_ind]
props[prop_name] = line[sep_ind + 1:-1] props[prop_name] = line[sep_ind + 1 : -1]
if prop_name == 'device_rule': if prop_name == "device_rule":
break break
except ValueError: except ValueError:
continue continue
@ -41,25 +41,35 @@ def parse_event_properties(stream, count):
def get_name_and_id_from_rule(rule): def get_name_and_id_from_rule(rule):
name = re.findall('(?<=name ")[^"]+(?=")', rule) name = re.findall('(?<=name ")[^"]+(?=")', rule)
if len(name) == 0: if len(name) == 0:
name = '' name = ""
else: else:
name = name[0] name = name[0]
id = re.findall('(?<=id )[a-z0-9]{4}:[a-z0-9]{4}', rule) id = re.findall("(?<=id )[a-z0-9]{4}:[a-z0-9]{4}", rule)
if len(id) == 0: if len(id) == 0:
id = '' id = ""
else: else:
id = id[0] id = id[0]
return name, id return name, id
def prompt_device_action(dev_id, name, long_id): def prompt_device_action(dev_id, name, long_id):
proc = Popen([DUNSTIFY_EXEC_NAME, '-p', proc = Popen(
'-A', 'block,Block', [
'-A', 'allow,Allow', DUNSTIFY_EXEC_NAME,
'-A', 'reject,Reject', "-p",
f'{name} ({long_id})', "-A",
'New Device'], "block,Block",
stdout=PIPE, text=True, bufsize=0) "-A",
"allow,Allow",
"-A",
"reject,Reject",
f"{name} ({long_id})",
"New Device",
],
stdout=PIPE,
text=True,
bufsize=0,
)
open_notifications[dev_id] = int(proc.stdout.readline()) open_notifications[dev_id] = int(proc.stdout.readline())
option = proc.communicate()[0][:-1] option = proc.communicate()[0][:-1]
try: try:
@ -67,24 +77,29 @@ def prompt_device_action(dev_id, name, long_id):
except KeyError: except KeyError:
pass pass
match option: match option:
case 'reject': case "reject":
run([USBGUARD_EXEC_NAME, 'reject-device', long_id]) run([USBGUARD_EXEC_NAME, "reject-device", long_id])
case 'allow': case "allow":
run([USBGUARD_EXEC_NAME, 'allow-device', long_id]) run([USBGUARD_EXEC_NAME, "allow-device", long_id])
case _: case _:
run([USBGUARD_EXEC_NAME, 'block-device', long_id]) run([USBGUARD_EXEC_NAME, "block-device", long_id])
def close_notification(dev_id): def close_notification(dev_id):
if dev_id in open_notifications: if dev_id in open_notifications:
notif_id = open_notifications.pop(dev_id) notif_id = open_notifications.pop(dev_id)
run([DUNSTIFY_EXEC_NAME, '-C', str(notif_id)]) run([DUNSTIFY_EXEC_NAME, "-C", str(notif_id)])
with Popen([USBGUARD_EXEC_NAME, 'watch'], with Popen(
stdin=DEVNULL, stdout=PIPE, text=True, bufsize=0) as usbguard_proc: [USBGUARD_EXEC_NAME, "watch"],
stdin=DEVNULL,
stdout=PIPE,
text=True,
bufsize=0,
) as usbguard_proc:
new_devices = set() new_devices = set()
usbguard_proc.stdout.readline() # get rid of initial connection message usbguard_proc.stdout.readline() # get rid of initial connection message
while True: while True:
@ -92,19 +107,21 @@ with Popen([USBGUARD_EXEC_NAME, 'watch'],
if event_type_result is None: if event_type_result is None:
continue continue
event_type, dev_id = event_type_result event_type, dev_id = event_type_result
if event_type not in ['PresenceChanged', 'PolicyApplied']: if event_type not in ["PresenceChanged", "PolicyApplied"]:
continue continue
props = parse_event_properties(usbguard_proc.stdout, 3) props = parse_event_properties(usbguard_proc.stdout, 3)
name, long_id = get_name_and_id_from_rule(props['device_rule']) name, long_id = get_name_and_id_from_rule(props["device_rule"])
match event_type: match event_type:
case 'PresenceChanged': case "PresenceChanged":
if props['event'] == 'Insert': if props["event"] == "Insert":
new_devices.add(dev_id) new_devices.add(dev_id)
else: else:
close_notification(dev_id) close_notification(dev_id)
new_devices.discard(dev_id) new_devices.discard(dev_id)
case 'PolicyApplied': case "PolicyApplied":
if props['target_new'] == 'block': if props["target_new"] == "block":
threading.Thread(target=prompt_device_action, threading.Thread(
args=(dev_id, name, long_id)).start() target=prompt_device_action,
args=(dev_id, name, long_id),
).start()
new_devices.discard(dev_id) new_devices.discard(dev_id)

View File

@ -15,7 +15,7 @@ function die {
# print_help <argv[0]> # print_help <argv[0]>
function print_help { function print_help {
local exec_name="$(basename "${1}")" local exec_name="${1:t}"
printf 'usage: %s [OPTIONS...] <GAME> [ARGS...]\n' "${exec_name}" printf 'usage: %s [OPTIONS...] <GAME> [ARGS...]\n' "${exec_name}"
printf 'Run a game inside an Xwayland session with a window manager. This\n' printf 'Run a game inside an Xwayland session with a window manager. This\n'
printf 'is very similar to gamescope, except that there is a window\n' printf 'is very similar to gamescope, except that there is a window\n'
@ -121,7 +121,7 @@ let cat_pid="${!}"
sh -c "exec -- ${window_manager}" & sh -c "exec -- ${window_manager}" &
function { function {
emulate -L sh emulate -L sh
eval "${@}" "${@}"
} "${@}" } "${@}"
let child_error_code="${?}" let child_error_code="${?}"
if (( ${child_error_code} )); then if (( ${child_error_code} )); then