From 858993fb6c3e9351988b193e6c296e6ea7862501 Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Sun, 28 Apr 2024 13:59:33 -0400 Subject: [PATCH 1/6] host info updates --- brc2 | 21 +++++++++++++------ easiest-to-type-numbers | 2 +- .../etc/openvpn/client-config-hole/bb8 | 1 - .../etc/openvpn/client-config-hole/frodo | 1 - .../etc/openvpn/client-config-hole/x2 | 2 +- .../systemd/system/openvpn-client-tr@.service | 4 ++-- 6 files changed, 19 insertions(+), 12 deletions(-) delete mode 100644 machine_specific/li/filesystem/etc/openvpn/client-config-hole/bb8 delete mode 100644 machine_specific/li/filesystem/etc/openvpn/client-config-hole/frodo diff --git a/brc2 b/brc2 index 05de0a3..3edbae0 100644 --- a/brc2 +++ b/brc2 @@ -2650,7 +2650,7 @@ host-info-all() { host-info-update() { local -A vpn_ips host_ips host_macs nonvpn_ips all_ips - local -a root_hosts nonroot_hosts host_usbs + local -a root_hosts nonroot_hosts # the hosts with no mac root_hosts=( bk je li b8.nz ) @@ -2687,6 +2687,7 @@ host-info-update() { # hosts is that it is for the User part, the IdentityFile part is # redundant to *.b8.nz. Also note ${host}i, we only setup those for vpn hosts, but there is no harm in overspecifying here. root_hosts+=($host ${host}i $host.b8.nz ${host}i.b8.nz) + root_hosts_a[$host]=t # a for associative array else nonroot_hosts+=($host ${host}i) fi @@ -2756,7 +2757,7 @@ EOF # shellcheck disable=SC2016 # shellcheck doesnt know this is sed sedi '/edits below here are made automatically/,$d' /p/c/machine_specific/li/filesystem/etc/wireguard/wgmail.conf for host in ${!vpn_ips[@]}; do - if [[ ${root_ips[$host]} ]]; then + if [[ ${root_hosts_a[$host]} ]]; then # root machines dont actually need vpn, but # the classification still helps with other # configurations. @@ -2764,7 +2765,7 @@ EOF fi ipsuf=${vpn_ips[$host]} wghole $host $ipsuf - sd /b/ds/machine_specific/li/filesystem/etc/openvpn/client-config-hole/$host < Date: Mon, 20 May 2024 18:03:51 -0400 Subject: [PATCH 2/6] a few important fixes, mostly improvements --- .tmux.conf | 6 + brc | 77 ++++++++++- brc2 | 130 ++++++++++++++++-- btrbk-run | 119 +++++++++------- btrfsmaint | 4 +- distro-begin | 12 +- distro-end | 6 +- ffs | 96 +++++++++++++ filesystem/usr/local/bin/abrowser | 6 +- filesystem/usr/local/bin/ethusb-nm | 31 ----- filesystem/usr/local/bin/ethusb-static | 74 ---------- i3-pull | 3 +- i3-sway/common.conf | 3 +- i3-sway/i3.conf | 36 ++--- laptop-xrandr | 11 +- obs-i3-monitor | 3 + pkgs | 2 + prof | 2 +- prof-remote | 2 +- rootsshsync | 2 + script-files | 1 + .../.config/systemd/user/profanity.service | 7 +- system-status | 44 +++--- 23 files changed, 442 insertions(+), 235 deletions(-) create mode 100644 .tmux.conf create mode 100755 ffs delete mode 100644 filesystem/usr/local/bin/ethusb-nm delete mode 100644 filesystem/usr/local/bin/ethusb-static diff --git a/.tmux.conf b/.tmux.conf new file mode 100644 index 0000000..9557d72 --- /dev/null +++ b/.tmux.conf @@ -0,0 +1,6 @@ +# a 256color term is needed for profanity to have colors, this is +# recommended by +# https://unix.stackexchange.com/questions/1045/getting-256-colors-to-work-in-tmux +set -g default-terminal "tmux-256color" +# default is green. I prefer it to stand out less. +set-option -g status-style bg=black diff --git a/brc b/brc index 7731b9c..dc32a10 100644 --- a/brc +++ b/brc @@ -774,6 +774,50 @@ EOF done } +screenrtp() { + + local ip port xoffset + read -r ip port xoffset <<<"$@" + + setxenv + + if [[ ! $port ]]; then + port=9999 + fi + + while true; do + # By default, plugged in screen goes to the right side, so we need an + # offset that is the same as the laptop's x resolution. If we are in + # mirror mode, then we don't need an offset. + if [[ ! $xoffset ]]; then + xoffset=0 + laptop_x=$(xrandr | awk '$1 == "LVDS-1" {print $4}' | sed 's/x.*//') || { sleep 1; continue; } + total_x=$(xdpyinfo| awk '$1 == "dimensions:" {print $2}' | sed 's/x.*//') || { sleep 1; continue; } + screen2_res=$(xrandr | awk '$2 == "connected" && $1 != "LVDS-1" { print $3 }' | sed 's/+.*//') + if (( laptop_x < total_x )); then + xoffset=$laptop_x + fi + fi + + m ffmpeg -probesize 50M -thread_queue_size 50 \ + -video_size $screen2_res -f x11grab -framerate 30 -i :0.0+$xoffset.0 \ + -vcodec libx264 -g 1 -tune zerolatency -preset ultrafast -pix_fmt yuv420p -x264-params repeat-headers=1 \ + -f rtp_mpegts rtp://$ip:$port ||: + + + sleep 1 + done +} + +setxenv() { + if [[ ! $DISPLAY ]]; then + export DISPLAY=:0.0 + fi + if [[ ! $XAUTHORITY ]]; then + export XAUTHORITY=$HOME/.Xauthority + fi +} + #### end fsf section @@ -2046,9 +2090,9 @@ nags() { /usr/bin/nagstamon & } -# profanity screen +# profanity tmux profsrc() { - screen -RD -S profanity + screen -L profanity a } # i dont want to wait for konsole to exit... @@ -3375,6 +3419,35 @@ if [[ $- == *i* ]]; then fi + +lp22viewers() { + v=0 + roomv=(0 0) + rooms=(jupiter saturn) + for ip in 209.51.188.25 live.fsf.org; do + out=$(curl -sS --insecure https://$ip/) + for i in 0 1 2; do + room=${rooms[i]} + while read -r n; do + v=$((v+n)) + roomv[$i]=$(( ${roomv[$i]} + n )) + done < <(printf "%s\n" "$out" | grep -Po "$room.*?current[^0-9]*[0-9]*" | grep -o '[0-9]*$' ) + done + done + printf "total: %s " $v + for i in 0 1; do + room=${rooms[i]} + printf "$room: %s " "${roomv[$i]}" + done + echo +} + +arpflush() { + local default_route_dev + default_route_dev=$(ip r show default | sed 's/.*dev \([^ ]*\).*/\1/' | head -n1) + m s ip n flush dev "$default_route_dev" +} + # * stuff that makes sense to be at the end diff --git a/brc2 b/brc2 index 3edbae0..5fde368 100644 --- a/brc2 +++ b/brc2 @@ -1504,7 +1504,6 @@ btrbk-host-debug() { # $ dig ns1.gnu.org @b0.org.afilias-nst.org. -# todo: make sm pull/push use systemd instead of the journal cat command bbk() { # btrbk wrapper local ret=0 c / @@ -1855,7 +1854,13 @@ dsign() { # set day start for use in other programs. # expected to do be in a format like 830, or 800 or 1300. ds() { + local regex + regex='[0-9]?[0-9]?[0-9][0-9]' if [[ $1 ]]; then + if [[ ! $1 =~ $regex ]]; then + echo "ds: error. expected \$1 to match $regex, got \$1: $1" + return 1 + fi echo $1 >/b/data/daystart else cat /b/data/daystart @@ -1998,8 +2003,8 @@ apache-header() { # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. # Copyright 2024 Ian Kelling @@ -2638,18 +2643,28 @@ wgkey() { host-info-all() { host-info-update + bindpushb8 + # for wireguard configs ssh iank@li.b8.nz conflink wrt-setup } -# if you change a host's ip, then run -# bindpushb8 -# wrt-setup + + + +## for updating host info like ip, location, update /p/c/host-info and +## host_info below. the host_info array should probably be in its own +## file that gets sourced so that it can be more easily updated. + +# todo: this is so long that it becomes confusing, +# try to split it up. +# +# To make some changes take effect, run host-info-all. host-info-update() { - local -A vpn_ips host_ips host_macs nonvpn_ips all_ips + local -A vpn_ips host_ips host_macs portfw_ips nonvpn_ips all_ips local -a root_hosts nonroot_hosts # the hosts with no mac @@ -2678,6 +2693,7 @@ host-info-update() { all_ips[$host]=$ip if $vpn; then + portfw_ips[$host]=$ip vpn_ips[$host]=$ip else nonvpn_ips[$host]=$ip @@ -2711,10 +2727,8 @@ EOF for host in ${!vpn_ips[@]}; do ipsuf=${vpn_ips[$host]} cat <$tmpf + cedit -e work-identity /p/c/subdir_files/.ssh/config-static <$tmpf + rm -f $tmpf + + ### begin focus on hosts file update ### + # + # This started as its own function, but it actually + # needed to alter the ssh config, so combined it. + # + # background: This is finally doing dynamic ip resolution via the hosts + # file. I considered detecting where each host was dynamically or + # something, but ultimately decided to mostly avoid that, other than + # detecting the status of the current machine I'm on. I want to be able + # to move it around without having to manually type much of anything. + local -a host_domain_suffix hosts + local -A ip_to_hosts + local suf ip i host at_home suf_from_here + + source /p/c/domain-info + + at_home=false + if ip n | grep -q "10.2.0.1 .* b4:75:0e:fd:06:4a"; then + at_home=true + fi + + for i in ${host_domain_suffix[@]}; do + if [[ $i == *.* ]]; then + suf=$i + continue + fi + hosts+=($i) + if [[ $i == "$HOSTNAME" ]]; then + unset "portfw_ips[$i]" + continue + fi + + suf_from_here=$suf + if ! $at_home && [[ $suf == .b8.nz || $suf == [wc].b8.nz ]]; then + suf_from_here=i.b8.nz + else + unset "portfw_ips[$i]" + fi + ip=$(getent ahostsv4 "$i$suf_from_here" | awk '{ print $1 }' | head -n1) ||: + if [[ ! $ip ]]; then + if [[ $suf == .office.fsf.org ]]; then + suf_from_here=wg.b8.nz + ip=$(getent ahostsv4 "$i$suf_from_here" | awk '{ print $1 }' | head -n1) ||: + fi + if [[ ! $ip ]]; then + echo error: failed to get ip of "$i$suf_from_here" + return 1 + fi + fi + ip_to_hosts[$ip]+=" $i" + done + for ip in "${!ip_to_hosts[@]}"; do + echo "$ip${ip_to_hosts[$ip]}" + done | s cedit -e hosts-file-up /etc/hosts + for host in ${hosts[@]}; do + echo $host + done | cedit -e /a/bin/ds/subdir_files/.dsh/group/btrbk + ### end focus on hosts file update ### + + + # note: note sure if this is a great way to check. + # todo: think about it + + if $at_home; then + # possible that in the future we want to create + # a dynamic file here, and then we can move the cat + # command above out of the conditional + rsync -a /p/c/subdir_files/.ssh/config-static ~/.ssh/config + else + for host in ${!portfw_ips[@]}; do + ipsuf=${portfw_ips[$host]} + cat < ~/.ssh/config-dynamic + cat /p/c/subdir_files/.ssh/config-static ~/.ssh/config-dynamic >~/.ssh/config + fi } # usage host ipsuf [extrahost] @@ -4675,6 +4772,9 @@ tclear() { done } +opensslcertinfo() { + openssl x509 -txt -in "$@" +} export BASEFILE_DIR=/a/bin/fai-basefiles diff --git a/btrbk-run b/btrbk-run index 8fc4c4f..9bd6e9b 100644 --- a/btrbk-run +++ b/btrbk-run @@ -45,25 +45,53 @@ EOF } + pre=btrbk-run + + script_name="${BASH_SOURCE[0]}" script_name="${script_name##*/}" + + +log-setup() { + if [[ ! $log_path ]]; then + mkdir -p /var/log/btrbk + log_path=/var/log/btrbk/$(date +%F_%T%:::z).log + fi +} d() { if $dry_run || $conf_only; then printf "$pre dry-run: %s\n" "$*" else - printf "$pre running: %s\n" "$*" - "$@" + log-setup + printf "$pre running: %s\n" "$*" |& pee cat 'ts "%F %T" >>'$log_path + "$@" |& pee cat 'ts "%F %T" >>'$log_path fi } m() { if $verbose; then printf "$pre %s\n" "$*"; fi; "$@"; } e() { printf "$pre %s\n" "$*"; } + +logq() { + local exit_code + exit_code=0 + log-setup + printf "$pre running: %s\n" "$*" | pee cat 'ts "%F %T" >>'$log_path + e logging to $log_path + "$@" |& ts "%F %T" >>$log_path || exit_code=$? + printf "$pre exit code:%s of %s\n" "$exit_code" "$*" | pee cat 'ts "%F %T" >>'$log_path + if (( exit_code > 0 )); then + e "error: command exit code: $exit_code. exiting after tail -n50 $log_path" + tail -n50 $log_path + exit $exit_code + fi +} + die() { printf "$pre error: %s\n" "$*" >&2; echo "$pre exiting with status 1" >&2; exit 1; } mexit() { echo "$pre exiting with status $1"; exit $1; } uninstalled-file-die() { - die "uninstalled file $1. run install-my-scripts or rerun with -f" + die "file $1 is not latest. run install-my-scripts or rerun with -f" } set-location() { @@ -104,10 +132,10 @@ add-x3-target() { # main work machine if ping -q -c1 -w1 x3.office.fsf.org &>/dev/null; then targets+=(x3.office.fsf.org) - elif ping -q -c1 -w1 $h.b8.nz &>/dev/null; then + elif ping -q -c1 -w1 x3.b8.nz &>/dev/null; then # in case we took it home targets+=(x3.b8.nz) - elif ping -q -c1 -w1 ${h}w.b8.nz &>/dev/null; then + elif ping -q -c1 -w1 x3w.b8.nz &>/dev/null; then targets+=(x3w.b8.nz) else targets+=(x3wg.b8.nz) @@ -164,7 +192,7 @@ ret=0 conf_only=false dry_run=false # mostly for testing rate_limit=no -verbose=true; verbose_arg=-v +verbose=true; verbose_arg="-l trace" force=false if [[ $INVOCATION_ID ]]; then # INVOCATION_ID means running as a systemd service. we cant show progress in this case, @@ -203,13 +231,17 @@ fast=false kd_spread=false check_installed=false orig_args=("$@") -temp=$(getopt -l check-installed,fast,pull-reexec,help 23cefikl:m:npqrs:t:vh "$@") || usage 1 +temp=$(getopt -l check-installed,fast,pull-reexec,help 23acefikl:m:npqrs:t:vh "$@") || usage 1 eval set -- "$temp" while true; do case $1 in # for the rare case we want to run multiple instances at the same time -2) conf_suf=2 ;; -3) conf_suf=3 ;; + -a) + # all moiuntpoints + mountpoints=(/a /o /qr /qd /q) + ;; # only creates the config file, does not run btrbk -c) conf_only=true ;; --check-installed) @@ -254,7 +286,9 @@ while true; do # snapshot. we have default hosts we will populate. -t) IFS=, targets=($2); unset IFS; shift ;; # verbose. - -v) verbose=true; verbose_arg=-v ;; + -v) + verbose=true; verbose_arg="-l trace" + ;; -h|--help) usage ;; --) shift; break ;; *) die "Internal error!" ;; @@ -278,7 +312,7 @@ if ! $force && { $check_installed || [[ ! $source ]]; } ; then fi done if ! diff -q /a/bin/bash-bear-trap/bash-bear /usr/local/lib/bash-bear; then - uninstalled-file-die err + uninstalled-file-die bash-bear fi if $check_installed; then exit 0 @@ -474,21 +508,24 @@ if ! command -v btrbk &>/dev/null; then die "error: no btrbk binary found" fi +# pull_reexec stops us from getting into an infinite loop if there is some +# kind of weird problem +pulla=false +for m in "${mountpoints[@]}"; do + if [[ $m == /a ]]; then + pulla=true + break + fi +done + if ! $pull_reexec && [[ $source ]] && $pulla && ! $force ; then - ssh root@$source btrbk-run --check-installed || exit 1 + ssh root@$source btrbk-run --check-installed fi #### end pre-checks ##### -mkdir -p /var/log/btrbk -# The journal doesnt go back to my oldest backups, and I've found myself -# wanting older logs. Not going to bother expiring old logs, since it is -# fine if they go back years. -log_path=/var/log/btrbk/$(date +%F_%T%:::z).log -echo copying output to $log_path -exec &> >(pee cat 'ts "%F %T"|dd of='$log_path' status=none') # print some non-default opts if $verbose; then @@ -519,26 +556,6 @@ if $verbose; then fi fi -if [[ -v targets ]]; then - echo "targets: ${targets[*]}" -fi - -if [[ $source ]]; then - echo "source: $source" -fi - -echo "mountpoints: ${mountpoints[*]}" - - -# pull_reexec stops us from getting into an infinite loop if there is some -# kind of weird problem -pulla=false -for m in "${mountpoints[@]}"; do - if [[ $m == /a ]]; then - pulla=true - break - fi -done if ! $pull_reexec && [[ $source ]] && $pulla ; then tmpf=$(mktemp) @@ -553,6 +570,16 @@ if ! $pull_reexec && [[ $source ]] && $pulla ; then fi fi + +if [[ -v targets ]]; then + echo "targets: ${targets[*]}" +fi +if [[ $source ]]; then + echo "source: $source" +fi +echo "mountpoints: ${mountpoints[*]}" + + # todo: check if we have no snapshots yet, because I always want to run # archive instead of run. Likely, I should give an error unless a cli # override is passed. perhaps check-subvol-stale could give the error. @@ -674,14 +701,6 @@ cat >/etc/btrbk$conf_suf.conf </dev/null /dev/null /dev/null); then if [[ $lock_info != *non-blanked* ]]; then locked=true fi - else - locked=true fi } diff --git a/distro-begin b/distro-begin index 59e50d5..82b349e 100755 --- a/distro-begin +++ b/distro-begin @@ -553,7 +553,7 @@ fi # disabled temporarily ###### setup /i # if home_network; then -# tu /etc/fstab <<'EOF' +# sudo teeu /etc/fstab <<'EOF' # /i/w /w none bind,noauto 0 0 # /i/k /k none bind,noauto 0 0 # EOF @@ -562,11 +562,11 @@ fi # sudo chown $USER:user2 /kr # fi # if [[ $HOSTNAME == frodo ]]; then -# tu /etc/fstab <<'EOF' +# sudo teeu /etc/fstab <<'EOF' # /k /kr none bind,noauto 0 0 # EOF # else -# tu /etc/fstab <<'EOF' +# sudo teeu /etc/fstab <<'EOF' # frodo:/k /kr nfs noauto 0 0 # EOF # fi @@ -636,7 +636,7 @@ if has_btrfs; then fi first_root_crypt=$(awk '$2 == "/" {print $1}' /etc/mtab) - tu /etc/fstab < 2)) && echo ,compress=zstd ) 0 0 EOF sudo mkdir -p $dir @@ -649,7 +649,7 @@ fi case $HOSTNAME in kd) - tu /etc/fstab <<'EOF' + sudo teeu /etc/fstab <<'EOF' /dev/mapper/crypt_dev_ata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V-part7 /d btrfs nofail,x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s,noatime,compress=zstd,subvol=d 0 0 /d/m /i none bind,compress=zstd 0 0 EOF @@ -665,7 +665,7 @@ EOF fi ;; frodo) - tu /etc/fstab <<'EOF' + sudo teeu /etc/fstab <<'EOF' /dev/mapper/crypt_dev_ata-ata-Hitachi_HDS722020ALA330_JK1121YAG7SXWS-part1 /i btrfs nofail,x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s,noatime,subvol=i 0 0 EOF if ! mountpoint /i &>/dev/null; then diff --git a/distro-end b/distro-end index ba0b585..d99e6a1 100755 --- a/distro-end +++ b/distro-end @@ -18,8 +18,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# shellcheck source=./brc -source ~/brc +export LC_USEBASHRC=t +source /a/bin/ds/.bashrc ### setup source /a/bin/bash-bear-trap/bash-bear @@ -1900,7 +1900,7 @@ case $HOSTNAME in wgip=$(command sudo sed -rn 's,^ *Address *= *([^/]+).*,\1,p' /etc/wireguard/wghole.conf) # old filename. remove once all hosts are updated. s rm -fv /etc/apache2/sites-enabled/${HOSTNAME}wg.b8.nz.conf - web-conf -i -a $wgip -p 9101 -f 9100 - apache2 ${HOSTNAME}wg.b8.nz <<'EOF' + s bash -x web-conf -i -a $wgip -p 9101 -f 9100 - apache2 ${HOSTNAME}wg.b8.nz <<'EOF' AuthType Basic AuthName "basic_auth" diff --git a/ffs b/ffs new file mode 100755 index 0000000..253b83b --- /dev/null +++ b/ffs @@ -0,0 +1,96 @@ +#!/bin/bash +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ffs = ffmpeg stream + +# todo: figure out how to record mumble +# todo: get icecast on li.b8.nz +# todo: https://superuser.com/questions/1106674/how-to-add-blank-lines-above-the-bottom-in-terminal + +if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi +shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 +set -eE -o pipefail +trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR + + +# 3 mountpoints: fsf-sysops (public), fsf (default, all staff), fsf-tech (tech team) +case $1 in + sysops|tech) + mount_suffix=-$1 + ;; +esac + +host=live.iankelling.org:8000 +if [[ $(dig +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]] \ + && ip n show 10.2.0.1 | grep . &>/dev/null; then + host=127.0.0.1:8000 +fi + +pass=$(sed -n 's/ *\([^<]*\).*/\1/p' /p/c/icecast.xml) + + +tmpf=$(mktemp) +xrandr >$tmpf + +# example xrandr output: 1280x800+0+0 +primary_res=$(awk '$2 == "connected" && $3 == "primary" { print $4 }' $tmpf | sed 's/+.*//') +secondary_res=$(awk '$2 == "connected" && $3 != "primary" { print $3 }' $tmpf | sed 's/+.*//') + +if [[ $secondary_res ]]; then + # assumes secondary is on the right + x_offset=${primary_res%%x*} + secondary_x=${secondary_res%%x*} + secondary_y=${secondary_res##*x} + stream_res=$(( secondary_x / 2 ))x$(( secondary_y / 2)) +else + x_offset=0 + stream_res=$primary_res +fi + + +opts=( + # nice to have: be a little less verbose + -hide_banner + -video_size $stream_res + -f x11grab + -framerate 4 + # input options come before -i + -i :0.0+$x_offset.0 + -vf drawbox=color=black + -vcodec libvpx + -g 8 + -quality realtime + -threads 2 + -error-resilient 1 + -content_type video/webm + -f webm + icecast://source:$pass@$host/fsf$mount_suffix.webm + +) + + +rm -f /tmp/iank-ffmpeg +mkfifo -m 0600 /tmp/iank-ffmpeg +echo executing: ffmpeg -stdin ${opts[@]} +# ffmpeg sits and waits until we do this. dunno why. whatever. +echo >/tmp/iank-ffmpeg & +ffmpeg ${opts[@]} > /tmp/a if (( $# == 0 )) && ! i3-msg -t get_tree | jq --stream -r 'select(.[1]|scalars!=null) | "\(.[0]|join(".")): \(.[1]|tojson)"' | grep 'marks.0: "abrowser"$' &>/dev/null; then @@ -60,7 +60,7 @@ if (( $# == 0 )) && ! i3-msg -t get_tree | jq --stream -r 'select(.[1]|scalars!= # into a single array instead of a list of arrays with [.[]], or else # it will add the arrays a bunch of times and give several results. # comm gives us just the new id. - id=$(i3-msg -t get_tree | jq -e '.nodes[].nodes[].nodes[].nodes | [.[]] + ( [.[].nodes[]]) | .[] | select(.window_properties.class=="abrowser") | .id' | comm -23 - $tmpf | head -n1) + id=$(i3-msg -t get_tree | jq -e '.nodes[].nodes[].nodes[].nodes | [.[]] + ( [.[].nodes[]]) + ( [.[].nodes[].nodes[]]) + ( [.[].nodes[].nodes[].nodes[]]) | .[] | select(.window_properties.class=="abrowser") | .id' | comm -23 - $tmpf | head -n1) rm -f $tmpf if [[ $id ]]; then i3-msg "[con_id=$id] mark abrowser" diff --git a/filesystem/usr/local/bin/ethusb-nm b/filesystem/usr/local/bin/ethusb-nm deleted file mode 100644 index 041124e..0000000 --- a/filesystem/usr/local/bin/ethusb-nm +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. - -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi -shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 -set -eE -o pipefail -trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR - -[[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@" - - -sed -i --follow-symlinks '/^[^#/]/s/^/#/' /etc/network/interfaces.d/ethusb diff --git a/filesystem/usr/local/bin/ethusb-static b/filesystem/usr/local/bin/ethusb-static deleted file mode 100644 index 97868d7..0000000 --- a/filesystem/usr/local/bin/ethusb-static +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. - -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi -shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 -set -eE -o pipefail -trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR - -[[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@" - - -shopt -s nullglob - -# we already configured the interface once, afterwards, comment and -# uncomment to enable/disable. This makes it so we don't depend on /p -# being mounted. - -if [[ -s /etc/network/interfaces.d/ethusb ]]; then - sed -i --follow-symlinks 's/^#//' /etc/network/interfaces.d/ethusb - exit 0 -fi - - -while read -r ip host mac; do - if [[ $mac != usb ]]; then - continue - fi - if [[ $host = ${HOSTNAME}c ]]; then - usbip=$ip - break - fi -done

/etc/network/interfaces.d/ethusb < /dev/null ||: if (( EPOCHSECONDS > start + 600 )); then fastcon=0 diff --git a/rootsshsync b/rootsshsync index 4767c7d..5395c44 100755 --- a/rootsshsync +++ b/rootsshsync @@ -95,3 +95,5 @@ if [[ ! -e $auth_file ]] || ! diff -q /root/.ssh/authorized_keys $auth_file; the cp -p /root/.ssh/authorized_keys $auth_file update-initramfs -u -k all fi + +rsync -tpur /p/c/subdir_files/.dsh /root diff --git a/script-files b/script-files index 712da26..e369ceb 100644 --- a/script-files +++ b/script-files @@ -45,6 +45,7 @@ my_bin_files=( prof-tail prof-notify /a/bin/newns/newns + /a/bin/fai/fai/config/distro-install-common/ethusb-static ) for f in /b/log-quiet/*; do diff --git a/subdir_files/.config/systemd/user/profanity.service b/subdir_files/.config/systemd/user/profanity.service index ed713cd..b7b9a05 100644 --- a/subdir_files/.config/systemd/user/profanity.service +++ b/subdir_files/.config/systemd/user/profanity.service @@ -11,8 +11,11 @@ Description=profanity After=gpg-agent.service [Service] -# bash is required to get colors working -ExecStart=/usr/bin/screen -S profanity -Dm /bin/bash -c profanity +# tmux requirement +Type=forking + +# new-session is a tmux command which allows launching profanity. +ExecStart=/usr/bin/tmux -L profanity new-session -d profanity [Install] WantedBy=default.target diff --git a/system-status b/system-status index d6269d9..ae37d3b 100755 --- a/system-status +++ b/system-status @@ -440,32 +440,30 @@ mute() { local locked export DISPLAY=:0 locked=false - if lock_info=$(xscreensaver-command -time); then + if lock_info=$(xscreensaver-command -time 2>/dev/null); then if [[ $lock_info != *non-blanked* ]]; then locked=true fi - else - locked=true - fi - midnight=$(date -d 00:00 +%s) - mdiff=$(( EPOCHSECONDS - midnight )) - if $locked && (( mdiff < 6 *60*60 || mdiff > 21 *60*60 )); then - case $(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') in - no) - # for log purposes - echo muted - pactl set-sink-mute @DEFAULT_SINK@ true - ;; - esac - fi - if ! $locked && (( mdiff > 6 *60*60 || mdiff < 12 *60*60 )) && [[ ! -e /tmp/ianknap ]]; then - case $(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') in - yes) - # for log purposes - echo unmuted - pactl set-sink-mute @DEFAULT_SINK@ false - ;; - esac + midnight=$(date -d 00:00 +%s) + mdiff=$(( EPOCHSECONDS - midnight )) + if $locked && (( mdiff < 6 *60*60 || mdiff > 21 *60*60 )); then + case $(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') in + no) + # for log purposes + echo muted + pactl set-sink-mute @DEFAULT_SINK@ true + ;; + esac + fi + if ! $locked && (( mdiff > 6 *60*60 || mdiff < 12 *60*60 )) && [[ ! -e /tmp/ianknap ]]; then + case $(pactl get-sink-mute @DEFAULT_SINK@ | awk '{print $2}') in + yes) + # for log purposes + echo unmuted + pactl set-sink-mute @DEFAULT_SINK@ false + ;; + esac + fi fi } -- 2.30.2 From 3ba18a2c386a5a9962cf7b47f490a17f244a0774 Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Sat, 25 May 2024 21:10:02 -0400 Subject: [PATCH 3/6] general fixes --- brc | 54 ++++++++++++++++++++++++++++-------- brc2 | 22 ++++++++++++--- distro-begin | 12 ++++---- input-setup | 8 +++--- subdir_files/.gnupg/gpg.conf | 4 +-- 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/brc b/brc index dc32a10..32a2c66 100644 --- a/brc +++ b/brc @@ -859,25 +859,40 @@ fpst() { # file paste } _khfix-common() { - local host ip port file key tmp - read -r host ip port < <(timeout -s 9 2 ssh -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $1 |& sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p" ||: ) + local host ip port file key tmp ssh_host alias + ssh_host=$1 + { + read -r host ip port + read -r alias; + # note ":graph:" is needed or else we get a trailing \r out of ssh, + # dunno why. web search says terminals add \r, so I tried adding -T + # to turn off psuedo terminal, but it didnt help. + } < <(timeout -s 9 2 ssh -TN -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $ssh_host |& + sed -rn "s/debug1: Connecting to ([^ ]+) \[([^\]*)] port ([0-9]+).*/\1 \2 \3/p; +s/^debug1: using hostkeyalias: ([[:graph:]]*).*/\1/p" ||: ) file=$(readlink -f ~/.ssh/known_hosts) if [[ ! $ip ]]; then echo "khfix: ssh failed" return 1 fi + ip_entry=$ip + host_entry=$host + if [[ $alias ]]; then + host_entry="$alias" + fi if [[ $port != 22 ]]; then ip_entry="[$ip]:$port" - host_entry="[$host]:$port" - else - ip_entry=$ip - host_entry=$host + if [[ ! $alias ]]; then + host_entry="[$host]:$port" + fi fi - if [[ $host != "$ip" ]]; then + if [[ $host_entry != "$ip_entry" ]]; then tmp=$(mktemp) ssh-keygen -F "$host_entry" -f $file >$tmp || [[ $? == 1 ]] # 1 when it doesnt exist in the file if [[ -s $tmp ]]; then key=$(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp) + else + echo "khfix WARNING: did not find host entry:$host_entry in known_hosts" fi rm $tmp if [[ $key ]]; then @@ -889,12 +904,13 @@ _khfix-common() { ssh-keygen -F "$ip_entry" -f $file >$tmp || [[ $? == 1 ]] if [[ -s $tmp ]]; then key=$(sed -r 's/^.*([^ ]+ +[^ ]+) *$/\1/' $tmp) + else + echo "khfix WARNING: did not find ip entry:$ip_entry in known_hosts" fi rm $tmp if [[ $key ]]; then grep -Fv "$key" "$file" | sponge "$file" fi - ll ~/.ssh/known_hosts } khfix-r() { # known hosts fix without syncing to root user _khfix-common "$@" || return 1 @@ -1419,7 +1435,12 @@ egrinid() { sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).* id=([^ ]+) T="(.*)" from (<[^ ]+> .*$)/\1 \5\n \3\n \4/p' <${1:-/var/log/exim4/mainlog} } etailin() { - tail -F /var/log/exim4/mainlog | sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' + local -a tail_arg + tail_arg=(-n500) + if [[ $1 ]]; then + tail_arg=($@) + fi + tail "${tail_arg[@]}" -F /var/log/exim4/mainlog | sed -rn '/testignore|jtuttle|eximbackup/!s/^[^ ]+ ([^ ]+) [^ ]+ [^ ]+ <= ([^ ]+).*T="(.*)" from (<[^ ]+> .*$)/\1 \4\n \3/p' } @@ -2775,8 +2796,15 @@ sl() { slr() { sl --rsync "$@" } -sss() { # ssh solo - sl -oControlMaster=no -oControlPath=/ "$@" + + +# ssh solo +# +# WARNING: If you are trying to use -i, remember that keys added to +# agent previously will still be tried. Use ssh-add -D to remove all +# keys from the agent. +sss() { + ssh -oControlMaster=no -oControlPath=/ "$@" } # kill off old shared socket then ssh ssk() { @@ -3448,6 +3476,10 @@ arpflush() { m s ip n flush dev "$default_route_dev" } +dsh() { + command dsh -c "$@" +} + # * stuff that makes sense to be at the end diff --git a/brc2 b/brc2 index 5fde368..6ac5183 100644 --- a/brc2 +++ b/brc2 @@ -441,7 +441,7 @@ ralerts() { # remote alerts ap() { # pushd in case current directory has an ansible.cfg file pushd /a/xans >/dev/null - ansible-playbook -v -l ${1:- $(hostname -f)} site.yml + ansible-playbook -v -i ${1:- $(hostname -f)}, site.yml popd >/dev/null } aw() { @@ -2963,7 +2963,7 @@ EOF done | s cedit -e hosts-file-up /etc/hosts for host in ${hosts[@]}; do echo $host - done | cedit -e /a/bin/ds/subdir_files/.dsh/group/btrbk + done >/p/c/subdir_files/.dsh/group/btrbk ### end focus on hosts file update ### @@ -3384,8 +3384,8 @@ myprof() { pushd /home/iank/.local/share/profanity/chatlogs/iank_at_fsf.org/rooms/office_at_conference.fsf.org logs=(*) logcount=${#logs[@]} - if (( logcount > 15 )); then - i=$(( logcount - 15 )) + if (( logcount > 16 )); then + i=$(( logcount - 16 )) else i=0 fi @@ -4776,6 +4776,20 @@ opensslcertinfo() { openssl x509 -txt -in "$@" } +# dsh on btrbk hosts +dsb() { +: + } + +# dsh a file and run it +dsa() { + local ret file + if ! parallel -j 10 scp x {}:/tmp <~/.dsh/group/btrbk; then + echo parallel scp failed. dsa returning $ret + fi + dsh -g btrbk + } + export BASEFILE_DIR=/a/bin/fai-basefiles #export ANDROID_HOME=/a/opt/android-home diff --git a/distro-begin b/distro-begin index 82b349e..74bbdff 100755 --- a/distro-begin +++ b/distro-begin @@ -553,7 +553,7 @@ fi # disabled temporarily ###### setup /i # if home_network; then -# sudo teeu /etc/fstab <<'EOF' +# sudo /a/exe/teeu /etc/fstab <<'EOF' # /i/w /w none bind,noauto 0 0 # /i/k /k none bind,noauto 0 0 # EOF @@ -562,11 +562,11 @@ fi # sudo chown $USER:user2 /kr # fi # if [[ $HOSTNAME == frodo ]]; then -# sudo teeu /etc/fstab <<'EOF' +# sudo /a/exe/teeu /etc/fstab <<'EOF' # /k /kr none bind,noauto 0 0 # EOF # else -# sudo teeu /etc/fstab <<'EOF' +# sudo /a/exe/teeu /etc/fstab <<'EOF' # frodo:/k /kr nfs noauto 0 0 # EOF # fi @@ -636,7 +636,7 @@ if has_btrfs; then fi first_root_crypt=$(awk '$2 == "/" {print $1}' /etc/mtab) - sudo teeu /etc/fstab < 2)) && echo ,compress=zstd ) 0 0 EOF sudo mkdir -p $dir @@ -649,7 +649,7 @@ fi case $HOSTNAME in kd) - sudo teeu /etc/fstab <<'EOF' + sudo /a/exe/teeu /etc/fstab <<'EOF' /dev/mapper/crypt_dev_ata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V-part7 /d btrfs nofail,x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s,noatime,compress=zstd,subvol=d 0 0 /d/m /i none bind,compress=zstd 0 0 EOF @@ -665,7 +665,7 @@ EOF fi ;; frodo) - sudo teeu /etc/fstab <<'EOF' + sudo /a/exe/teeu /etc/fstab <<'EOF' /dev/mapper/crypt_dev_ata-ata-Hitachi_HDS722020ALA330_JK1121YAG7SXWS-part1 /i btrfs nofail,x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s,noatime,subvol=i 0 0 EOF if ! mountpoint /i &>/dev/null; then diff --git a/input-setup b/input-setup index db8eb44..5b30b31 100755 --- a/input-setup +++ b/input-setup @@ -100,10 +100,10 @@ if set_device_id "Logitech Unifying Device"; then fi ## slow down ploopy trackball, until we recompile firmware -# id=$(xinput list | grep -F 'Ploopy Corporation Trackball Mouse' | sed -rn 's/.*[[:space:]]id=([^[:space:]]*).*/\1/p' ||:) -# if [[ $id ]]; then -# xinput --set-prop $id 'libinput Accel Speed' -0.9 -# fi +id=$(xinput list | grep -F 'Ploopy Corporation Trackball Mouse' | sed -rn 's/.*[[:space:]]id=([^[:space:]]*).*/\1/p' ||:) +if [[ $id ]]; then + xinput --set-prop $id 'libinput Accel Speed' -0.7 +fi set +x exit 0 diff --git a/subdir_files/.gnupg/gpg.conf b/subdir_files/.gnupg/gpg.conf index 035415d..8c6a6f1 100644 --- a/subdir_files/.gnupg/gpg.conf +++ b/subdir_files/.gnupg/gpg.conf @@ -46,9 +46,9 @@ default-key B125F60B7B287FF6A2B7DF8F170AF0E2954295DF #keyserver hkp://keys.gnupg.net #keyserver hkp://keyserver.ubuntu.com #keyserver hkp://keyring.debian.org -#keyserver keyserver.ubuntu.com +keyserver keyserver.ubuntu.com # more secure hkps, but had problems with my gpg version -keyserver hkps://hkps.pool.sks-keyservers.net +#keyserver hkps://hkps.pool.sks-keyservers.net ### begin things added by enigmail cert-digest-algo SHA256 -- 2.30.2 From e6cd2e555df3af0cf23da016b833529a34ffc84c Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Tue, 4 Jun 2024 22:43:02 -0400 Subject: [PATCH 4/6] mostly improvements, wip --- brc | 15 ++- brc2 | 39 +++---- dynamic-ip-update | 7 ++ ffp | 35 +++++++ ffs | 236 +++++++++++++++++++++++++++++++++++++++---- i3-maybe-double-move | 5 + i3-set-layout | 65 ++++++++++++ i3-split-maybe | 47 ++++++--- i3-sway/bar.conf | 31 ++++++ i3-sway/common.conf | 48 +++++---- i3-sway/gen | 5 + i3-sway/i3.conf | 34 +------ myi3status | 11 ++ obs-clip | 71 ------------- obs-i3-interlude | 35 ------- pkgs | 2 + script-files | 1 + stream-clip | 99 ++++++++++++++++++ stream-interlude | 83 +++++++++++++++ toggle-mute | 66 ++++++++++++ 20 files changed, 727 insertions(+), 208 deletions(-) create mode 100755 ffp create mode 100755 i3-set-layout create mode 100644 i3-sway/bar.conf delete mode 100755 obs-clip delete mode 100755 obs-i3-interlude create mode 100755 stream-clip create mode 100755 stream-interlude create mode 100755 toggle-mute diff --git a/brc b/brc index 32a2c66..8e1b327 100644 --- a/brc +++ b/brc @@ -689,10 +689,10 @@ jdo() { fi # -q = quiet journalctl -qn2 -f -u "$cmd_name" & + jr_pid=$! # Trial and error of time needed to avoid missing initial lines. # .5 was not reliable. 1 was not reliable. 2 was not reliable sleep 4 - jr_pid=$! systemd-run --unit "$cmd_name" --wait --collect "$cmd" "$@" || ret=$? # The sleep lets the journal output its last line # before the prompt comes up. @@ -2450,6 +2450,19 @@ serstat() { systemctl -n 40 status "$@" } +# assume last arg is a service and we want to tail its log. +serj() { + local service jr_pid ret + ret=0 + service="${*: -1}" + journalctl -qn2 -f -u "$service" & + sleep 3 + s systemctl "$@" || ret=$? + sleep .5 + kill %% + (( ret == 0 )) || return $ret +} + seru() { systemctl --user "$@"; } # like restart, but do nothing if its not already started srestart() { diff --git a/brc2 b/brc2 index 6ac5183..7699add 100644 --- a/brc2 +++ b/brc2 @@ -1682,11 +1682,9 @@ jdo() { if [[ $cmd != /* ]]; then cmd=$(type -P "$cmd") fi + #note date format for since is date '+%F %T' # -q = quiet - journalctl -qn2 -f -u "$cmd_name" & - # Trial and error of time needed to avoid missing initial lines. - # .5 was not reliable. 1 was not reliable. 2 was not reliable - sleep 4 + journalctl --since=now -qn2 -f -u "$cmd_name" & jr_pid=$! # note, we could have a version that does system --user, but if for example # it does sudo ssh, that will leave a process around that we can't kill @@ -4177,10 +4175,7 @@ vpn() { fi [[ $1 ]] || { echo need arg; return 1; } - journalctl --unit=$vpn_service@$1 -f -n0 & - # sometimes the journal doesnt open until after the vpn output - # has happened. hoping this fixes that. - sleep 1 + journalctl --since=now --unit=$vpn_service@$1 -f -n0 & sudo systemctl start $vpn_service@$1 # sometimes the ask-password agent does not work and needs a delay. sleep .5 @@ -4199,15 +4194,17 @@ fixu() { fi } -# unmute +# unmute desktop output um() { - local sink card + local sink card sedcmd sink=$(pactl get-default-sink) if [[ $sink == auto_null ]]; then # guessing there is just one with an off profile. otherwise we will # need some other solution, like storing the card identifier that we - # muted with nap. - card=$(pacmd list-cards | sed -n '/^[[:space:]]*index:/{s/^[[:space:]]*index://;h};/^[[:space:]]*active profile: $/{g;p;q}') + # muted with nap. Or, we could so some hakery with + # pactl -f json. + sedcmd='/^[[:space:]]*index:/{s/^[[:space:]]*index://;h};/^[[:space:]]*active profile: $/{g;p;q}' + card=$(pacmd list-cards | sed -n "$sedcmd") m pacmd set-card-profile "$card" output:analog-stereo fi @@ -4725,7 +4722,8 @@ ftoc() { units "tempF($1)" tempC } -# requires dns/firewall setup first +# note: requires dns setup of live.iankelling.org, & if i'm home, port +# forwarding in wrt-setup-local. todo: automate that. local-icecast() { web-conf -e ian@iankelling.org -f 8000 - apache2 live.iankelling.org <<'EOF' @@ -4778,17 +4776,24 @@ opensslcertinfo() { # dsh on btrbk hosts dsb() { -: - } + : +} # dsh a file and run it dsa() { local ret file if ! parallel -j 10 scp x {}:/tmp <~/.dsh/group/btrbk; then echo parallel scp failed. dsa returning $ret - fi + fi dsh -g btrbk - } +} + +# temporary +zmqsend() { + /nocow/t/ffmpeg-release/ffmpeg-7.0.1/tools/zmqsend "$@" +} + +ffg() { /nocow/t/ffmpeg-release/ffmpeg-7.0.1/tools/graph2dot -o /tmp/g.tmp && dot -Tpng /tmp/g.tmp -o /tmp/g.png && feh /tmp/g.png; } export BASEFILE_DIR=/a/bin/fai-basefiles diff --git a/dynamic-ip-update b/dynamic-ip-update index 12726ae..44d9ffe 100755 --- a/dynamic-ip-update +++ b/dynamic-ip-update @@ -88,6 +88,11 @@ main() { case $gateway in 10.2.0.1) dyndomain=b8.nz + # This domain is for any case where we want some different + # configuration based on lan vs wan. For right now, the only use + # is for ssh config to use port forwarding ports on the wan + # domain. + dyndomain_internet=i.b8.nz ;; *) return 0 @@ -176,6 +181,8 @@ EOF cat >>$tmpf </tmp/iank-ffmpeg & +# ffmpeg ... /tmp/iank-ffmpeg +# echo "cdrawbox -1 t fill" >/tmp/iank-ffmpeg +# echo "cdrawtext -1 reinit text=''" >/tmp/iank-ffmpeg +# echo "cvolume -1 volume=1" >/tmp/iank-ffmpeg + + +# For testing: to show the number of audio channels in a resulting file +# https://stackoverflow.com/questions/47905083/how-to-check-number-of-channels-in-my-audio-wav-file-using-ffmpeg-command +# +# ffprobe -i /tmp/out.wav -show_entries stream=channels -select_streams a:0 -of compact=p=0:nk=1 -v 0 + +# for a right/left speaker test: +# https://askubuntu.com/questions/148363/which-linux-command-can-i-use-to-test-my-speakers-for-current-talk-radio-output +# p install alsa-utils +# speaker-test -t wav -c 2 -l 1 + +# There are 2 other options for audio, so I wanted to do a little +# performance measurement of this method. +# 1 is to combine the 2 audio sources in pulse, +# https://unix.stackexchange.com/questions/351764/create-combined-source-in-pulseaudio . +# 1 is to record mumble and combine in post processing. + +### benchmark / perf tests: these are pretty inaccurate. +# 29 seconds cpu use. video bitrate 1500k, 8 fps, 2x keyframe interval. +# * 64k vorbis: 69.7% +# * 128k vorbis: 70.1% (used in subsequent tests) +# * 1 audio input: 64.3% +# * 0 audio inputs: 59.2% + +# how I did perf testing: add -to 00:00:30 to ffmpeg opts to +# conveniently exit after measurement. Then run: +# +# ffmpeg "${opts[@]}" & +# pid=$! +# sleep 29 +# ps -p $pid -o %cpu +# kill %% + +# filter for only 1 audio input: +#-filter_complex "[0]azmq,volume=precision=fixed;[1]zmq='b=tcp\://127.0.0.1\:5557',drawbox=color=0x262626,drawtext=fontsize=90: fontcolor=beige: x=40: y=40: text=''" +# filter with 0 audio input: +# -filter_complex "[0]zmq='b=tcp\://127.0.0.1\:5557',drawbox=color=0x262626,drawtext=fontsize=90: fontcolor=beige: x=40: y=40: text=''" + + +# When things weren't working, I did some checking on newer ffmpeg to +# see if that helped. It never did. I compiled the latest ffmpeg release +# tarball, 7.0.1, and tried the version in debian bullseye by schrooting +# before running ffmpeg. Building was just configure; make, but then I +# found some flags that were needed. gpl flags r just because I noticed them. +# ./configure --enable-libzmq --enable-libpulse --enable-libvorbis --enable-gpl --enable-version3 +# + +### end background/development docs ### + if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 @@ -32,12 +131,27 @@ set -eE -o pipefail trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR -# 3 mountpoints: fsf-sysops (public), fsf (default, all staff), fsf-tech (tech team) -case $1 in - sysops|tech) - mount_suffix=-$1 - ;; -esac +debug=false +loglevel=fatal + +# 3 mountpoints: fsf-sysops (default, public), fsf (all staff), fsf-tech (tech team) +# # note: duplicated in ffp +mount_suffix=-sysops +while [[ $1 ]]; do + case $1 in + sysops|tech) + mount_suffix=-$1 + ;; + staff) + mount_suffix= + ;; + -d) + debug=true + loglevel=debug + ;; + esac + shift +done host=live.iankelling.org:8000 if [[ $(dig +short @10.2.0.1 -x 10.2.0.2 2>&1 ||:) == kd.b8.nz. ]] \ @@ -66,31 +180,111 @@ else stream_res=$primary_res fi +framerate=8 +keyframe_interval=$((framerate * 2)) + +# Monitor of default sink. +# eg: alsa_output.usb-Audio-gd_Audio-gd-00.analog-stereo +pa_sink=$(pacmd list-sinks | awk '/\*/ {getline; print $2}' | sed 's/^$//').monitor opts=( - # nice to have: be a little less verbose + # global options + # be relatively quiet. switch to debug when testing. + -v $loglevel -hide_banner + -nostats + + # note: ordering of inputs also affects zmqsend commands. + + ## audio input options + + -f pulse + -name ffs + # note: duplicated above + -thread_queue_size 160 + -fragment_size 512 + -i default + + -f pulse + # this is for ffmpeg warnings. doesnt seem to affect latency. + -thread_queue_size 160 + # pulse knows this name somewhere + -name ffsdesktop + # This fixes latency. i haven't tried tuning it, but going too low creates + # choppy output. + -fragment_size 512 + -i "$pa_sink" + + + ## video input options -video_size $stream_res -f x11grab - -framerate 4 - # input options come before -i + -framerate $framerate -i :0.0+$x_offset.0 - -vf drawbox=color=black + + # Video + audio filter. Note: this has only the things we actually need in it. + # + # volume=precision=fixed fixes this error: + # The following filters could not choose their formats: Parsed_amerge_4. + # + # Default volume precision is float. Our input is fixed. maybe ffmpeg + # thinks the input could change and so can't commit to an output. + # The error suggests using aformat, which seems like it would probably + # also fix the error. + # + # man page say zmq url default includes "localhost", but specifying a + # localhost url caused an error for me. + -filter_complex "[0]azmq,volume=precision=fixed: volume=0 [vol0]; +[1]azmq='b=tcp\://127.0.0.1\:5556',volume=precision=fixed: volume=0 [vol1]; +[vol0][vol1] amerge=inputs=2 [out]; +[2]zmq='b=tcp\://127.0.0.1\:5557',drawbox=color=0x262626,drawtext=fontsize=90: fontcolor=beige: x=40: y=40: text=''" + + # Based on error message and poking around, it seems ffmpeg is not + # smart enough to see that [vol0] and [vol1] are inputs to the amerge + # filter, and thus we would not want them as final outputs. So, we + # have to identify the amerge output and pass it to -out. This + # identifier is called an "output pad" in man ffmpeg-filters, and a + # "link label" in man ffmpeg. + -map '[out]' + + # video output options -vcodec libvpx - -g 8 + -g $keyframe_interval -quality realtime + # for 1080p, default 256k is poor quality. 500 is ok. 1500 is a bit better. + -b:v 1500k -threads 2 + -buffer_duration 10 -error-resilient 1 + + ## audio output options + -c:a libvorbis + -b:a 128k + # afaik, this ensures that the amerge doesn't make 4 channel output if + # our output format supported it. + -ac 2 + -content_type video/webm -f webm icecast://source:$pass@$host/fsf$mount_suffix.webm - ) +rm -f /tmp/iank-ffmpeg-interlude-toggle + +# start muted +pactl set-source-mute @DEFAULT_SOURCE@ true + +#echo executing: ffmpeg ${opts[@]} + +#{ sleep 1; ffp &>/dev/null & } + +if $debug; then + ffmpeg "${opts[@]}" + exit 0 +fi -rm -f /tmp/iank-ffmpeg -mkfifo -m 0600 /tmp/iank-ffmpeg -echo executing: ffmpeg -stdin ${opts[@]} -# ffmpeg sits and waits until we do this. dunno why. whatever. -echo >/tmp/iank-ffmpeg & -ffmpeg ${opts[@]} /dev/null; then i3-msg "move $direction; move $direction" else diff --git a/i3-set-layout b/i3-set-layout new file mode 100755 index 0000000..89780d1 --- /dev/null +++ b/i3-set-layout @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +import sys +from i3ipc import Connection, Event + + +def find_parent(i3, window_id): + """ + Find the parent of a given window id + """ + + def finder(con, parent): + if con.id == window_id: + return parent + for node in con.nodes: + res = finder(node, con) + if res: + return res + return None + + return finder(i3.get_tree(), None) + + +def set_layout(i3): + """ + Set the layout/split for the currently + focused window to either vertical or + horizontal, depending on its width/height + """ + + + + win = i3.get_tree().find_focused() + parent = find_parent(i3, win.id) + + + # We never want to set the layout of a single window container, + # there are already keys for that. I don't know why i3 even does + # this, it is stupid. So, eliminate single window container if we + # are focused on one. + # + # Alternatively, it could first focus the parent, but I think when + # layout changes, we expect new windows to be created within that + # layout. + if (parent and len(parent.nodes) == 1): + gp = find_parent(i3, parent.id) + if (gp.nodes[0].id == parent.id): + if (gp.layout == 'splitv'): + i3.command('move down') + else: # splith or tabbed + i3.command('move right') + else: + if (gp.layout == 'splitv'): + i3.command('move up') + else: + i3.command('move left') + i3.command('layout ' + sys.argv[1]) + +def main(): + i3 = Connection() + set_layout(i3) + + +if __name__ == "__main__": + main() diff --git a/i3-split-maybe b/i3-split-maybe index 69e42c5..dcb268a 100755 --- a/i3-split-maybe +++ b/i3-split-maybe @@ -29,21 +29,41 @@ set -e; . /usr/local/lib/bash-bear; set +e # just after a window is created, or just before a window is # created. # -# *Doing it after a window is created allows you to move a window into +# * Doing it after a window is created allows you to move a window into # the split that only has 1 window, whereas the other way doesn't. For # my use cases, I think I don't really want to move it into the split if -# it is a tabbed split. +# it is a tabbed split. upon further reflection, I've determined that +# single window containers are inherently confusing because they tend to +# exist and get nested at unexpected times and then it is unclear how to +# get rid of them and what is going on and the benefit is generally not +# worth it. This command helps identify single window containers during +# testing: /a/opt/i3ipc-python/examples/i3-debug-console.py # -# *Doing it just before a windows is created, you need to -# call this script, which means wrapping launch of a program, which I -# have no way to do for all cases, I just do it for the common programs -# I have bound to keys in i3. +# * Doing it just before a windows is created, you need to call this +# script, which means wrapping launch of a program, which I have no way +# to do for all cases, I just do it for the common programs I have bound +# to keys in i3. # # * Doing it after a window is created also leaves that split behind if -# * the window is closed. I partially deal with that below. +# the window is closed. I partially deal with that below. # # I have a keybind which disables both, it runs /b/ds/i3-auto-layout-toggle + +dry_run=false +m() { "$@"; } +d() { + if $dry_run; then + printf "%s\n" "$*" + fi +} +case $1 in + -n) + dry_run=true + m() { printf "%s\n" "$*"; } + ;; +esac + if [[ -e /tmp/iank-i3-no-auto ]]; then exit 0 fi @@ -57,26 +77,27 @@ i3-msg -t get_workspaces | jq ".[]| select(.focused==true) | .rect | .width, .he i3-msg -t get_tree | jq -r ".. | select(.focused? == true).rect | .width, .height" >$tmp -half_w=$(( screen_width / 2 + 100 )) -half_h=$(( screen_height / 2 + 100 )) +half_w=$(( screen_width / 2 )) +half_h=$(( screen_height / 2 )) { read -r w; read -r h; } <$tmp +d w=$w , h=$h , half_w=$half_w , half_h=$half_h if (( screen_width < 1920 )); then # haven't considered this case yet exit 0 fi -if (( w < half_w && h < half_h )); then - i3-msg "split vertical, layout tabbed" + +if (( w <= half_w && h <= half_h )); then + m i3-msg "split vertical, layout tabbed" elif (( w == screen_width )); then - : # if we had 2 windows on screen, made them vertical splits, then # closed one, it stays vertical split, but we want it horizontal at # that point. So, make it horizontal here. - i3-msg "split horizontal" + m i3-msg "split horizontal" fi rm -f $tmp diff --git a/i3-sway/bar.conf b/i3-sway/bar.conf new file mode 100644 index 0000000..d1457be --- /dev/null +++ b/i3-sway/bar.conf @@ -0,0 +1,31 @@ +# need this for kde connect +bar { + +# keep it only on secondary monitor to save space and make for less +# missing pixes in obs live stream. For docs on this, search "output +# primary" in the i3 guide. +output primary + +# the builtin prog +#status_command i3status + +#for faster testing +#status_command /a/bin/ds/myi3status +status_command /usr/local/bin/myi3status +#mode hide +# hidden_state hide +font pango:monospace 18 + +# i have no need for the tray icons so far +tray_output primary + +# I found I didn't need these, but, I'm trying them out again. +# workspace_buttons no +} + +## dont want to see this bar for now +# bar { +# status_command /p/c/myi3life +# tray_output none +# workspace_buttons no +# } diff --git a/i3-sway/common.conf b/i3-sway/common.conf index 017f4bd..2cc2d1c 100644 --- a/i3-sway/common.conf +++ b/i3-sway/common.conf @@ -31,7 +31,7 @@ bindsym $mod+3 $ex "/b/ds/i3-split-maybe"; exec "abrowser" bindsym $mod+4 $ex "/b/ds/i3-split-maybe"; exec "abrowser -no-remote -P firefox-main-profile" # todo: figure out a stream delay & way to cut the stream. # settings, advanced, stream delay -bindsym $mod+5 $ex "/a/bin/ds/obs-i3-interlude" +bindsym $mod+5 $ex "/a/bin/ds/stream-interlude" bindsym $mod+6 $ex "/b/ds/i3-split-maybe"; exec "/usr/local/bin/start-tor-browser" bindsym $mod+7 $ex "/a/bin/ds/laptop-xrandr" #bindsym $mod+6 $ex "/a/bin/redshift.sh" @@ -45,7 +45,7 @@ bindsym $mod+1 focus parent bindsym $mod+shift+1 focus child # undo split: https://github.com/i3/i3/issues/3808 bindsym $mod+grave floating toggle; floating toggle -bindsym $mod+equal $ex "dunstctl close-all" +bindsym $mod+equal $ex "/a/exe/i3-set-layout splith" # move firefox to current workspace. # https://i3wm.org/docs/userguide.html#keybindings # get class with xprop, example output @@ -57,14 +57,24 @@ bindsym $mod+shift+w fullscreen toggle bindsym $mod+e $ex i3-pull emacs bindsym $mod+shift+e unmark emacs; mark emacs bindsym $mod+r $ex "/a/bin/ds/xl" -# todo, in newer i3, make this toggle split tabbed -bindsym $mod+t layout toggle splith splitv tabbed + +# todo, in newer i3, make this toggle split tabbed. +bindsym $mod+t $ex "/a/exe/i3-set-layout splitv" #bindsym $mod+Shift+t move workspace to output up bindsym $mod+Shift+t move workspace to output right -# there's a bug about this. it is not logical that there is no "split -# tabbed", but you accomplish that by doing this. -bindsym $mod+g split horizontal, layout tabbed -bindsym $mod+shift+n layout tabbed + +# todo: consider a command that moves a window, and erases any single +# container window left behind. + +# todo: port /b/ds/i3-maybe-double-move into python. + +# todo: consider a command which alters things as if the current window +# had been created into a single window split. For horizontal split, +# this would be like: focus left, split vertical, focus right, move +# left. With that, we could totally eliminate single window containers. + +bindsym $mod+g $ex "/a/exe/i3-set-layout tabbed" + bindsym $mod+shift+g $ex "/b/ds/i3-auto-layout-toggle" # Use Mouse+$mod to drag floating windows to their wanted position @@ -129,6 +139,7 @@ bindsym $mod+8 workspace 9 bindsym $mod+Shift+9 move container to workspace 10 bindsym $mod+9 workspace 10 +bindsym $mod+m $ex "dunstctl close-all" bindsym $mod+Shift+m border toggle # 65 = space. @@ -149,14 +160,14 @@ bindcode $mod+shift+65 focus mode_toggle # Use Mouse+$mod to drag floating windows to their wanted position floating_modifier $mod -bindsym $mod+shift+h $ex obs-clip hc +bindsym $mod+shift+h $ex /b/ds/stream-clip hc bindsym $mod+j $ex "/b/ds/i3-split-maybe"; exec emacsclient -c -bindsym $mod+shift+j $ex obs-clip up +bindsym $mod+shift+j $ex /b/ds/stream-clip up bindsym $mod+k $ex "/b/ds/i3-split-maybe"; exec konsole -bindsym $mod+shift+k $ex obs-clip intro +bindsym $mod+shift+k $ex /b/ds/stream-clip intro bindsym $mod+l $ex dmenu_run -bindsym $mod+shift+l $ex obs-clip steady -bindsym $mod+shift+semicolon $ex obs-clip sad +bindsym $mod+shift+l $ex /b/ds/stream-clip steady +bindsym $mod+shift+semicolon $ex /b/ds/stream-clip sad # note default is 27% on my system76. not sure if these # keybinds will screw up other laptop brightness keys. bindsym XF86MonBrightnessUp $ex brightnessctl s +5% @@ -166,7 +177,6 @@ bindsym XF86MonBrightnessDown $ex brightnessctl s 5%- # is used in the bar {} block below. font pango:monospace 7 -# todo: only available in newer i3n hide_edge_borders vertical #exec --no-startup-id /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd @@ -176,11 +186,13 @@ hide_edge_borders vertical # shortcut to selection widget (primary) -bindsym $mod+End $ex /a/opt/clipster/clipster -sp +bindsym $mod+End $ex "/b/ds/toggle-mute" # title bars but no borders. i tried this out a bit #default_border normal 0 default_border pixel 4 +# for debugging +default_border normal 10 # I dont see a way to make processing windows act like normal windows, # this does it. @@ -193,6 +205,6 @@ default_border pixel 4 # this is the processing window for my app named focus. for_window [class="focus" instance="focus"] floating disable -client.focused #4c7899 #285577 #ffffff #2e9ef4 #ff4400 -client.focused_inactive #333333 #5f676a #ffffff #484e50 #DBEEF4 -client.unfocused #333333 #222222 #888888 #292d2e #B8C8CD +# client.focused #4c7899 #285577 #ffffff #2e9ef4 #ff4400 +# client.focused_inactive #333333 #5f676a #ffffff #484e50 #DBEEF4 +# client.unfocused #333333 #222222 #888888 #292d2e #B8C8CD diff --git a/i3-sway/gen b/i3-sway/gen index 99e4503..cf262ad 100755 --- a/i3-sway/gen +++ b/i3-sway/gen @@ -32,3 +32,8 @@ cat common.conf sway.conf > $dir/config dir=/a/bin/distro-setup/subdir_files/.config/i3 mkdir -p $dir cat common.conf i3.conf > $dir/config + +monitor_count=$(xrandr|grep -c ' connected') +if [[ $1 == bar ]] || (( monitor_count >= 2 )); then + cat bar.conf >> $dir/config +fi diff --git a/i3-sway/i3.conf b/i3-sway/i3.conf index 265a932..737acc2 100644 --- a/i3-sway/i3.conf +++ b/i3-sway/i3.conf @@ -3,39 +3,9 @@ bindsym $mod+Shift+o exec "i3-nagbar -t warning -m 'You pressed the exit shortcu bindsym $mod+Shift+p restart -# need this for kde connect -# bar { - -# # keep it only on secondary monitor to save space and make for less -# # missing pixes in obs live stream. For docs on this, search "output -# # primary" in the i3 guide. -# output primary - -# # the builtin prog -# #status_command i3status - -# #for faster testing -# #status_command /a/bin/ds/myi3status -# status_command /usr/local/bin/myi3status -# #mode hide -# # hidden_state hide -# font pango:monospace 18 - -# # i have no need for the tray icons so far -# tray_output primary - -# # I found I didn't need these, but, I'm trying them out again. -# # workspace_buttons no -# } - -## dont want to see this bar for now -# bar { -# status_command /p/c/myi3life -# tray_output none -# workspace_buttons no -# } $ex copyq $ex dunst $ex /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd -$ex /a/opt/i3-alternating-layout/alternating_layouts.py +# this dies when we restart i3. +exec_always --no-startup-id alternating_layouts.py diff --git a/myi3status b/myi3status index bd0c634..2df9746 100755 --- a/myi3status +++ b/myi3status @@ -181,6 +181,17 @@ main() { ps_char="$ps_char O" fi + if pgrep -fc '^ffmpeg.*icecast://source.*/fsf' &>/dev/null; then + if [[ -e /tmp/iank-ffmpeg-interlude-toggle ]]; then + ps_char="= BRB = $ps_char" + else + ps_char="=|=|= STREAMING =|=|= $ps_char" + if pactl get-source-mute @DEFAULT_SOURCE@ 2>/dev/null | awk '{print $2}' | grep no &>/dev/null; then + ps_char="! UNMUTED ! $ps_char" + fi + fi + fi + printf '{ "name":"status", "color":"#ED297D", "full_text": "%s' "$ps_char" printf '"},' diff --git a/obs-clip b/obs-clip deleted file mode 100755 index 60bafda..0000000 --- a/obs-clip +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. - -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e; . /usr/local/lib/bash-bear; set +e - -type=$1 - -cd /a/bin/data/clips/$type - -if pgrep mpv; then - pkill mpv - exit 0 -fi - - -case $type in - up) - if [[ ! -s /tmp/last-up ]]; then - find . -type f -printf '%f\n' | shuf > /tmp/last-up - fi - clip=$(head -n1 /tmp/last-up) - tail -n+2 /tmp/last-up | sponge /tmp/last-up - ;; - *) - clip=$(find . -type f -printf '%f\n' | \ - { if [[ -e /tmp/last-$type ]]; then - sed "/^$(cat /tmp/last-$type)\$/d" - else - cat - fi ; } | \ - shuf | head -n1) - echo $clip >/tmp/last-$type - ;; -esac - -found=false - -p=$(cat /p/obs-ws-pass) -# note, if the desktop audio is already on, this will do the wrong thing. -# obs-cmd needs more commands. But, I don't use desktop audio for anything -# else atm. -if pgrep '^obs$' &>/dev/null; then - # this is so the script keeps working when obs is not running, but - # also doesn't ignore errors. - found=true - obs-cmd -w obsws://localhost:4455/$p toggle-mute 'Desktop Audio' -fi -mpv --profile=a $clip ||: - -if $found; then - obs-cmd -w obsws://localhost:4455/$p toggle-mute 'Desktop Audio' -fi diff --git a/obs-i3-interlude b/obs-i3-interlude deleted file mode 100755 index 03636da..0000000 --- a/obs-i3-interlude +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. - -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e; . /usr/local/lib/bash-bear; set +e - -if [[ -e /tmp/no-obs-auto-scene-switch ]]; then - rm -f /tmp/no-obs-auto-scene-switch - if [[ -s /tmp/last-obs-i3-mark ]]; then - p=$(cat /p/obs-ws-pass) - mark=$(cat /tmp/last-obs-i3-mark) - obs-cmd -w obsws://localhost:4455/$p scene switch $mark - fi -else - touch /tmp/no-obs-auto-scene-switch - obs-cmd -w obsws://localhost:4455/$p scene switch interlude -fi diff --git a/pkgs b/pkgs index 97e4e9d..8d58447 100644 --- a/pkgs +++ b/pkgs @@ -255,6 +255,8 @@ p3=( pidgin pidgin-otr pixz + # unattended-upgrades.log: Please install powermgmt-base package to check power status + powermgmt-base profanity pry # https://wiki.archlinux.org/title/bluetooth diff --git a/script-files b/script-files index e369ceb..2bc52a0 100644 --- a/script-files +++ b/script-files @@ -46,6 +46,7 @@ my_bin_files=( prof-notify /a/bin/newns/newns /a/bin/fai/fai/config/distro-install-common/ethusb-static + /a/opt/i3-alternating-layout/alternating_layouts.py ) for f in /b/log-quiet/*; do diff --git a/stream-clip b/stream-clip new file mode 100755 index 0000000..267ca31 --- /dev/null +++ b/stream-clip @@ -0,0 +1,99 @@ +#!/bin/bash +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to switch +# its license to GPL. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e; . /usr/local/lib/bash-bear; set +e + +type=$1 + +cd /a/bin/data/clips/$type + +pregex='^mpv --profile=a [^/]+$' +if pgrep -fc "$pregex" >/dev/null; then + pkill -f "$pregex" + exit 0 +fi + +case $type in + up) + if [[ ! -s /tmp/last-up ]]; then + find . -type f -printf '%f\n' | shuf > /tmp/last-up + fi + clip=$(head -n1 /tmp/last-up) + tail -n+2 /tmp/last-up | sponge /tmp/last-up + ;; + *) + # don't listen to the very last clip, but otherwise we don't mind + # recent repeats. + count=$(find . -type f -printf '%f\n' | wc -l) + if (( count > 1 )); then + clip=$(find . -type f -printf '%f\n' | \ + { if [[ -e /tmp/last-$type ]]; then + sed "/^$(cat /tmp/last-$type)\$/d" + else + cat + fi ; } | \ + shuf | head -n1) + echo $clip >/tmp/last-$type + else + clip=./* + fi + ;; +esac + +found=false + +# When I was using obs. Incorporate if I try it again. +# unmute() { +# p=$(cat /p/obs-ws-pass) +# # note, if the desktop audio is already on, this will do the wrong thing. +# # obs-cmd needs more commands. But, I don't use desktop audio for anything +# # else atm. +# if pgrep '^obs$' &>/dev/null; then +# # this is so the script keeps working when obs is not running, but +# # also doesn't ignore errors. +# found=true +# obs-cmd -w obsws://localhost:4455/$p toggle-mute 'Desktop Audio' +# fi +# } +# mute() { +# if $found; then +# obs-cmd -w obsws://localhost:4455/$p toggle-mute 'Desktop Audio' +# fi +# } + + +# note: condition duplicated in stream-clip, myi3status +if pgrep -fc '^ffmpeg.*icecast://source.*/fsf' >/dev/null; then + found=true + toggle-mute mute + echo Parsed_volume_3 volume 1 | zmqsend -b tcp://127.0.0.1:5556 +fi + +mpv --profile=a $clip ||: + +if $found; then + # I dunno if this is needed, but I think it is theoretically possible + # for us to mute ffmpeg before it finishes processing the mpv + # output. It would probably only need a few miliseconds, but whatever. + sleep 1 + echo Parsed_volume_3 volume 0 | zmqsend -b tcp://127.0.0.1:5556 +fi diff --git a/stream-interlude b/stream-interlude new file mode 100755 index 0000000..ea5812e --- /dev/null +++ b/stream-interlude @@ -0,0 +1,83 @@ +#!/bin/bash +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to switch +# its license to GPL. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e; . /usr/local/lib/bash-bear; set +e + +obs-interlude() { + p=$(cat /p/obs-ws-pass) + if [[ -e /tmp/no-obs-auto-scene-switch ]]; then + rm -f /tmp/no-obs-auto-scene-switch + if [[ -s /tmp/last-obs-i3-mark ]]; then + mark=$(cat /tmp/last-obs-i3-mark) + obs-cmd -w obsws://localhost:4455/$p scene switch $mark + fi + else + touch /tmp/no-obs-auto-scene-switch + obs-cmd -w obsws://localhost:4455/$p scene switch interlude + fi + +} + +ffmpeg-interlude() { + f=/tmp/iank-ffmpeg-interlude-toggle + + # interlude off + if [[ -e $f ]]; then + + rm -f $f + # note: get _6 from looking for "parsed" ffmpeg debug output. + zsend -b tcp://127.0.0.1:5557 Parsed_drawbox_6 t 0 + zsend -b tcp://127.0.0.1:5557 Parsed_drawtext_7 reinit "text=''" + + else + + # I started an attempt to track if I was muted before an interlude, + # but decided against it. Seems easier to handle unmuting with + # whatever normal process I have for it. + # + # muted=$(pactl get-source-mute @DEFAULT_SOURCE@ | awk '{print $2}') + + zsend Parsed_volume_1 volume 0 + zsend -b tcp://127.0.0.1:5557 Parsed_drawbox_6 t fill + zsend -b tcp://127.0.0.1:5557 Parsed_drawtext_7 reinit "text='$(date "+%H\:%M %Z") - Be right back'" + touch $f + fi +} + +zsend() { + local out + if [[ $1 == -b ]]; then + zmq_args=("$1" "$2") + shift 2 + fi + out=$(printf "%s\n" "$*" | zmqsend ${zmq_args[@]} ||:) + if [[ $out != "0 Success" ]]; then + i3-nagbar -m "FAILED zmqsend: $*" -t error -f "pango:monospace 30" + fi +} + + +if pgrep '^obs$' &>/dev/null; then + obs-interlude +else + ffmpeg-interlude +fi diff --git a/toggle-mute b/toggle-mute new file mode 100755 index 0000000..b50b37e --- /dev/null +++ b/toggle-mute @@ -0,0 +1,66 @@ +#!/bin/bash +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi +shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4 +set -eE -o pipefail +trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR + + +mute=true +volume_level=0 + +# mute / unmute instead of toggle. +if [[ $1 ]]; then + case $1 in + mute) + mute=true + ;; + unmute) + mute=false + ;; + esac +else + + muted=$(pactl get-source-mute @DEFAULT_SOURCE@ | awk '{print $2}' ||:) + case $muted in + no) : ;; + yes) mute=false; volume_level=1 ;; + *) + i3-nagbar -m "FAILED TO GET PULSE MUTE STATE" -t error -f "pango:monospace 30" + ;; + esac +fi + +# we double mute here because it could be useful, and I figured out how +# and feel like using what I know. + +pactl set-source-mute @DEFAULT_SOURCE@ $mute + +# note: condition duplicated in stream-clip, myi3statsus +if pgrep -fc '^ffmpeg.*icecast://source.*/fsf' >/dev/null; then + out=$(echo Parsed_volume_1 volume $volume_level | zmqsend ||:) + if [[ $out != "0 Success" ]]; then + i3-nagbar -m "FAILED to set ffmpeg volume to $volume_level" -t error -f "pango:monospace 30" + fi +fi -- 2.30.2 From dab96f8fa4c701db13ba734fa0c07b5d12fc8fae Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Wed, 5 Jun 2024 14:32:38 -0400 Subject: [PATCH 5/6] i3 improvements wip --- i3-event-hook | 61 +++++++++++++++++ i3-maybe-double-move | 35 ---------- i3-set-layout | 42 ++++++------ i3-split-maybe | 151 ++++++++++++++++++++++++------------------- i3-split-push | 87 +++++++++++++++++++++++++ i3-sway/common.conf | 45 +++++++------ i3-sway/i3.conf | 3 +- script-files | 8 ++- 8 files changed, 289 insertions(+), 143 deletions(-) create mode 100755 i3-event-hook delete mode 100755 i3-maybe-double-move create mode 100755 i3-split-push diff --git a/i3-event-hook b/i3-event-hook new file mode 100755 index 0000000..01514a6 --- /dev/null +++ b/i3-event-hook @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +import sys +import os +from i3ipc import Connection, Event +from pprint import pprint + + +def find_parent(i3, window_id): + """ + Find the parent of a given window id + """ + + def finder(con, parent, gp): + if con.id == window_id: + return (parent, gp) + for node in con.nodes: + res = finder(node, con, parent) + if res: + return res + return None + + return finder(i3.get_tree(), None, None) + + +def focus_hook(i3, e): + """ + Set the layout/split for the currently + focused window to either vertical or + horizontal, depending on its width/height + """ + + if os.path.isfile("/tmp/iank-i3-no-auto"): + return + + # debugging + #pprint(vars(e)) + + parent, gp = find_parent(i3, e.container.id) + + # This gets rid of tabbed container with single windows. + #if (parent and gp and parent.layout == 'tabbed' and len(parent.nodes) == 1): + # This gets rid of all single window containers. + if (parent and gp and len(parent.nodes) == 1): + i3.command('mark i3ha') + i3.command('focus parent') + i3.command('focus parent') + i3.command('mark i3hb') + i3.command('[con_mark="i3ha"] focus') + i3.command('move window to mark i3hb') + i3.command('unmark i3ha') + i3.command('unmark i3hb') + +def main(): + i3 = Connection() + i3.on(Event.WINDOW_FOCUS, focus_hook) + i3.main() + + +if __name__ == "__main__": + main() diff --git a/i3-maybe-double-move b/i3-maybe-double-move deleted file mode 100755 index 4f45238..0000000 --- a/i3-maybe-double-move +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. - -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e; . /usr/local/lib/bash-bear; set +e - -direction="$1" - -# for testing, always do normal move -i3-msg "move $direction" -exit 0 - -if i3-msg -t get_tree | jq -e -C '.nodes[].nodes[].nodes[].nodes[] | select((.nodes| length == 1) and (.nodes[0].focused == true))' &>/dev/null; then - i3-msg "move $direction; move $direction" -else - i3-msg "move $direction" -fi diff --git a/i3-set-layout b/i3-set-layout index 89780d1..fc68666 100755 --- a/i3-set-layout +++ b/i3-set-layout @@ -9,16 +9,16 @@ def find_parent(i3, window_id): Find the parent of a given window id """ - def finder(con, parent): + def finder(con, parent, gp): if con.id == window_id: - return parent + return (parent, gp) for node in con.nodes: - res = finder(node, con) + res = finder(node, con, parent) if res: return res return None - return finder(i3.get_tree(), None) + return finder(i3.get_tree(), None, None) def set_layout(i3): @@ -28,10 +28,8 @@ def set_layout(i3): horizontal, depending on its width/height """ - - win = i3.get_tree().find_focused() - parent = find_parent(i3, win.id) + parent, gp = find_parent(i3, win.id) # We never want to set the layout of a single window container, @@ -39,21 +37,25 @@ def set_layout(i3): # this, it is stupid. So, eliminate single window container if we # are focused on one. # - # Alternatively, it could first focus the parent, but I think when + # Alternatively, we could first focus the parent, but I think when # layout changes, we expect new windows to be created within that # layout. - if (parent and len(parent.nodes) == 1): - gp = find_parent(i3, parent.id) - if (gp.nodes[0].id == parent.id): - if (gp.layout == 'splitv'): - i3.command('move down') - else: # splith or tabbed - i3.command('move right') - else: - if (gp.layout == 'splitv'): - i3.command('move up') - else: - i3.command('move left') + # + # Todo: if the direction we are moving has a split/tabbed container + # as a peer to parent, this will move our window into that container + # instead of what we want. And in fact, the whole logic below is + # incorrect and based on testing which did not realize that fact. + # + if (parent and gp and len(parent.nodes) == 1): + # https://unix.stackexchange.com/questions/173754/how-to-move-a-window-up-to-the-level-of-its-parent-window-in-i3wm + i3.command('mark i3ha') + i3.command('focus parent') + i3.command('focus parent') + i3.command('mark i3hb') + i3.command('[con_mark="i3ha"] focus') + i3.command('move window to mark i3hb') + i3.command('unmark i3ha') + i3.command('unmark i3hb') i3.command('layout ' + sys.argv[1]) def main(): diff --git a/i3-split-maybe b/i3-split-maybe index dcb268a..d8bd54c 100755 --- a/i3-split-maybe +++ b/i3-split-maybe @@ -1,33 +1,8 @@ -#!/bin/bash -# I, Ian Kelling, follow the GNU license recommendations at -# https://www.gnu.org/licenses/license-recommendations.en.html. They -# recommend that small programs, < 300 lines, be licensed under the -# Apache License 2.0. This file contains or is part of one or more small -# programs. If a small program grows beyond 300 lines, I plan to switch -# its license to GPL. +#!/usr/bin/python3 -# Copyright 2024 Ian Kelling - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -set -e; . /usr/local/lib/bash-bear; set +e - -# We use this along with -# /a/opt/i3-alternating-layout/alternating_layouts.py to anticipate when -# we want to split/tab windows. There are 2 options of when to do it: -# just after a window is created, or just before a window is -# created. +# This anticipates when we want to tab windows. There are 2 options of +# when to do it: just after a window is created, or just before a window +# is created. # # * Doing it after a window is created allows you to move a window into # the split that only has 1 window, whereas the other way doesn't. For @@ -44,60 +19,100 @@ set -e; . /usr/local/lib/bash-bear; set +e # to do for all cases, I just do it for the common programs I have bound # to keys in i3. # -# * Doing it after a window is created also leaves that split behind if -# the window is closed. I partially deal with that below. +# * Note: doing it just before a window is created also leaves that split behind if +# the window is closed, and I don't want single window splits hanging around, +# so I close them out in # # I have a keybind which disables both, it runs /b/ds/i3-auto-layout-toggle -dry_run=false -m() { "$@"; } -d() { - if $dry_run; then - printf "%s\n" "$*" - fi -} -case $1 in - -n) - dry_run=true - m() { printf "%s\n" "$*"; } - ;; -esac +import sys +import os +from i3ipc import Connection, Event +# for debugging +#from pprint import pprint + + +def find_parent(i3, window_id): + """ + Find the parent of a given window id + """ + + def finder(con, parent, gp): + if con.id == window_id: + return (parent, gp) + for node in con.nodes: + res = finder(node, con, parent) + if res: + return res + return None + + return finder(i3.get_tree(), None, None) + + +def set_layout(i3): + """ + Set the layout/split for the currently + focused window to either vertical or + horizontal, depending on its width/height + """ + + if os.path.isfile("/tmp/iank-i3-no-auto"): + return + + win = i3.get_tree().find_focused() + parent, gp = find_parent(i3, win.id) + -if [[ -e /tmp/iank-i3-no-auto ]]; then - exit 0 -fi + workspace = win.workspace() + #pprint(vars(workspace.rect)) + screen_width = workspace.rect.width + screen_height = workspace.rect.height + half_w = screen_width / 2 + 1 + half_h = screen_height / 2 + 1 -tmp=$(mktemp) + w = win.rect.width + h = win.rect.height + ph = parent.rect.height + pw = parent.rect.width -i3-msg -t get_workspaces | jq ".[]| select(.focused==true) | .rect | .width, .height" >$tmp + # There is potential for future use with < 1920, but I'm + # not thinking about it yet. + if ( screen_width < 1920 or parent.layout == 'tabbed' or gp.layout == 'tabbed'): + return -{ read -r screen_width; read -r screen_height; } <$tmp + # print('d2: len(parent.nodes)', len(parent.nodes),' > 1', + # 'and ( ph ',ph,' > h + 10',h + 10,' or pw',pw,' > w',w,' )', + # 'and w <= half_w',half_w,'+ and h <= half_h',half_h) -i3-msg -t get_tree | jq -r ".. | select(.focused? == true).rect | .width, .height" >$tmp + # h + 10 because a tabbed window loses high compared to its parent. + # Note, it is redundant since we check above if the parent is tabbed, + # but just being cautious. + if (len(parent.nodes) > 1 + and ( ph > h + 10 or pw > w ) + and w <= half_w and h <= half_h ): + i3.command('split vertical, layout tabbed') +# print('d1: tabbed') -half_w=$(( screen_width / 2 )) -half_h=$(( screen_height / 2 )) +### further potential use cases: -{ read -r w; read -r h; } <$tmp +# We could automatically do a vertical split when there are 2 or 3 +# horizontal windows. -d w=$w , h=$h , half_w=$half_w , half_h=$half_h +# We could undo a vertical split when we close out windows. +# elif (( w == screen_width )); then +# # if we had 2 windows on screen, made them vertical splits, then +# # closed one, it stays vertical split, but we want it horizontal at +# # that point. So, make it horizontal here. +# m i3-msg "split horizontal" -if (( screen_width < 1920 )); then - # haven't considered this case yet - exit 0 -fi +def main(): + i3 = Connection() + set_layout(i3) -if (( w <= half_w && h <= half_h )); then - m i3-msg "split vertical, layout tabbed" -elif (( w == screen_width )); then - # if we had 2 windows on screen, made them vertical splits, then - # closed one, it stays vertical split, but we want it horizontal at - # that point. So, make it horizontal here. - m i3-msg "split horizontal" -fi -rm -f $tmp +if __name__ == "__main__": + main() diff --git a/i3-split-push b/i3-split-push new file mode 100755 index 0000000..a9f76e5 --- /dev/null +++ b/i3-split-push @@ -0,0 +1,87 @@ +#!/usr/bin/python3 + +# There are only 2 cases where I want single window split containers. +# +# * just before creating a new window in it. +# +# * When I want to make 1 window a split container and bring an existing +# window into it. In vanilla i3, this is super awkward. Usually, you are +# starting out focused on the window you want to move into the +# container. So, you focus the window which is to become a container, +# split it, focus the window you want to join the container, move it +# into that container. 4 actions, totally annoying. Lets simplify this +# to 2 actions, a key to say what split we want, then a key to say which +# direction to move the current window. Since we have a hook that erases +# all single window split containers on focus change, we can consider a +# single window split container to indicate the split we want. + +import sys +from i3ipc import Connection, Event +# for debugging +from pprint import pprint +import os + + +def find_parent(i3, window_id): + """ + Find the parent of a given window id + """ + + def finder(con, parent, gp): + if con.id == window_id: + return (parent, gp) + for node in con.nodes: + res = finder(node, con, parent) + if res: + return res + return None + + return finder(i3.get_tree(), None, None) + + +def set_layout(i3): + """ + Set the layout/split for the currently + focused window to either vertical or + horizontal, depending on its width/height + """ + + direction = sys.argv[1] + + win = i3.get_tree().find_focused() + parent, gp = find_parent(i3, win.id) + layout = parent.layout + + if (parent and gp and len(parent.nodes) == 1): + i3.command('focus ' + direction) + + exists = False + if os.path.exists('/tmp/iank-i3-no-auto'): + exists = True + else: + open('/tmp/iank-i3-no-auto', 'a') + + if (layout == 'splith'): + i3.command('split horizontal') + elif (layout == 'splitv'): + i3.command('split vertical') + elif (layout == 'tabbed'): + i3.command('split vertical') + i3.command('layout tabbed') + + i3.command('[con_id=%s] focus' % win.id) + i3.command('move ' + direction) + if (not exists): + os.remove('/tmp/iank-i3-no-auto') + else: + i3.command('move ' + direction) + + + +def main(): + i3 = Connection() + set_layout(i3) + + +if __name__ == "__main__": + main() diff --git a/i3-sway/common.conf b/i3-sway/common.conf index 2cc2d1c..fc3236b 100644 --- a/i3-sway/common.conf +++ b/i3-sway/common.conf @@ -19,20 +19,20 @@ set $mod Mod4 # for non-gui apps, use this. set $ex exec --no-startup-id -bindsym $mod+2 $ex "/b/ds/i3-split-maybe"; exec "pavucontrol" +bindsym $mod+2 $ex "i3-split-maybe"; exec "pavucontrol" # calling without -no-remote makes this to be the instance that links # will open in from other applications. -bindsym $mod+3 $ex "/b/ds/i3-split-maybe"; exec "abrowser" +bindsym $mod+3 $ex "i3-split-maybe"; exec "abrowser" # calling just abrowser mysteriously stopped working, # so I figured out this is how to get output, but then # it suddenly started working again. #bindsym $mod+3 exec "abrowser 2>&1 >/tmp/l" #bindsym $mod+3 exec "abrowser -no-remote -P sfw" -bindsym $mod+4 $ex "/b/ds/i3-split-maybe"; exec "abrowser -no-remote -P firefox-main-profile" +bindsym $mod+4 $ex "i3-split-maybe"; exec "abrowser -no-remote -P firefox-main-profile" # todo: figure out a stream delay & way to cut the stream. # settings, advanced, stream delay bindsym $mod+5 $ex "/a/bin/ds/stream-interlude" -bindsym $mod+6 $ex "/b/ds/i3-split-maybe"; exec "/usr/local/bin/start-tor-browser" +bindsym $mod+6 $ex "i3-split-maybe"; exec "/usr/local/bin/start-tor-browser" bindsym $mod+7 $ex "/a/bin/ds/laptop-xrandr" #bindsym $mod+6 $ex "/a/bin/redshift.sh" # bindsym $mod+equal $ex "t s w; t in" @@ -45,7 +45,7 @@ bindsym $mod+1 focus parent bindsym $mod+shift+1 focus child # undo split: https://github.com/i3/i3/issues/3808 bindsym $mod+grave floating toggle; floating toggle -bindsym $mod+equal $ex "/a/exe/i3-set-layout splith" +bindsym $mod+equal $ex "i3-set-layout splith" # move firefox to current workspace. # https://i3wm.org/docs/userguide.html#keybindings # get class with xprop, example output @@ -58,37 +58,42 @@ bindsym $mod+e $ex i3-pull emacs bindsym $mod+shift+e unmark emacs; mark emacs bindsym $mod+r $ex "/a/bin/ds/xl" -# todo, in newer i3, make this toggle split tabbed. -bindsym $mod+t $ex "/a/exe/i3-set-layout splitv" +bindsym $mod+t $ex "i3-set-layout splitv" #bindsym $mod+Shift+t move workspace to output up bindsym $mod+Shift+t move workspace to output right # todo: consider a command that moves a window, and erases any single # container window left behind. -# todo: port /b/ds/i3-maybe-double-move into python. - # todo: consider a command which alters things as if the current window # had been created into a single window split. For horizontal split, # this would be like: focus left, split vertical, focus right, move # left. With that, we could totally eliminate single window containers. -bindsym $mod+g $ex "/a/exe/i3-set-layout tabbed" +bindsym $mod+g $ex "i3-set-layout tabbed" -bindsym $mod+shift+g $ex "/b/ds/i3-auto-layout-toggle" # Use Mouse+$mod to drag floating windows to their wanted position floating_modifier $mod bindsym $mod+u focus left; $ex "i3-mouse-warp" +# i dont expect to use this much +bindsym $mod+shift+u $ex "i3-auto-layout-toggle" bindsym $mod+i focus right; $ex "i3-mouse-warp" bindsym $mod+o focus up; $ex "i3-mouse-warp" bindsym $mod+p focus down; $ex "i3-mouse-warp" -bindsym $mod+Left $ex "/a/exe/i3-maybe-double-move left" -bindsym $mod+Right $ex "i3-maybe-double-move right" -bindsym $mod+Up $ex "i3-maybe-double-move up" -bindsym $mod+Down $ex "i3-maybe-double-move down" +bindsym $mod+Left $ex "i3-split-push left" +bindsym $mod+Right $ex "i3-split-push right" +bindsym $mod+Up $ex "i3-split-push up" +bindsym $mod+Down $ex "i3-split-push down" + +# for testing in case there is a problem with above. +# these could be rebound to other things. +bindsym $mod+shift+Left move left +bindsym $mod+shift+Right move right +bindsym $mod+shift+Up move up +bindsym $mod+shift+Down move down bindsym $mod+Shift+a move container to workspace 4 bindsym $mod+a workspace 4 @@ -114,7 +119,11 @@ bindsym $mod+v split vertical bindsym $mod+Shift+v split horizontal # ## temp for testing, add antying here -##bindsym $mod+shift+g +#bindsym $mod+shift+5 + + + + bindsym $mod+b $ex i3-pull term bindsym $mod+shift+b unmark term; mark term # for use to cleanup extra emacs windows @@ -161,9 +170,9 @@ bindcode $mod+shift+65 focus mode_toggle floating_modifier $mod bindsym $mod+shift+h $ex /b/ds/stream-clip hc -bindsym $mod+j $ex "/b/ds/i3-split-maybe"; exec emacsclient -c +bindsym $mod+j $ex "i3-split-maybe"; exec emacsclient -c bindsym $mod+shift+j $ex /b/ds/stream-clip up -bindsym $mod+k $ex "/b/ds/i3-split-maybe"; exec konsole +bindsym $mod+k $ex "i3-split-maybe"; exec konsole bindsym $mod+shift+k $ex /b/ds/stream-clip intro bindsym $mod+l $ex dmenu_run bindsym $mod+shift+l $ex /b/ds/stream-clip steady diff --git a/i3-sway/i3.conf b/i3-sway/i3.conf index 737acc2..bf1aba8 100644 --- a/i3-sway/i3.conf +++ b/i3-sway/i3.conf @@ -1,6 +1,7 @@ # exit i3 (logs you out of your X session) bindsym $mod+Shift+o exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" +bindsym $mod+Shift+i reload bindsym $mod+Shift+p restart @@ -8,4 +9,4 @@ $ex copyq $ex dunst $ex /usr/lib/x86_64-linux-gnu/libexec/kdeconnectd # this dies when we restart i3. -exec_always --no-startup-id alternating_layouts.py +exec_always --no-startup-id i3-event-hook diff --git a/script-files b/script-files index 2bc52a0..f4f4b93 100644 --- a/script-files +++ b/script-files @@ -46,7 +46,13 @@ my_bin_files=( prof-notify /a/bin/newns/newns /a/bin/fai/fai/config/distro-install-common/ethusb-static - /a/opt/i3-alternating-layout/alternating_layouts.py + i3-auto-layout-toggle + i3-event-hook + i3-mouse-warp + i3-pull + i3-set-layout + i3-split-maybe + i3-split-push ) for f in /b/log-quiet/*; do -- 2.30.2 From 7ed3b98c4d3678d982c33741f1f42727144e66ce Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Wed, 5 Jun 2024 17:44:50 -0400 Subject: [PATCH 6/6] i think this fixes i3 issues --- brc2 | 2 +- ffp | 21 +++++++++ i3-auto-layout-toggle | 4 ++ i3-event-hook | 103 +++++++++++++++++++++++++++++++++++------- i3-set-layout | 4 +- i3-split-maybe | 24 +++++++++- i3-split-push | 29 ++++++++++-- i3-sway/common.conf | 33 ++++++-------- 8 files changed, 178 insertions(+), 42 deletions(-) diff --git a/brc2 b/brc2 index 7699add..8406dab 100644 --- a/brc2 +++ b/brc2 @@ -2036,7 +2036,7 @@ apache-apply-repo() { apache-apply() { for file; do - if head -n1 "$file"| grep -E '^#!/bin/bash\b' &>/dev/null; then + if head -n1 "$file"| grep -E '^#!/' &>/dev/null; then { head -n1 "$file" apache-header diff --git a/ffp b/ffp index 3ada2fc..cf331e0 100755 --- a/ffp +++ b/ffp @@ -1,4 +1,25 @@ #!/bin/bash +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + volume=0 diff --git a/i3-auto-layout-toggle b/i3-auto-layout-toggle index e879bda..f7330a3 100755 --- a/i3-auto-layout-toggle +++ b/i3-auto-layout-toggle @@ -24,8 +24,12 @@ set -e; . /usr/local/lib/bash-bear; set +e f=/tmp/iank-i3-no-auto +# todo: if it is disabled, update the status bar. + if [[ -e $f ]]; then + echo "enabling" rm -f $f else + echo "disabling" touch $f fi diff --git a/i3-event-hook b/i3-event-hook index 01514a6..196e608 100755 --- a/i3-event-hook +++ b/i3-event-hook @@ -1,5 +1,38 @@ #!/usr/bin/env python3 +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + +# This gets rid of all single window containers. +# If a single window container was nested in another, +# it ignores that, but I don't generally expect to create them and +# we change focus enough that we would kill them off. + +# Note: I spent a lot of time figuring out how to do this properly, +# https://github.com/i3/i3/issues/3808 there are a bunch of links +# which suggest either float toggle; float toggle, which doesn't put windows back in the same place, or doing a move, which only actually works if you are moving in a direction which does not have a container there, else your window joins the container, and . + + import sys import os from i3ipc import Connection, Event @@ -11,11 +44,11 @@ def find_parent(i3, window_id): Find the parent of a given window id """ - def finder(con, parent, gp): + def finder(con, parent, workspace): if con.id == window_id: - return (parent, gp) + return (parent, workspace) for node in con.nodes: - res = finder(node, con, parent) + res = finder(node, con, con if con and con.type == 'workspace' else workspace) if res: return res return None @@ -23,6 +56,33 @@ def find_parent(i3, window_id): return finder(i3.get_tree(), None, None) +def kill_single_win_containers(i3, e, node, parent): + if len(parent.nodes) == 1 and len(node.nodes) == 0: + print("d1: killing parent") + # parent is a single window container, kill it. + + # Note: based on testing, + # i3 takes care of not calling this program for + # events which we create within it. Otherwise, + # we could create our disabling file here + # and delete it later if it wasn't already there. + i3.command('[con_id=%s] focus' % node.id) + i3.command('mark i3ha') + i3.command('focus parent') + i3.command('focus parent') + i3.command('mark i3hb') + i3.command('[con_mark="i3ha"] focus') + i3.command('move window to mark i3hb') + i3.command('unmark i3ha') + i3.command('unmark i3hb') + # back to our original focus + i3.command('[con_id=%s] focus' % e.container.id) + elif len(node.nodes) >= 1: + for child in node.nodes: + kill_single_win_containers(i3, e, child, node) + + + def focus_hook(i3, e): """ Set the layout/split for the currently @@ -33,27 +93,36 @@ def focus_hook(i3, e): if os.path.isfile("/tmp/iank-i3-no-auto"): return + # I identify container vs a real windows by the fact that it has nodes. + # looking through the data, another notable difference is that it has + # 'window': None, + # 'window_type': None, + + parent, workspace = find_parent(i3, e.container.id) # debugging - #pprint(vars(e)) + #exit(0) - parent, gp = find_parent(i3, e.container.id) + if not workspace: + return + #pprint(vars(workspace)) + #print() + for pnode in workspace.nodes: + # debugging + # if (len(pnode.nodes) >= 1): + # print("pnodes: ", pnode.nodes) - # This gets rid of tabbed container with single windows. - #if (parent and gp and parent.layout == 'tabbed' and len(parent.nodes) == 1): - # This gets rid of all single window containers. - if (parent and gp and len(parent.nodes) == 1): - i3.command('mark i3ha') - i3.command('focus parent') - i3.command('focus parent') - i3.command('mark i3hb') - i3.command('[con_mark="i3ha"] focus') - i3.command('move window to mark i3hb') - i3.command('unmark i3ha') - i3.command('unmark i3hb') + for node in pnode.nodes: + kill_single_win_containers(i3, e, node, pnode) def main(): i3 = Connection() i3.on(Event.WINDOW_FOCUS, focus_hook) + # if we don't have move, and we move a window out of a container, + # leaving behind a single window container, then we move it back, it + # will go into the container. We could expect that if we do it + # quickly, but it would be unexpected after a few seconds and we + # forget that it was a container. + i3.on(Event.WINDOW_MOVE, focus_hook) i3.main() diff --git a/i3-set-layout b/i3-set-layout index fc68666..6e28c71 100755 --- a/i3-set-layout +++ b/i3-set-layout @@ -29,6 +29,8 @@ def set_layout(i3): """ win = i3.get_tree().find_focused() + # i don't use gp: todo: revert to original alternating_layout function + # which did not return gp. parent, gp = find_parent(i3, win.id) @@ -46,7 +48,7 @@ def set_layout(i3): # instead of what we want. And in fact, the whole logic below is # incorrect and based on testing which did not realize that fact. # - if (parent and gp and len(parent.nodes) == 1): + if (parent and parent.type == 'con' and len(parent.nodes) == 1): # https://unix.stackexchange.com/questions/173754/how-to-move-a-window-up-to-the-level-of-its-parent-window-in-i3wm i3.command('mark i3ha') i3.command('focus parent') diff --git a/i3-split-maybe b/i3-split-maybe index d8bd54c..0a98c90 100755 --- a/i3-split-maybe +++ b/i3-split-maybe @@ -1,5 +1,27 @@ #!/usr/bin/python3 +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + # This anticipates when we want to tab windows. There are 2 options of # when to do it: just after a window is created, or just before a window # is created. @@ -24,7 +46,7 @@ # so I close them out in # # I have a keybind which disables both, it runs /b/ds/i3-auto-layout-toggle - +# import sys import os diff --git a/i3-split-push b/i3-split-push index a9f76e5..f78ca9a 100755 --- a/i3-split-push +++ b/i3-split-push @@ -1,3 +1,24 @@ +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + #!/usr/bin/python3 # There are only 2 cases where I want single window split containers. @@ -14,7 +35,8 @@ # direction to move the current window. Since we have a hook that erases # all single window split containers on focus change, we can consider a # single window split container to indicate the split we want. - +# +# import sys from i3ipc import Connection, Event # for debugging @@ -53,13 +75,13 @@ def set_layout(i3): layout = parent.layout if (parent and gp and len(parent.nodes) == 1): - i3.command('focus ' + direction) - exists = False if os.path.exists('/tmp/iank-i3-no-auto'): exists = True else: open('/tmp/iank-i3-no-auto', 'a') + i3.command('focus ' + direction) + if (layout == 'splith'): i3.command('split horizontal') @@ -77,7 +99,6 @@ def set_layout(i3): i3.command('move ' + direction) - def main(): i3 = Connection() set_layout(i3) diff --git a/i3-sway/common.conf b/i3-sway/common.conf index fc3236b..738cd0d 100644 --- a/i3-sway/common.conf +++ b/i3-sway/common.conf @@ -9,6 +9,7 @@ # todo: think whether this is useful: https://github.com/tmfink/i3-wk-switch # todo: see comment by Jakstern551 here for tip about jumping to windows +# https://old.reddit.com/r/i3wm/comments/k8m4k4/share_your_i3_tips_and_tricks_that_you_have/ # https://i3wm.org/docs/userguide.html#keybindings #To get the current mapping of your keys, use xmodmap -pke. To @@ -43,8 +44,12 @@ bindsym $mod+7 $ex "/a/bin/ds/laptop-xrandr" bindsym $mod+1 focus parent bindsym $mod+shift+1 focus child -# undo split: https://github.com/i3/i3/issues/3808 -bindsym $mod+grave floating toggle; floating toggle + +# note, i used to have a key: "floating toggle; floating toggle" to +# as undo split, as suggested here https://github.com/i3/i3/issues/3808 +# but something +# +bindsym $mod+grave floating toggle bindsym $mod+equal $ex "i3-set-layout splith" # move firefox to current workspace. # https://i3wm.org/docs/userguide.html#keybindings @@ -62,14 +67,6 @@ bindsym $mod+t $ex "i3-set-layout splitv" #bindsym $mod+Shift+t move workspace to output up bindsym $mod+Shift+t move workspace to output right -# todo: consider a command that moves a window, and erases any single -# container window left behind. - -# todo: consider a command which alters things as if the current window -# had been created into a single window split. For horizontal split, -# this would be like: focus left, split vertical, focus right, move -# left. With that, we could totally eliminate single window containers. - bindsym $mod+g $ex "i3-set-layout tabbed" @@ -114,7 +111,7 @@ bindsym $mod+Shift+x move container to workspace 6 bindsym $mod+x workspace 6 -# todo, in newer i3, make this split toggle +# todo, in newer i3, consider split toggle bindsym $mod+v split vertical bindsym $mod+Shift+v split horizontal # @@ -166,8 +163,6 @@ bindcode $mod+65 $ex obs-auto-scene-switch-toggle; floating toggle; sticky enabl # change focus between tiling / floating windows bindcode $mod+shift+65 focus mode_toggle -# Use Mouse+$mod to drag floating windows to their wanted position -floating_modifier $mod bindsym $mod+shift+h $ex /b/ds/stream-clip hc bindsym $mod+j $ex "i3-split-maybe"; exec emacsclient -c @@ -199,9 +194,11 @@ bindsym $mod+End $ex "/b/ds/toggle-mute" # title bars but no borders. i tried this out a bit #default_border normal 0 -default_border pixel 4 + +# default border is like 2 pixels +default_border pixel # for debugging -default_border normal 10 +#default_border normal 10 # I dont see a way to make processing windows act like normal windows, # this does it. @@ -214,6 +211,6 @@ default_border normal 10 # this is the processing window for my app named focus. for_window [class="focus" instance="focus"] floating disable -# client.focused #4c7899 #285577 #ffffff #2e9ef4 #ff4400 -# client.focused_inactive #333333 #5f676a #ffffff #484e50 #DBEEF4 -# client.unfocused #333333 #222222 #888888 #292d2e #B8C8CD +client.focused #4c7899 #285577 #ffffff #2e9ef4 #ff4400 +client.focused_inactive #333333 #5f676a #ffffff #484e50 #DBEEF4 +client.unfocused #333333 #222222 #888888 #292d2e #B8C8CD -- 2.30.2