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))
|
(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)
|
||||||
@ -59,18 +72,19 @@
|
|||||||
current-notice ""))
|
current-notice ""))
|
||||||
((and in-valarm (uiop:string-prefix-p "TRIGGER:" line))
|
((and in-valarm (uiop:string-prefix-p "TRIGGER:" line))
|
||||||
(ppcre:register-groups-bind
|
(ppcre:register-groups-bind
|
||||||
(negative (#'parse-integer num) unit)
|
(negative (#'parse-integer num) unit)
|
||||||
(pattern (subseq line 8))
|
(pattern (subseq line 8))
|
||||||
(setq current-offset
|
(setq current-offset
|
||||||
(* (if (equal negative "-") -1 1)
|
(* (if (equal negative "-") -1 1)
|
||||||
(cond
|
(cond
|
||||||
((equal unit "S") num)
|
((equal unit "S") num)
|
||||||
((equal unit "M") (* num 60))
|
((equal unit "M") (* num 60))
|
||||||
((equal unit "H") (* num 60 60))
|
((equal unit "H") (* num 60 60))
|
||||||
((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"
|
||||||
@ -166,4 +181,4 @@
|
|||||||
(defun toplevel ()
|
(defun toplevel ()
|
||||||
(sb-ext:disable-debugger)
|
(sb-ext:disable-debugger)
|
||||||
(exit-on-ctrl-c
|
(exit-on-ctrl-c
|
||||||
(main)))
|
(main)))
|
||||||
|
|||||||
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() {
|
function print_state() {
|
||||||
percentage = state_info["percentage"]
|
percentage = state_info["percentage"]
|
||||||
printf "%s%s%d%%\n",
|
tooltip = ""
|
||||||
get_icon(percentage),
|
if (state_info["charging"]) {
|
||||||
state_info["charging"] ? "" : "",
|
tooltip = "Until Full: " (percentage == 100 ? "N/A" : state_info["time to full"])
|
||||||
percentage
|
} else {
|
||||||
|
tooltip = "Until Empty: " state_info["time to empty"]
|
||||||
|
}
|
||||||
|
printf "{\"text\":\"%s%s%d%%\",\"tooltip\":\"%s\"}\n",
|
||||||
|
get_icon(percentage),
|
||||||
|
state_info["charging"] ? "" : "",
|
||||||
|
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) {
|
||||||
@ -75,8 +86,10 @@ function parse_record(record, exit_on_absent) {
|
|||||||
if (name == "") {
|
if (name == "") {
|
||||||
return 0
|
return 0
|
||||||
} 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) {
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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
|
#!/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)
|
|
||||||
|
|||||||
@ -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=''
|
||||||
bluetoothctl show | grep 'Powered: yes' >/dev/null && output="${output} "
|
if (( bluetooth )); then
|
||||||
|
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
|
||||||
|
|||||||
@ -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
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
|
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
|
||||||
|
|||||||
@ -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
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 {
|
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=()
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user