Commit Diff


commit - 8727e48c019cbf81e7b9290bb08459ac5b512d65
commit + af8af444582ea33ed63a21a78329af59e191db4f
blob - e39dca1bf2bde6b5093f9678e00a62433ca1f20a
blob + dc2adffeb301cdd13645a7daf8bd99bf7ecce2a1
--- flake.lock
+++ flake.lock
@@ -32,11 +32,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1784030718,
-        "narHash": "sha256-6EvmUvmyG6ciz71anFi+KbH3JZf8Z0SeI1jVb2u8Rgo=",
+        "lastModified": 1784049068,
+        "narHash": "sha256-nvO5rN+SWdpVGY1OIFziAz8JaTl265lgSRlPnQCciIc=",
         "owner": "~mtmn",
         "repo": "corpus",
-        "rev": "2f4f1bb6530a517dd155e2fcf7d2aa93f8343e15",
+        "rev": "4a464dc7ea2c23ef107e4b2d9f5cc6c840c52e26",
         "type": "sourcehut"
       },
       "original": {
blob - 92754fba4f0c97d4edd1bf914c5bf0492cf02c3c
blob + 4b23cbe5c0b76e3801826dbe54d702f724404753
--- hosts/void/overlays/bin/mics
+++ hosts/void/overlays/bin/mics
@@ -1,12 +1,32 @@
-#!/bin/sh
-sw=numid=7
+#!/bin/bash -e
 
-cur=$(amixer -c 1 cget "$sw" | sed -n 's/.*: values=\(on\|off\).*/\1/p' | head -1)
+usage() {
+	echo "Usage: mics mute|unmute [seconds]"
+	exit 1
+}
 
-if [ "$cur" = off ]; then
-	amixer -c 1 cset "$sw" on >/dev/null
-	printf '%s\n' "mic unmuted"
+cmd=${1:-mute}
+
+if [ "$cmd" != "mute" ] && [ "$cmd" != "unmute" ]; then
+	usage
+fi
+
+indices=$(pactl list short sources | awk '{print $1}')
+
+if [ "$cmd" = "mute" ]; then
+	echo "$indices" | xargs -I{} pactl set-source-volume {} 0%
 else
-	amixer -c 1 cset "$sw" off >/dev/null
-	printf '%s\n' "mic muted"
+	time=$2
+	echo "$indices" | xargs -I{} pactl set-source-volume {} 100%
+	if [ "$time" ]; then
+		sleep "$time"
+		echo "$indices" | xargs -I{} pactl set-source-volume {} 0%
+	fi
 fi
+
+echo "Current microphone status:"
+echo "$indices" | while read -r idx; do
+	echo "Source $idx:"
+	echo "  $(pactl get-source-mute "$idx")"
+	echo "  $(pactl get-source-volume "$idx")"
+done
blob - c75f4db815840fec3f55eaf3a98a25db91c671f9 (mode 644)
blob + /dev/null
--- hosts/void/overlays/bin/otterb
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require "fileutils"
-require "open3"
-require "etc"
-
-class Builder
-  REPO = "https://repo-default.voidlinux.org/current"
-  DEPS = %w[
-    cmake
-    make
-    gcc
-    qt5-devel
-    qt5-svg-devel
-    qt5-multimedia-devel
-    qt5-declarative-devel
-    qt5-webengine
-    qt5-webengine-devel
-    qt5-webkit-devel
-    qt5-webchannel-devel
-    qt5-location-devel
-    hunspell-devel
-    gstreamer1-devel
-    libxml2-devel
-    qt5-tools
-  ]
-    .freeze
-
-  def initialize(argv)
-    @outdir = nil
-    @chroot = "/var/chroot/otter-build"
-    @keep = false
-    @quiet = false
-    parse(argv)
-    validate
-  end
-
-  def run
-    bootstrap
-    mount_vfs
-    prep_chroot
-    install_deps
-    build
-    extract
-  ensure
-    unmount_vfs
-    remove_chroot unless @keep
-    msg("done.")
-  end
-
-  private
-
-  def parse(argv)
-    i = 0
-    while i < argv.length
-      case argv[i]
-      when "-o"
-        @outdir = argv[i += 1]
-      when "-c"
-        @chroot = argv[i += 1]
-      when "-k"
-        @keep = true
-      when "-q"
-        @quiet = true
-      when "-h", "--help"
-        usage(code: 0)
-      else
-        usage
-      end
-
-      i += 1
-    end
-  end
-
-  def usage_lines
-    [
-      "Usage: #{File.basename($0)} [-o DIR] [-c DIR] [-k] [-q]",
-      "  -o DIR   output directory (default: invoking user home)",
-      "  -c DIR   chroot directory (default: /var/chroot/otter-build)",
-      "  -k       keep chroot after build",
-      "  -q       quiet"
-    ]
-  end
-
-  def usage(code: 1)
-    usage_lines.each { |l| puts(l) }
-    exit(code)
-  end
-
-  def validate
-    unless Process.uid.zero?
-      usage_lines.each { |l| puts(l) }
-      die("must run as root")
-    end
-
-    @src = Dir.pwd
-    die("no CMakeLists.txt in #{@src}") unless File.file?(File.join(@src, "CMakeLists.txt"))
-
-    inv = ENV["SUDO_USER"] || ENV["DOAS_USER"]
-    if inv
-      @pw = Etc.getpwnam(inv) rescue nil
-      die("cannot resolve user #{inv}") unless @pw
-      @outdir ||= @pw.dir
-    else
-      @outdir ||= File.expand_path("~")
-    end
-  end
-
-  def msg(s) = puts(s) unless @quiet
-
-  def die(s)
-    $stderr.puts("#{File.basename($0)}: #{s}")
-    exit(1)
-  end
-
-  def run_cmd(cmd, fatal: true)
-    msg(cmd)
-    _o, e, st = Open3.capture3(cmd)
-    die("#{cmd}: #{e.strip}") if !st.success? && fatal
-    st.success?
-  end
-
-  def chroot_cmd(script)
-    msg("chroot #{@chroot} /bin/bash -c '#{script}'")
-    die("chroot command failed") unless system("chroot #{@chroot} /bin/bash -c '#{script}'")
-  end
-
-  def bootstrap
-    FileUtils.mkdir_p(@chroot)
-    return if File.executable?(File.join(@chroot, "bin/bash"))
-
-    msg("bootstrapping chroot...")
-    kdir = File.join(@chroot, "var/db/xbps/keys")
-    FileUtils.mkdir_p(kdir)
-    Dir.glob("/var/db/xbps/keys/*").each { |f| FileUtils.cp(f, kdir) }
-
-    run_cmd("xbps-install -Syu -R #{REPO} -r #{@chroot} base-voidstrap", fatal: false)
-    die("bootstrap failed") unless File.executable?(File.join(@chroot, "bin/bash"))
-    msg("bootstrap ok")
-  end
-
-  def mount_vfs
-    mounts = {
-      File.join(@chroot, "proc") => ["-t", "proc", "proc"],
-      File.join(@chroot, "sys") => ["-t", "sysfs", "sys"],
-      File.join(@chroot, "dev") => ["--bind", "/dev"]
-    }
-    mounts.each do |dst, opts|
-      FileUtils.mkdir_p(dst)
-      next if system("mountpoint -q #{dst} 2>/dev/null")
-      system("mount #{opts.join(" ")} #{dst}")
-    end
-  end
-
-  def unmount_vfs
-    %w[dev sys proc].each { |n| system("umount #{File.join(@chroot, n)} 2>/dev/null") }
-  end
-
-  def prep_chroot
-    FileUtils.cp("/etc/resolv.conf", File.join(@chroot, "etc/resolv.conf"))
-    kdir = File.join(@chroot, "var/db/xbps/keys")
-    FileUtils.mkdir_p(kdir)
-    Dir.glob("/var/db/xbps/keys/*").each { |f| FileUtils.cp(f, kdir) }
-  end
-
-  def install_deps
-    chroot_cmd("xbps-install -Syu")
-    chroot_cmd("xbps-install -S #{DEPS.join(" ")}")
-  end
-
-  def build
-    dst = File.join(@chroot, "src/otter-browser")
-    FileUtils.rm_rf(dst)
-    FileUtils.mkdir_p(File.dirname(dst))
-    system("cp -a #{@src} #{dst}") || die("copy failed")
-
-    cm = File.join(dst, "CMakeLists.txt")
-    txt = File.read(cm)
-    unless txt.include?("FATAL_ON_MISSING_REQUIRED_PACKAGES")
-      die("CMakeLists.txt: FATAL_ON_MISSING_REQUIRED_PACKAGES not found")
-    end
-
-    txt.gsub!("FATAL_ON_MISSING_REQUIRED_PACKAGES", "")
-    File.write(cm, txt)
-
-    script = "set -e; cd /src/otter-browser; rm -rf build; mkdir build; cd build; cmake -DENABLE_QT5=ON ../; make -j$(nproc)"
-    chroot_cmd(script)
-  end
-
-  def extract
-    bin = File.join(@chroot, "src/otter-browser/build/otter-browser")
-    die("binary not found") unless File.file?(bin)
-
-    out = File.join(@outdir, "otter-browser")
-    system("cp #{bin} #{out}") || die("copy binary failed")
-    FileUtils.chmod("+x", out)
-
-    if @pw
-      begin
-        File.chown(@pw.uid, @pw.gid, out)
-      rescue => e
-        msg("chown failed: #{e}")
-      end
-    end
-
-    msg("binary: #{out}")
-  end
-
-  def remove_chroot
-    msg("removing #{@chroot}...")
-    FileUtils.rm_rf(@chroot)
-  end
-end
-
-Builder.new(ARGV).run
blob - ee6c7a7b0ffbad97cfc990add38df7b78472982b
blob + 48b410ec42d1892bf13c48b3cdb3537815a2fc7b
--- hosts/void/overlays/config/river/init
+++ hosts/void/overlays/config/river/init
@@ -127,20 +127,4 @@ riverctl map normal $alt+Shift Tab send-to-previous-ta
 riverctl map normal $mod Tab focus-previous-tags
 riverctl map normal $mod+Shift Tab send-to-previous-tags
 
-swaybg -m fill -c "#0d1612" -i "$HOME/misc/random/wallpapers/curtains.jpg" &
-mkfifo "$wobsock" && tail -f "$wobsock" | wob &
-wl-paste --watch cliphist store &
-
-services_to_start=(
-	battery-notify
-	fnott
-	pipewire
-	kanshi
-	psi-notify
-	memcached
-	nix-daemon
-)
-
-for s in "${services_to_start[@]}"; do
-	"$s" 2>&1 | vlogger -t "$s" &
-done
+runsvdir "$HOME/.config/sv" &
blob - /dev/null
blob + d7890f835d0920aa84eff1ca11382994d8849dfc (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/battery-notify.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'battery-notify '("battery-notify"))
blob - /dev/null
blob + 3dd1f9669954f510716dde7a90b1be4a49957454 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/cliphist.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'cliphist '("wl-paste" "--watch" "cliphist" "store"))
blob - /dev/null
blob + 0ee7387bd902c2edca98407c2596ae79f521ae0f (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/fnott.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'fnott '("fnott"))
blob - /dev/null
blob + 990f67e1094a6021172cd1b0b715b029df87ab59 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/hister.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'hister '("hister" "listen"))
blob - /dev/null
blob + a33c316e34352b8463d3eb91564168370a34cac9 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/kanshi.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'kanshi '("kanshi"))
blob - /dev/null
blob + 09304c6911eff2c72925319adcd8eefc2716e8db (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/memcached.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'memcached '("memcached"))
blob - /dev/null
blob + e339da50cbe82c2f9ce4ef4d7fe2072de8b9c814 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/nix-daemon.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'nix-daemon '("nix-daemon"))
blob - /dev/null
blob + dc458a7d0d58fdb438904bd22447d4e55c4717a5 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/pipewire.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'pipewire '("pipewire"))
blob - /dev/null
blob + 7fa8d625de9bdf259ba9d88c1201f98d748e18bd (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/psi-notify.scm
@@ -0,0 +1,2 @@
+;; -*- mode: scheme; -*-
+(exec-service 'psi-notify '("psi-notify"))
blob - /dev/null
blob + 05e9f3832b683dfdfe602e91415e0269e1c42328 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/swaybg.scm
@@ -0,0 +1,5 @@
+;; -*- mode: scheme; -*-
+(exec-service 'swaybg
+              (list "swaybg" "-m" "fill" "-c" "#0d1612"
+                    "-i" (string-append (getenv "HOME")
+                                        "/misc/random/wallpapers/curtains.jpg")))
blob - /dev/null
blob + 9bd2f907e28d3de02df2aacf7f787039b2c0c043 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.d/wob.scm
@@ -0,0 +1,9 @@
+;; -*- mode: scheme; -*-
+;; wob reads volume/brightness levels from a FIFO at $XDG_RUNTIME_DIR/wob.sock
+(exec-service
+ 'wob
+ (list "sh" "-c"
+       (string-append
+        "sock=\"$XDG_RUNTIME_DIR/wob.sock\"; "
+        "[ -p \"$sock\" ] || mkfifo \"$sock\"; "
+        "exec sh -c 'tail -f \"$1\" | wob' _ \"$sock\"")))
blob - /dev/null
blob + 740c3450d99aac3e5b09f96119cfd9ff9f9283e0 (mode 644)
--- /dev/null
+++ hosts/void/overlays/config/shepherd/init.scm
@@ -0,0 +1,33 @@
+;; -*- mode: scheme; -*-
+;;; ~/.config/shepherd/init.scm
+
+(use-modules (shepherd service)
+             (shepherd support)
+             ((ice-9 ftw) #:select (scandir)))
+
+(default-environment-variables (environ))
+
+;; Per-service log files live here as <name>.log
+(mkdir-p %user-log-dir)
+
+(define* (exec-service name command #:key (respawn? #t))
+  (let ((svc (service (list name)
+               #:documentation (string-append "Run " (symbol->string name) ".")
+               #:start (make-forkexec-constructor
+                        command
+                        #:log-file (string-append %user-log-dir "/"
+                                                  (symbol->string name) ".log"))
+               #:stop (make-kill-destructor)
+               #:respawn? respawn?)))
+    (register-services (list svc))
+    (start-service svc)))
+
+;; Send Shepherd to the background
+(perform-service-action root-service 'daemonize)
+
+;; Load every ~/.config/shepherd/init.d/*.scm file
+(let ((init.d (string-append %user-config-dir "/init.d")))
+  (for-each (lambda (file)
+              (load (string-append init.d "/" file)))
+            (scandir init.d
+                     (lambda (file) (string-suffix? ".scm" file)))))
blob - 861b5e9fe5887e520fef1c1ba48ae802cab5470a
blob + e4c6eea62d3d66878d16ca5f507223971ef5f0e9
--- hosts/void/overlays/config/volare/config
+++ hosts/void/overlays/config/volare/config
@@ -158,19 +158,8 @@ mode "resize" {
     bindsym --locked XF86MonBrightnessUp exec sh -c 'brightnessctl set +5% | sed -En "s/.*\(([0-9]+)%\).*/\1/p" > $wobsock'
     bindsym --locked XF86MonBrightnessDown exec sh -c 'brightnessctl set 5%- | sed -En "s/.*\(([0-9]+)%\).*/\1/p" > $wobsock'
 
-exec swaybg -m fill -c "#0d1612" -i "$HOME/misc/random/wallpapers/curtains.jpg"
-exec sh -c 'mkfifo $wobsock 2>/dev/null; tail -f $wobsock | wob'
-exec wl-paste --watch cliphist store
+exec shepherd
 
-exec battery-notify
-exec fnott
-exec pipewire
-exec kanshi
-exec psi-notify
-exec memcached
-exec nix-daemon
-exec hister listen
-
 font Fragment Mono 10
 
 bar {
blob - /dev/null
blob + dd170015435b965bf885c4000ff40c694807ed5c (mode 644)
--- /dev/null
+++ modules/mixins/dotfiles/bin/ffmpeg-cut
@@ -0,0 +1,84 @@
+#!/bin/bash -e
+
+# Takes a file with lines like:
+#
+# VID_20240630_143147_036.mp4
+# VID_20240630_144842_041.mp4 01:45
+# VID_20240630_145538_042.mp4 01:00 01:50
+#
+# ...and extracts them into another folder.
+#
+# The keyframe seeking is imprecise, but with the Ace Pro it's close enough
+# that it doesn't matter.
+
+while getopts ":i:o:" opt; do
+  case $opt in
+    i) input_dir="$OPTARG"
+    ;;
+    o) output_dir="$OPTARG"
+    ;;
+    \?) echo "Invalid option -$OPTARG" >&2
+        exit 1
+    ;;
+  esac
+done
+
+shift $((OPTIND - 1))
+file_list="$1"
+
+if [[ -z "$input_dir" || -z "$output_dir" || -z "$file_list" ]]; then
+  echo "Usage: $0 -i input_dir -o output_dir file_list"
+  exit 1
+fi
+
+time_to_seconds() {
+  IFS=: read -r m s <<< "$1"
+  echo $((10#${m:-0} * 60 + 10#${s:-0}))
+}
+
+while IFS=' ' read -r file start end; do
+  if [[ -z "$file" ]]; then
+    echo "File name is empty" >&2
+    exit 1
+  fi
+
+
+  input_file="$input_dir/$file"
+
+  if ! [[ -f "$input_file" ]]; then
+    echo "$input_file does not exist" >&2
+    exit 1
+  fi
+
+  base_name=$(basename "$file" .mp4)
+  if [[ -n "$start" && -n "$end" ]]; then
+    start_formatted=$(echo "$start" | tr -d ':')
+    end_formatted=$(echo "$end" | tr -d ':')
+    output_file="$output_dir/${base_name}_${start_formatted}_${end_formatted}.mp4"
+  elif [[ -n "$start" ]]; then
+    start_formatted=$(echo "$start" | tr -d ':')
+    output_file="$output_dir/${base_name}_${start_formatted}.mp4"
+  else
+    output_file="$output_dir/$file"
+  fi
+
+  cmd=("ffmpeg" "-nostdin")
+
+  if [[ -n "$start" ]]; then
+    cmd+=("-ss" "$start")
+  fi
+
+  cmd+=("-i" "$input_file" "-muxpreload" "0" "-muxdelay" "0")
+
+  if [[ -n "$end" ]]; then
+    start_seconds=$(time_to_seconds "$start")
+    end_seconds=$(time_to_seconds "$end")
+    relative_end_seconds=$((end_seconds - start_seconds))
+    cmd+=("-to" "$relative_end_seconds")
+  fi
+
+  cmd+=("-c" "copy" "$output_file")
+
+  echo "${cmd[@]}"
+  "${cmd[@]}"
+done < "$file_list"
blob - /dev/null
blob + 2fccc51f0d56aa55ae2fb7c7afbaffbed6c5cec1 (mode 644)
--- /dev/null
+++ modules/mixins/dotfiles/bin/ffmpeg-merge
@@ -0,0 +1,8 @@
+#!/bin/bash -ex
+
+out=${1?}
+shift
+
+# Need to put working directory in or ffmpeg will think it's relative to the
+# named pipe in /proc...?
+ffmpeg -f concat -safe 0 -i <(printf "file '$PWD/%s'\n" "$@") -c copy "$out"
blob - 03e286932f1adbc0b9c082eb822d4d739ced7652
blob + 000de57cf5ecb30b22f75e21bdf4f2257189cf58
--- modules/mixins/dotfiles/bin/isolate
+++ modules/mixins/dotfiles/bin/isolate
@@ -39,7 +39,6 @@ for p in \
 	"$HOME/.config" \
 	"$HOME/.gitconfig" \
 	"$HOME/.local/bin" \
-	"$HOME/.local/share/tools" \
 	"$HOME/.cache/zig" \
 	"$HOME/.rustup" \
 	"$HOME/.nix-profile"; do
blob - /dev/null
blob + c97eeae4858b91d27cc85a31042595eb9ac9a183 (mode 644)
--- /dev/null
+++ modules/mixins/dotfiles/bin/plane
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+# Base mp3 is from donation export from https://mynoise.net/NoiseMachines/rainNoiseGenerator.php?c=2&l=2420212127302925252300&m=&d=0
+# This is then cut +10 from beginning/-10 from end to get rid of fades, so it can be looped:
+#
+#   ffmpeg -i myNoise_RAIN_24202121273029252523_0_5.mp3 -vn -acodec copy -ss 00:10 -to 04:10 rain.mp3
+#
+# We play with --no-config to avoid normalisation settings that are mostly for video
+# You'll also want to go into Audacity or similar and cut off the silence
+
+exec mpv --quiet --no-config --loop-file --no-audio-display --no-resume-playback ~/plane.mp3
blob - /dev/null
blob + 16e6e9e014cee32c35086abe5445f46d054d597b (mode 644)
--- /dev/null
+++ modules/mixins/dotfiles/bin/sprunge
@@ -0,0 +1,4 @@
+#!/bin/bash -e
+
+curl --data-binary @- https://paste.rs/
+printf "\n"
blob - /dev/null
blob + 35152ea411a04c5298245af0ed2fccbb0b12e04e (mode 644)
--- /dev/null
+++ modules/mixins/dotfiles/bin/thunder
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+# Base mp3 is from donation export from https://mynoise.net/NoiseMachines/rainNoiseGenerator.php?c=2&l=2420212127302925252300&m=&d=0
+# This is then cut +10 from beginning/-10 from end to get rid of fades, so it can be looped:
+#
+#   ffmpeg -i myNoise_RAIN_24202121273029252523_0_5.mp3 -vn -acodec copy -ss 00:10 -to 04:10 rain.mp3
+#
+# We play with --no-config to avoid normalisation settings that are mostly for video
+# You'll also want to go into Audacity or similar and cut off the silence
+
+exec mpv --quiet --no-config --loop-file --no-audio-display --no-resume-playback ~/misc/thunder.mp3