Compare commits
29 Commits
2dcc8b409f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
a3aba45b6e
|
|||
|
e0dad7f7dd
|
|||
|
99f991728b
|
|||
|
5b551a4075
|
|||
|
d7fbed68bf
|
|||
|
48b6b0afde
|
|||
|
204035cb8c
|
|||
|
59e6c14c1b
|
|||
|
2ee4b47086
|
|||
|
f763fc1188
|
|||
|
42ea5f1370
|
|||
|
4f0093975b
|
|||
|
ad3a00266a
|
|||
|
43a8d7f2a7
|
|||
|
37a87cc34e
|
|||
|
ed0b55c3b4
|
|||
|
a0d2b83c4f
|
|||
|
01fe96b46f
|
|||
|
a4106633c8
|
|||
|
c8657e9378
|
|||
|
fef223a62f
|
|||
|
ee1321e857
|
|||
|
f40f9f5277
|
|||
|
d91c7f00ba
|
|||
|
ebd8d9ef2c
|
|||
|
3c3782df42
|
|||
|
34693cf757
|
|||
|
477caac066
|
|||
|
342dd058ee
|
133
cl/inhibit-sleep-for-audio.lisp
Normal file
133
cl/inhibit-sleep-for-audio.lisp
Normal 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))
|
||||
@ -36,6 +36,16 @@
|
||||
(subseq str 0 (1- len))
|
||||
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)
|
||||
(with-open-file (stream path :direction :input :if-does-not-exist nil)
|
||||
(when stream
|
||||
@ -49,7 +59,10 @@
|
||||
while line
|
||||
do (cond
|
||||
((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)
|
||||
(setq in-valarm t))
|
||||
((uiop:string-prefix-p "END:VALARM" line)
|
||||
@ -70,7 +83,8 @@
|
||||
((equal unit "D") (* num 60 60 24))
|
||||
(t 0))))))
|
||||
((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))))
|
||||
finally (return (mapcar (lambda (alarm)
|
||||
(list (if (uiop:emptyp (car alarm))
|
||||
@ -115,7 +129,8 @@
|
||||
:time alarm-time
|
||||
:event-title event-title
|
||||
:event-end end
|
||||
:uid uid)))) alarms)))))))
|
||||
:uid uid))))
|
||||
alarms)))))))
|
||||
|
||||
(defun build-alarm-list (calendar-dirs &optional exclude-before)
|
||||
(let* ((output (run-khal "list"
|
||||
|
||||
46
enable-displays-for-sleep
Executable file
46
enable-displays-for-sleep
Executable 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
|
||||
@ -56,13 +56,24 @@ function warn_if_low() {
|
||||
|
||||
function print_state() {
|
||||
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),
|
||||
state_info["charging"] ? "" : "",
|
||||
percentage
|
||||
percentage,
|
||||
tooltip
|
||||
fflush()
|
||||
}
|
||||
|
||||
function string_or(str, def) {
|
||||
return str == "" ? def : str
|
||||
}
|
||||
|
||||
function parse_record(record, exit_on_absent) {
|
||||
split(record, fields)
|
||||
for (i in fields) {
|
||||
@ -77,6 +88,8 @@ function parse_record(record, exit_on_absent) {
|
||||
} else if ((! BATTERY && props["power supply"] == "yes" && \
|
||||
props["native-path"] ~ /BAT[0-9]+/) || name == BATTERY) {
|
||||
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
|
||||
} else if ((! ADAPTER && props["power supply"] == "yes" && \
|
||||
props["native-path"] ~ /ADP[0-9]+/) || name == ADAPTER) {
|
||||
|
||||
@ -1,6 +1,61 @@
|
||||
#!/bin/sh
|
||||
if [ "${#}" -gt 0 ]; then
|
||||
config_flags="-c ${1}"
|
||||
fi
|
||||
fcitx5-remote -t
|
||||
eww ${config_flags} update fcitx5-state="$(fcitx5-remote)"
|
||||
#!/usr/bin/env -S emacs -x
|
||||
;; -*- mode: emacs-lisp; lexical-binding: t -*-
|
||||
(require 'cl-lib)
|
||||
(require 'server)
|
||||
(require 'dbus)
|
||||
|
||||
(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:
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -1,17 +1,11 @@
|
||||
#!/usr/bin/env -S emacs -x
|
||||
;;; -*- mode: emacs-lisp; lexical-binding: t -*-
|
||||
(require 'server)
|
||||
(princ
|
||||
(condition-case _
|
||||
(if-let ((modeline-string (server-eval-at "server" '(mu4e--modeline-string)))
|
||||
((string-match "\\([0-9]+\\)\\((\\+[0-9]+)\\)?/[0-9]+ $" modeline-string))
|
||||
(matched-string (match-string 1 modeline-string)))
|
||||
(progn
|
||||
(set-text-properties 0 (length matched-string)
|
||||
nil
|
||||
matched-string)
|
||||
matched-string)
|
||||
"0")
|
||||
(error
|
||||
"0")))
|
||||
(terpri)
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
let greather_than_zero=0
|
||||
if [[ "${1}" == '-g' ]]; then
|
||||
greather_than_zero=1
|
||||
fi
|
||||
|
||||
lines=(${(f)"$(mu find '(maildir:/protonmail/Inbox or maildir:/ucsc-gmail/Inbox) AND flag:unread' 2>/dev/null)"})
|
||||
if [[ ${#lines} > 0 ]] || ! (( greather_than_zero )); then
|
||||
printf '%d' "${#lines}"
|
||||
fi
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
let bluetooth=1
|
||||
if [[ "${1}" == '--no-bluetooth' ]] || [[ "${1}" == '-n' ]]; then
|
||||
bluetooth=0
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" = 'Linux' ]]; then
|
||||
local active_networks="$(nmcli c s --active)"
|
||||
local active_networks="$(nmcli --fields type c s --active)"
|
||||
local output=''
|
||||
[[ "${active_networks}" = *' wifi '* ]] && output="${output} "
|
||||
[[ "${active_networks}" = *' ethernet '* ]] && output="${output}"
|
||||
[[ "${active_networks}" = *' wireguard '* ]] && output="${output} "
|
||||
[[ "${active_networks}" = *'wifi'* ]] && output="${output} "
|
||||
[[ "${active_networks}" = *'ethernet'* ]] && output="${output}"
|
||||
[[ "${active_networks}" = *'wireguard'* ]] && output="${output} "
|
||||
(( ${#output} == 0 )) && output=''
|
||||
if (( bluetooth )); then
|
||||
bluetoothctl show | grep 'Powered: yes' >/dev/null && output="${output} "
|
||||
fi
|
||||
printf '%s\n' "${output}"
|
||||
else
|
||||
echo "${0}: error: unknown os: \"$(uname)\"" >&2
|
||||
|
||||
@ -5,10 +5,12 @@ function print-volume {
|
||||
local icon
|
||||
if [[ "$(pamixer --get-mute)" = "true" ]]; then
|
||||
icon=''
|
||||
elif ((${volume} > 50)); then
|
||||
elif (( ${volume} >= 50 )); then
|
||||
icon=''
|
||||
elif ((${volume} >= 0)); then
|
||||
elif ((${volume} > 0)); then
|
||||
icon=''
|
||||
elif ((${volume} == 0)); then
|
||||
icon=''
|
||||
else
|
||||
icon='?'
|
||||
fi
|
||||
|
||||
35
games/sse-nxmhandler-wrapper.sh
Executable file
35
games/sse-nxmhandler-wrapper.sh
Executable 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
60
setup-qbittorent.zsh
Executable 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}"
|
||||
@ -24,10 +24,12 @@ function notify_vol {
|
||||
local icon
|
||||
if [[ "$(pamixer --get-mute)" == 'true' ]]; then
|
||||
icon=''
|
||||
elif (( ${vol} > 50 )); then
|
||||
elif (( ${vol} >= 50 )); then
|
||||
icon=''
|
||||
elif (( ${vol} >= 0 )); then
|
||||
elif ((${vol} > 0)); then
|
||||
icon=''
|
||||
elif ((${vol} == 0)); then
|
||||
icon=''
|
||||
else
|
||||
icon='?'
|
||||
fi
|
||||
|
||||
@ -14,19 +14,27 @@ function has-rootful-xwayland-p {
|
||||
|
||||
function run {
|
||||
# ensure multiple instances do not run
|
||||
mkdir -p "$(dirname "${LOCKFILE}")"
|
||||
mkdir -p "${LOCKFILE:h}"
|
||||
exec 4<>"${LOCKFILE}"
|
||||
flock -n 4 || exit 0
|
||||
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'
|
||||
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 \
|
||||
timeout 15 "${suspend_command}" resume "${wake_command}" &
|
||||
timeout 15 "enable-displays-for-sleep -d ${ignored}" \
|
||||
resume "enable-displays-for-sleep {$ignored}" &
|
||||
local swayidle_pid="${!}"
|
||||
swaylock ${empty_flag} ${img_flags}
|
||||
kill "${swayidle_pid}"
|
||||
eval "${wake_command}"
|
||||
fi
|
||||
dunstctl set-paused false
|
||||
fix_eww
|
||||
@ -57,14 +65,6 @@ while [[ "${1}" =~ '^-' ]]; do
|
||||
fix_eww+="${2}"
|
||||
shift
|
||||
;;
|
||||
-s)
|
||||
local suspend_command="${2}"
|
||||
shift
|
||||
;;
|
||||
-w)
|
||||
local wake_command="${2}"
|
||||
shift
|
||||
;;
|
||||
-e)
|
||||
empty_flag='-e'
|
||||
;;
|
||||
@ -75,18 +75,6 @@ while [[ "${1}" =~ '^-' ]]; do
|
||||
shift
|
||||
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}")
|
||||
|
||||
if ${background}; then
|
||||
|
||||
14
system-menu/confirm-logout.sh
Executable file
14
system-menu/confirm-logout.sh
Executable 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
|
||||
6
system-menu/river-restart-eww.sh
Executable file
6
system-menu/river-restart-eww.sh
Executable 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"
|
||||
@ -5,9 +5,11 @@ function is-laptop-p {
|
||||
}
|
||||
|
||||
function is-desktop-p {
|
||||
! is-laptop
|
||||
! is-laptop-p
|
||||
}
|
||||
|
||||
echo $(is-desktop-p)
|
||||
|
||||
pgrep swayidle && swayidle_state="Enabled" || swayidle_state="Disabled"
|
||||
|
||||
# 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'
|
||||
'Configure USB device access' 'usbguard-menu.py' 'pgrep usbguard-daemon'
|
||||
'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 enabled_entries=()
|
||||
|
||||
@ -18,11 +18,12 @@ case "${choice}" in
|
||||
0)
|
||||
if [[ "${swayidle_state}" == 'Disabled' ]]; then
|
||||
local cmd_cache=(${(0)"$(<"${CACHE_PATH}")"})
|
||||
(
|
||||
{
|
||||
set -x
|
||||
cd "${cmd_cache[1]}"
|
||||
eval "${cmd_cache[2]}" &
|
||||
)
|
||||
exec ${=${cmd_cache[2]}}
|
||||
} &
|
||||
disown
|
||||
fi
|
||||
;;
|
||||
1)
|
||||
@ -36,3 +37,10 @@ case "${choice}" in
|
||||
fi
|
||||
;;
|
||||
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
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#!/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
|
||||
Disable
|
||||
EOF
|
||||
@ -10,9 +13,9 @@ if (( ${?} != 0 )) || (( "${choice}" == -1 )); then
|
||||
fi
|
||||
case "${choice}" in
|
||||
0)
|
||||
wlr-randr --output DP-3 --mode 1920x1080@60 --scale 1 --on
|
||||
kanshictl switch tv
|
||||
;;
|
||||
1)
|
||||
wlr-randr --output DP-3 --off
|
||||
kanshictl switch no-tv
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -5,19 +5,19 @@ import re
|
||||
import threading
|
||||
from subprocess import Popen, run, DEVNULL, PIPE
|
||||
|
||||
USBGUARD_EXEC_NAME = shutil.which('usbguard')
|
||||
DUNSTIFY_EXEC_NAME = shutil.which('dunstify')
|
||||
USBGUARD_EXEC_NAME = shutil.which("usbguard")
|
||||
DUNSTIFY_EXEC_NAME = shutil.which("dunstify")
|
||||
open_notifications = {}
|
||||
|
||||
|
||||
def parse_event_type_and_id(stream):
|
||||
line = stream.readline()
|
||||
if not line.startswith('[device] '):
|
||||
if not line.startswith("[device] "):
|
||||
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:
|
||||
return None
|
||||
event_id = re.findall('(?<=id=)[0-9]+', line)
|
||||
event_id = re.findall("(?<=id=)[0-9]+", line)
|
||||
if len(event_id) == 0:
|
||||
return None
|
||||
return event_type[0], int(event_id[0])
|
||||
@ -28,10 +28,10 @@ def parse_event_properties(stream, count):
|
||||
for _ in range(count):
|
||||
line = stream.readline()
|
||||
try:
|
||||
sep_ind = line.index('=')
|
||||
sep_ind = line.index("=")
|
||||
prop_name = line[1:sep_ind]
|
||||
props[prop_name] = line[sep_ind + 1:-1]
|
||||
if prop_name == 'device_rule':
|
||||
props[prop_name] = line[sep_ind + 1 : -1]
|
||||
if prop_name == "device_rule":
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
@ -41,25 +41,35 @@ def parse_event_properties(stream, count):
|
||||
def get_name_and_id_from_rule(rule):
|
||||
name = re.findall('(?<=name ")[^"]+(?=")', rule)
|
||||
if len(name) == 0:
|
||||
name = ''
|
||||
name = ""
|
||||
else:
|
||||
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:
|
||||
id = ''
|
||||
id = ""
|
||||
else:
|
||||
id = id[0]
|
||||
return name, id
|
||||
|
||||
|
||||
def prompt_device_action(dev_id, name, long_id):
|
||||
proc = Popen([DUNSTIFY_EXEC_NAME, '-p',
|
||||
'-A', 'block,Block',
|
||||
'-A', 'allow,Allow',
|
||||
'-A', 'reject,Reject',
|
||||
f'{name} ({long_id})',
|
||||
'New Device'],
|
||||
stdout=PIPE, text=True, bufsize=0)
|
||||
proc = Popen(
|
||||
[
|
||||
DUNSTIFY_EXEC_NAME,
|
||||
"-p",
|
||||
"-A",
|
||||
"block,Block",
|
||||
"-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())
|
||||
option = proc.communicate()[0][:-1]
|
||||
try:
|
||||
@ -67,24 +77,29 @@ def prompt_device_action(dev_id, name, long_id):
|
||||
except KeyError:
|
||||
pass
|
||||
match option:
|
||||
case 'reject':
|
||||
run([USBGUARD_EXEC_NAME, 'reject-device', long_id])
|
||||
case "reject":
|
||||
run([USBGUARD_EXEC_NAME, "reject-device", long_id])
|
||||
|
||||
case 'allow':
|
||||
run([USBGUARD_EXEC_NAME, 'allow-device', long_id])
|
||||
case "allow":
|
||||
run([USBGUARD_EXEC_NAME, "allow-device", long_id])
|
||||
|
||||
case _:
|
||||
run([USBGUARD_EXEC_NAME, 'block-device', long_id])
|
||||
run([USBGUARD_EXEC_NAME, "block-device", long_id])
|
||||
|
||||
|
||||
def close_notification(dev_id):
|
||||
if dev_id in open_notifications:
|
||||
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'],
|
||||
stdin=DEVNULL, stdout=PIPE, text=True, bufsize=0) as usbguard_proc:
|
||||
with Popen(
|
||||
[USBGUARD_EXEC_NAME, "watch"],
|
||||
stdin=DEVNULL,
|
||||
stdout=PIPE,
|
||||
text=True,
|
||||
bufsize=0,
|
||||
) as usbguard_proc:
|
||||
new_devices = set()
|
||||
usbguard_proc.stdout.readline() # get rid of initial connection message
|
||||
while True:
|
||||
@ -92,19 +107,21 @@ with Popen([USBGUARD_EXEC_NAME, 'watch'],
|
||||
if event_type_result is None:
|
||||
continue
|
||||
event_type, dev_id = event_type_result
|
||||
if event_type not in ['PresenceChanged', 'PolicyApplied']:
|
||||
if event_type not in ["PresenceChanged", "PolicyApplied"]:
|
||||
continue
|
||||
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:
|
||||
case 'PresenceChanged':
|
||||
if props['event'] == 'Insert':
|
||||
case "PresenceChanged":
|
||||
if props["event"] == "Insert":
|
||||
new_devices.add(dev_id)
|
||||
else:
|
||||
close_notification(dev_id)
|
||||
new_devices.discard(dev_id)
|
||||
case 'PolicyApplied':
|
||||
if props['target_new'] == 'block':
|
||||
threading.Thread(target=prompt_device_action,
|
||||
args=(dev_id, name, long_id)).start()
|
||||
case "PolicyApplied":
|
||||
if props["target_new"] == "block":
|
||||
threading.Thread(
|
||||
target=prompt_device_action,
|
||||
args=(dev_id, name, long_id),
|
||||
).start()
|
||||
new_devices.discard(dev_id)
|
||||
|
||||
@ -15,7 +15,7 @@ function die {
|
||||
|
||||
# print_help <argv[0]>
|
||||
function print_help {
|
||||
local exec_name="$(basename "${1}")"
|
||||
local exec_name="${1:t}"
|
||||
printf 'usage: %s [OPTIONS...] <GAME> [ARGS...]\n' "${exec_name}"
|
||||
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'
|
||||
@ -121,7 +121,7 @@ let cat_pid="${!}"
|
||||
sh -c "exec -- ${window_manager}" &
|
||||
function {
|
||||
emulate -L sh
|
||||
eval "${@}"
|
||||
"${@}"
|
||||
} "${@}"
|
||||
let child_error_code="${?}"
|
||||
if (( ${child_error_code} )); then
|
||||
|
||||
Reference in New Issue
Block a user