- 1. Requirements
- 2. Notes
- 3. Installation
- 4. Contents
- 4.1.
bash-pure
- runs Bash in a clean environment - 4.2.
with-env-pure
- runs a command underbash-pure
- 4.3.
bash-user
- runs Bash with the current user’s environment - 4.4.
zsh-user
- runs Zsh with the current user’s environment - 4.5.
with-env-user
- runs a command underbash-user
- 4.6.
with-env-user-sh
- runs a shell command underbash-user
- 4.7.
conda/
- utils for Python Conda - 4.8.
guix/
- utils for the Guix package manager - 4.9.
chruby/
- utils forchruby
- 4.10.
sh
- utils for shell scripting - 4.11.
git/
- utils for Git version control - 4.12.
graalvm/
- utils for GraalVM - 4.13.
inferno/
- utils for Inferno OS - 4.14.
with-pause
- runs a command, then pause and wait for user to input Enter to exit - 4.15.
9
- sets up the environment for Plan9port applications - 4.16.
with-retry <timeout> <times> <cmd>…
- runs a command, retrying on error - 4.17.
9-term
- runs Plan9port terminal emulator within a 9 environment - 4.18.
9-rc
- runs RC shell with Plan 9 Port environment - 4.19.
9-acme
- runs Plan9port Acme with9
- 4.20. Terminal helpers
- 4.20.1.
term-emu
- wrapper for terminal emulator - 4.20.2.
with-term-emu-sh
- runs a shell command in a terminal emulator - 4.20.3.
with-termux
- runs a command with a terminal multiplexer - 4.20.4.
with-mux-session
- runs a command in a term multiplexer session - 4.20.5.
bring-termux-session
- brings a terminal emulator session here
- 4.20.1.
- 4.21.
newline
- 4.22.
gnu-sed
- specifically calls the GNU implementation of sed - 4.23.
gnu-tar
- specifically calls the GNU implementation of Tar - 4.24.
gnu-tr
- attempts to call the GNU implementation of tr - 4.25.
upcase
- upcases a string (read from stdin) - 4.26.
downcase
- downcases a string (read from stdin) - 4.27.
view-man
- views a Man page in a GUI pager (Rmacs) - 4.28.
rmacs-pager
- uses Rmacs GUI as a poor man’s pager - 4.29.
cat-which <executable>
-cat $(which executable-1 [executable-2] […])
- 4.30.
check-broken-symlinks [path1] [path2] […]
- 4.31.
symlink
- symlinks, prompting for overwriting if destination exists - 4.32.
list-broken-symlinks
- lists broken symlinks in a directory - 4.33.
check-xinput
- checks if a device appears inxinput list
- 4.34.
wait-for-xinput
- waits until an xinput device is available - 4.35.
chmod-default [dir]
- fixes permissions - 4.36.
add-line-comment
- comments code, read from stdin - 4.37. Input device configuration
- 4.37.1.
config-keymap-altgr
- keyboard layout: Programmer Dvorak + Right Alt as AltGr + Ctrl-Alt swapped + CapsLock-Escape swapped - 4.37.2.
config-inputs-cmpitg
- config keyboards, mice, … all input devices - 4.37.3.
config-keymap-steam
- keyboard layout: Programmer Dvorak without AltGr - 4.37.4. Config mice & extra peripherals
- 4.37.1.
- 4.38.
extract-audio
- 4.39.
convert-to-mp3
- converts files to MP3 - 4.40.
convert-to-ogg-vorbis
- converts files to Ogg Vorbis - 4.41.
count-monitors
- 4.42.
get-cpu-usage
- 4.43.
create-ctags
- 4.44.
do-notify
- sends a desktop notification - 4.45.
do-notify-short
- sends a short desktop notification - 4.46.
drop-lines
- 4.47.
in_epoch2datetime
- converts the epoch from read from stdin to local date time - 4.48.
en2fi
- translates from English to Finnish - 4.49.
fi2en
- translates from Finnish to English - 4.50.
format-text
- formats text from stdin using Emacs’sfill-paragraph
- 4.51.
replace-extension
- replaces file extension - 4.52.
gitserve
- runs a Git server - 4.53.
get-all-randr-outputs
- gets all RandR outputs - 4.54.
get-all-execs
- gets all executables, optionally prefixed with a string and history support - 4.55.
get-sensors-data
- gets meaningful sensors data - 4.56.
dedup-PATH
- gets a clean PATH variable (dedup’ed) - 4.57.
dedup-lines
- dedups lines - 4.58.
html2text
- 4.59.
i3-exec-command
- executes an i3 command - 4.60.
i3-move-to-workspace
- moves a window to a workspace with i3 - 4.61.
i3-rename-workspace
- renames current workspace in i3 - 4.62.
switch-window
- window switcher - 4.63.
set-only-monitors
- sets and configs only certain monitors, all others are off - 4.64.
set-monitors-auto
- configs all monitors with default settings from XRandR - 4.65.
set-1-monitor
- 4.66. Running applications
- 4.66.1. Volume control
- 4.66.2. Disk daemon
- 4.66.3. Clipboard manager
- 4.66.4. Keybind Daemon
- 4.66.5. Power manager
- 4.66.6. Volume daemon
- 4.66.7. Input method daemon with Ibus
- 4.66.8. Screenshot manager
- 4.66.9. Screensaver daemon
- 4.66.10. Network manager
- 4.66.11. GNOME settings daemon
- 4.66.12. Insync
- 4.66.13. Menu program
- 4.66.14. Context-menu program
- 4.66.15. Application launcher
- 4.66.16. Google Chrome
- 4.66.17. Whatsapp
- 4.66.18. Desktop calendar
- 4.67. Desktop utilities
- 4.67.1.
run-tdrop
- helper to runtdrop
with default options - 4.67.2.
emacsclient-commander-for-dropdown
- helper to display a commander window with Emacsclient - 4.67.3.
toggle-dropdown-term-emu
- toggles a dropdown terminal emulator - 4.67.4.
toggle-dropdown-commander
- toggles a dropdown commander window
- 4.67.1.
- 4.68.
disable-x-bell
- 4.69.
dispatch-action
- dispatches an action based on a string - 4.70.
run-menu-and-dispatch
- runs a menu program, allowing user to choose an item/input custom string, then dispatch an action based on the output - 4.71.
call-omni-switcher-stdin
- display a universal switcher - 4.72.
call-wm-menu
- calls the main window manager menu, current window manager is detected automatically - 4.73.
add-to-history
- 4.74.
normalize-filename
- 4.75.
all-dev-debs
- lists all Debian-dev
packages installed - 4.76.
add-deb-repo
- adds a Debian-based repository - 4.77.
local-tcp-open-p
- checks if a local TCP port is opened - 4.78.
lockscreen
- 4.79.
monitor-off
- 4.80.
now-standardized
- 4.81.
now-to-clipboard
- 4.82.
symlink-p
- 4.83.
filter-broken-symlinks [path1] [path2] […]
- 4.84.
find-deep-path
- returns the full path for a file/directory that is found fromPATH
- 4.85.
prompt-y-n
- prompts for a yes/no answer - 4.86.
executable-exists <exec-file>
- checks if an executable exists inPATH
- 4.87.
report-missing-executables <bin-1> <software-1> <bin-2> <software-2> …
- 4.88.
prefix
- prefixes all lines read from stdin with a string - 4.89.
suffix
- suffixes all lines read from stdin with a string - 4.90.
query-password
- queries password from a password manager - 4.91.
qrcode
- creates QR code from a string - 4.92.
system-temperature
- 4.93.
running-p
- determines if a process is running - 4.94.
using-x-p
- determines if we’re using an X server - 4.95.
show-keyboard
- shows keyboard of modifiers, convenient when making screencast - 4.96.
with-sudo
- runssudo
with some environment variables preserved - 4.97.
with-sudo-wallet
- runswith-sudo
, uses a password manager to manage the Sudo password - 4.98.
with-workdir <dir> <command> [args…]
- runs a command in a directory - 4.99.
get-all-x-displays
- gets all X displays - 4.100.
get-all-logged-in-x-displays
- gets all X displays of logged in users - 4.101.
with-all-x-displays
- runs a command with all X displays - 4.102.
with-all-logged-in-x-displays
- runs a command with all X displays - 4.103.
prompt-y-n-gui-all-displays
- prompts a yes/no answer in all current displays - 4.104.
prompt-y-n-gui-all-logged-in-displays
- prompts a yes/no answer in all current displays for logged in users - 4.105.
event/
- event-based triggers - 4.106.
sudo-askpass
- runssudo
with a graphical askpass program, also preserving some environment variables - 4.107.
suspend-me
- suspends computer - 4.108.
take-from
- takes all lines from stdin, starting from a pattern - 4.109.
take-lines
- takes the firstn
lines - 4.110.
wget-site
- 4.111.
wm/
- utils for window manager (WM)- 4.111.1.
format-time-for-panel
- formats date & time to display in a desktop panel - 4.111.2.
get-wm-name
- retrieves the current WM name - 4.111.3.
pprint-client-list
- pretty-prints the list of clients with their title & IDs - 4.111.4.
pprint-desktop-list
- pretty-prints the list of desktops - 4.111.5.
get-desktop-name
- gets the current desktop name - 4.111.6.
copy-client-description
- copies description for a client - 4.111.7.
copy-client-id
- copies a client ID - 4.111.8.
call-omni-switcher
- calls the omni switcher with WM patterns - 4.111.9.
call-desktop-list
- calls desktop list menu that allows interaction with current desktops - 4.111.10.
get-menu <menu-name>
- 4.111.11.
menus/
- common menus across all WMs - 4.111.12.
herbstluft/
- utils to manage HerbstluftWM
- 4.111.1.
- 4.112.
start-xephyr
- starts a Xephyr server (for debugging window manager) - 4.113.
openfile-dialog
- creates a open-file dialog and prints the selected path to stdout - 4.114.
edit-which
-${EDITOR} $(which <executable>)
- 4.115.
compute-checksum-url
- computes checksum for remote file - 4.116.
safe-download
- downloads file, prompts overwriting, and compares checksums - 4.117.
gen-string
- generates a random alphanumeric string - 4.118.
gen-names
- generates random English names - 4.119.
gen-filename
- generates a random filename - 4.120.
daemontools
-compatible utils
- 4.1.
- 5. High-level "fingertip" executables
- 5.1.
get-exec
- gets the full path of an executable fromPATH
- 5.2.
get-from-paths
- gets the full path of a file/directory fromPATH
- 5.3.
deep-exec
- execs a command fromPATH
, the command might be a path instead of just a file name - 5.4.
exec-stdin
- execs from stdin - 5.5.
exec-and-echo-stdin
- execs and echoes stdin - 5.6.
enrich-path
- enrichesPATH
, then executes a command - 5.7.
fastls
- a bit fasterls
- 5.1.
- 6. Legacy/Unused executables
- 7. License
Script collection. Many of them are written in Rc shell, for Bourne-or-Bash-compatible shell suffers from many design flaws, making it difficult to maintain.
This document is written in literate programming style. To generate scripts and documentation, you need latest stable version of Ulquikit. You could also clone the repo and start using it yourself. All executables are in bin/
.
TODO: Script to display a persistent notification for current desktop
TODO: Explain how commands are built the way they are built below: * When to pass as argumment? When to pass into stdin? * When to output as text? Human-readable? * What about exit code?
TODO: Help text for all commands
TODO: fzf integration; use cases: convert-to-* + fzf
TODO: GNU parallel integration
TODO: Write about shell design flaws
For everything to function correctly, you need:
-
Plan 9 from User Space (Plan9port)
-
GNU Emacs 26+
-
Python 3
Some scripts depend on others. It’s best to fulfill the requirements for all of them.
My environment is unusual:
-
${HOME}/Data
is either a symlink or a mount point, pointing to all configuration and data belonging to the tools I use.If you have a separated
${HOME}
, you just need to create theData
directory.The main reasons why I don’t use separated partition for
${HOME}
is because: 1)${HOME}
itself is extremely inconsistent and cluttered (.config
,.local
, dot files, capticalized names vs. lower-case names, etc.); and 2) I use serveral distros, where each piece of software is slightly different in versions, thus different in configuration.In my main system,
/home/cmpitg/Data
is a symlink to/mnt/home/cmpitg
, whereas/mnt/home
is a mount point. -
${HOME}/Data/Mount-Points
contains a collection of shortcuts to various directories, and/m
is its symlink into/
.I like to separate the original directories from their shortcuts and to make things globally visible. Some might argue that this is a serious security flaw. I disagree. Private things should be kept away. Your
.ssh
should never stay in/m
. -
/m/${USER}
is a symlink back to${HOME}/Data
, so all symlinks in/m
can utilize/m/${USER}
itself.Symlinks are very useful if used appropriately (examples include the Nix package manager). For me, using
/m/src
is much more effective and unified than~/src
for~
depends on what your current user is. I could also re-symlink/m/src
whenever I with minimal effects on other parts of the system.✗ l /m lrwxrwxrwx 1 root root 30 Nov 28 20:24 /m -> /home/cmpitg/Data/Mount-Points/ ✗ l /m/ total 12K drwxr-xr-x 4 cmpitg cmpitg 4.0K Nov 28 22:04 ./ drwxr-xr-x 29 cmpitg cmpitg 4.0K Dec 1 23:44 ../ dr-xr-xr-x 1 cmpitg cmpitg 0 Jan 1 1970 9p-fonts/ drwxr-xr-x 2 cmpitg cmpitg 4.0K Nov 23 22:23 acme/ lrwxrwxrwx 1 cmpitg cmpitg 13 Nov 28 22:01 bin -> /m/cmpitg/Bin/ lrwxrwxrwx 1 cmpitg cmpitg 17 Nov 28 20:22 cmpitg -> /home/cmpitg/Data/ lrwxrwxrwx 1 cmpitg cmpitg 16 Feb 15 2015 config -> /m/cmpitg/Config/ lrwxrwxrwx 1 cmpitg cmpitg 13 Nov 28 22:02 opt -> /m/cmpitg/Opt/ lrwxrwxrwx 1 cmpitg cmpitg 17 Aug 3 2014 scratch -> /m/cmpitg/Scratch/ lrwxrwxrwx 1 cmpitg cmpitg 18 Nov 28 22:04 src -> /m/cmpitg/Src/ lrwxrwxrwx 1 cmpitg cmpitg 15 Feb 15 2015 talks -> /m/cmpitg/Talks/ lrwxrwxrwx 1 cmpitg cmpitg 17 Aug 3 2014 toolbox -> /m/cmpitg/Toolbox/ lrwxrwxrwx 1 cmpitg cmpitg 22 Aug 3 2014 virtenvs -> /m/cmpitg/Virtual-Envs/ lrwxrwxrwx 1 cmpitg cmpitg 18 Nov 28 20:35 www -> /m/cmpitg/WWW/
Most directories should speak for themselves. Exceptions include:
-
/m/9p-fonts
: mounted by Plan9port’sfontsrv
to serve fonts, and -
/m/acme
: file system interface of Acme.
-
For installation of Plan9port, please refer to its original documentation. Below is one example session for Debian-based distros:
sudo apt install -y build-essential libfreetype6-dev libx11-dev libx11-xcb-dev git libxt-dev xorg-dev xserver-xorg-dev
cd /path/to/src/
git clone https://github.com/9fans/plan9port.git
cd plan9port
./INSTALL
# No need to add plan9port/bin to PATH as the `9` script below is used to
# invoke Plan 9 applications
For Emacs, Python, and Noto Font, please consult your distro’s documentation. Example with Debian:
sudo apt install python3 fonts-noto emacs25
#!/usr/bin/env tclsh
package require Tclx
proc removeElementsWithPattern {xs pattern} {
set res {}
foreach x $xs {
if {![string match $pattern $x]} {
lappend res $x
}
}
return $res
}
foreach {var val} [array get env] {
set newVals [removeElementsWithPattern [split $val :] *guix*]
set ::env($var) [join $newVals :]
}
execl bash [list --login --noprofile --norc {*}$::argv]
#!/usr/bin/env bash-pure
exec "$@"
#!/usr/bin/env bash-pure
# All hell break loose without the following!!!
# Why is the varible set to empty???
unset GDK_PIXBUF_MODULE_FILE
if [ -f ~/.env ]; then
. ~/.env
fi
if [ -f ~/.env-prog ]; then
. ~/.env-prog
fi
exec bash "$@"
#!/usr/bin/env bash-user
exec zsh "$@"
TODO: Doc: For a single command, e.g. shell operators don’t work
#!/usr/bin/env bash-user
exec "$@"
TODO: Doc: shell operators work
#!/usr/bin/env bash-user
exec "${SHELL}" -c "$*"
4.7. conda/
- utils for Python Conda
4.8. guix/
- utils for the Guix package manager
#!/usr/bin/env bash-user
set +x
# Do not re-read user env
export _READ_USER_ENV_=0
export GUIX_LOCPATH="${HOME}/.guix-profile/lib/locale"
export GUIX_LD_WRAPPER_ALLOW_IMPURITIES=n
export PATH="${HOME}/.config/guix/current/bin:${PATH}"
export INFOPATH="${HOME}/.config/guix/current/share/info:${INFOPATH}"
export GUIX_PROFILE="${HOME}/.guix-profile"
[[ -e "${HOME}/.config/guix/current/etc/profile" ]] && . "${HOME}/.config/guix/current/etc/profile"
[[ -e "${HOME}/.guix-profile/etc/profile" ]] && . "${HOME}/.guix-profile/etc/profile"
XDG_DATA_DIRS="${XDG_DATA_DIRS:-${HOME}/.local/share}"
XDG_DATA_DIRS="${XDG_DATA_DIRS}:/usr/share:/usr/local/share:${HOME}/.local/share"
export XDG_DATA_DIRS
unset _READ_USER_ENV_
exec "$@"
4.9. chruby/
- utils for chruby
#!/usr/bin/env bash
if [[ -z "${RUBY_CHRUBY_VERSION}" ]]; then
echo "RUBY_CHRUBY_VERSION not defined, aborting..." >&2
exit 3
fi
# Only run with Bash or Zsh
if [[ -n "${BASH}" || -n "${ZSH_NAME}" ]]; then
if [[ -f /usr/local/share/chruby/chruby.sh && -f /usr/local/share/chruby/auto.sh ]]; then
. /usr/local/share/chruby/chruby.sh
. /usr/local/share/chruby/auto.sh
chruby ${RUBY_CHRUBY_VERSION}
fi
fi
exec "$@"
#!/usr/bin/env sh
git ls-files --deleted | xargs git rm --cached
TODO: * Help text * Error if missing arguments
#!/usr/bin/env sh
base_branch_="${1}"
head_branch_="${2}"
git merge-tree $(git merge-base "${base_branch_}" "${head_branch_}") "${base_branch_}" "${head_branch_}"
4.12. graalvm/
- utils for GraalVM
4.13. inferno/
- utils for Inferno OS
#!/usr/bin/env bash
set -o nounset
# export EMU='-g800x600 -c1'
export EMU='-g800x600'
export PATH="${INFERNO_OS_ROOT}/Linux/386/bin:${PATH}"
exec "$@"
#!/usr/bin/env sh
set -o nounset
export user=cmpitg
exec emu /dis/wm/wm.dis wm/logon -n "/usr/${user}/namespace" -u "${user}" "$@"
#!/usr/bin/env bash
"$@"
read -p "Press Enter to exit..."
-
Starts and mounts 9p font server to
/m/9p-fonts
-
Creates temporary directory:
/tmp/9-${USER}
-
And executes a command in a Plan9port environment in
${PLAN9}/bin
. IfPLAN9
variable is not set, it is set to/m/opt/plan9port
by default.
TODO: Customize plumber dir TODO: Proper docs
#!/usr/bin/env bash
##
## Sets up the environment for Plan9port applications:
## * Starts plumber and font server
## * Runs the corresponding program
##
export TEMP9="${TEMP9:-/tmp/9-${USER}}"
export PLAN9="${PLAN9:-/m/opt/plan9port}"
export PATH="${PLAN9}/bin:${PATH}"
export PLAN9_FONTDIR="${PLAN9_FONTDIR:-/m/9p-fonts}"
export SHELL=rc
export TERM=9term
# export font="${PLAN9_FONTDIR}/GoMono/11a/font"
mkdir -p "${TEMP9}"
mkdir -p "${PLAN9_FONTDIR}"
plumber 2>/dev/null
nohup fontsrv -m "${PLAN9_FONTDIR}" >"${TEMP9}/fontsrv.out" 2>"${TEMP9}/fontsrv.err" &
exec ${PLAN9}/bin/9 "$@"
TODO: Help text
#!/usr/bin/env tclsh
set timeout [lindex $::argv 0]
set times [lindex $::argv 1]
set cmd [lrange $::argv 2 end]
proc execCmd {cmd} {
return [exec {*}$cmd <@ stdin >@ stdout 2>@ stderr]
}
if {[catch {execCmd $cmd}]} {
while {$times > 0} {
puts stderr [format "Command failed, retrying (times=%s) after %sms" $times $timeout...]
after $timeout
if {[catch {execCmd $cmd}]} {
incr times -1
} else {
exit 0
}
}
exit 1
} else {
exit 0
}
#!/usr/bin/env sh
#
# Starts 9term within an Rc environment.
#
exec 9 9term $*
Font can be chosen by setting the font
environment variable.
#!/usr/bin/env 9-rc
#
# Starts Acme with font specified by variable `font'. By default, use Go Mono.
#
if (~ $font '') {
font='/m/9p-fonts/CascadiaCode-Regular/11a/font'
}
mkdir -p /m/acme
acme -a -m /m/acme -f $font $*
If X is not running, just run the command with the default Bourne-compatible shell.
TODO: Run always ${SHELL} -c
#!/usr/bin/env tclsh
package require Tclx
# Terminator has some memory leaks, throws GTK error messages to the console
# GNOME terminal doesn't handle mouse scrolling well
# XFCE4 terminal crashes randomly (under load?)
# konsole -e <cmd>
# xfce4-terminal -x <cmd>
# deep-exec guix/with-env kitty <cmd>
if {[info exists ::env(MY_TERM_EMU)]} {
set ::TERM_EMU $::env(MY_TERM_EMU)
} else {
set ::TERM_EMU x-terminal-emulator
}
if {[catch {exec using-x-p <@ stdin >@ stdout 2>@ stderr}]} {
set ::args {}
foreach x $::argv {
lappend ::args "\"$x\""
}
execl $::env(SHELL) [list -c [join $::args " "]]
} else {
execl $::TERM_EMU [list -e {*}$::argv]
}
TODOs
-
Running without X?
-
Documentation
-
Doc:
- <args..>
→ Arguments will be merged into one, executed in a shell (shell ops like && works)
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables tmux Tmux <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
package require Tclx
if {[lsearch $::argv "-"] == -1} {
set ::args [list "-" {*}$::argv]
} else {
set ::args $::argv
}
set ::withoutTermux 0
set ::detachTermux 0
set ::pauseAfterExec 0
set ::verbose 0
set ::shell $::env(SHELL)
set ::mainCmd [lrange $::argv [expr {[lsearch $::argv "-"] + 1}] end]
foreach arg $::args {
if {$arg eq "-"} {
break
}
switch $arg {
--verbose {
set ::verbose 1
}
--without-termux {
set ::withoutTermux 1
}
--detach-termux {
set ::detachTermux 1
}
--pause-after-exec {
set ::pauseAfterExec 1
}
default {
error "Error: Unknown argument $arg"
}
}
}
if {$::withoutTermux && $::detachTermux} {
error "--without-termux and --detach-termux cannot go together"
}
if {$::verbose} {
puts "Without termux: $::withoutTermux"
puts "Detach termux: $::detachTermux"
puts "Pause after execution: $::pauseAfterExec"
puts "Shell: $::shell"
puts "Command: $::mainCmd"
}
##############################################################################
# Main
##############################################################################
if {$::withoutTermux} {
if {$::pauseAfterExec} {
execl term-emu [list with-pause $::shell -c $::mainCmd]
} else {
execl term-emu [list $::shell -c $::mainCmd]
}
}
set ::termuxWindowName [exec echo $::mainCmd | sed -r {s/:/COLON/g ; s/\\./DOT/g ; s/\(/OPEN_P/g ; s/\)/CLOSE_P/g ; s/\{/OPEN_C/g ; s/\}/CLOSE_C/g ; s/[[:space:]]+/_/g}]
if {$::verbose} {
puts "Termux window name: $::termuxWindowName"
}
if {$::detachTermux} {
if {$::pauseAfterExec} {
execl term-emu [list with-termux -n $::termuxWindowName with-pause $::shell -c $::mainCmd \; detach]
} else {
execl term-emu [list with-termux -n $::termuxWindowName $::shell -c $::mainCmd \; detach]
}
} else {
if {$::pauseAfterExec} {
execl term-emu [list with-termux -n $::termuxWindowName with-pause $::shell -c $::mainCmd]
} else {
execl term-emu [list with-termux -n $::termuxWindowName $::mainCmd]
}
}
-
See a flash of a term emu
with-term-emu-sh --without-termux - 'pwd && pwd'
-
See a term emu, paused
with-term-emu-sh --without-termux - 'pwd && pwd ; with-pause true' with-term-emu-sh --without-termux --pause-after-exec - 'pwd && pwd'
-
See a term emu, paused, with the failed command
with-term-emu-sh --without-termux - 'aoeu && pwd ; with-pause true' with-term-emu-sh --without-termux --pause-after-exec - 'aoeu && pwd'
-
See a term emu with termux, paused
with-term-emu-sh - 'pwd && pwd ; with-pause true' with-term-emu-sh - 'aoeu && pwd ; with-pause true' with-term-emu-sh --pause-after-exec - 'aoeu && pwd'
-
See a flash of a term emu, but could find the session in termux manually
with-term-emu-sh --detach-termux - 'pwd && pwd ; with-pause true' with-term-emu-sh --detach-termux --pause-after-exec - 'pwd && pwd' with-term-emu-sh --detach-termux - 'aoeu && pwd ; with-pause true' with-term-emu-sh --detach-termux --pause-after-exec - 'aoeu && pwd'
#!/usr/bin/env sh
if using-x-p; then
exec tmux new-session "$@"
else
exec tmux new-window "$@"
fi
#!/usr/bin/env tclsh
package require Tclx
# TODO: Help text
if {[catch {exec report-missing-executables tmux Tmux <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
if {[lsearch $::argv "-"] == -1} {
puts stderr "ERROR: Invalid command, needs to have -"
exit 2
}
if {[lindex $::argv 0] eq "-"} {
execl tmux [list new-window {*}[lrange $::argv 1 end]]
} else {
set sessionName [lindex $::argv 0]
execl tmux [list new-window -t $sessionName {*}[lrange $::argv 2 end]]
}
#!/usr/bin/env bash
report-missing-executables sed "GNU sed" || exit 1
if (/bin/sed --version 2>/dev/null | head -1 | grep sed &>/dev/null); then
exec /bin/sed "$@"
elif (sed --version 2>/dev/null | head -1 | grep sed &>/dev/null); then
exec sed "$@"
else
echo You don\'t have GNU sed installed. >&2
exit 1
fi
#!/usr/bin/env bash
report-missing-executables tar "GNU tar" || exit 1
if (/bin/tar --version 2>/dev/null | head -1 | grep tar &>/dev/null); then
exec /bin/tar "$@"
elif (tar --version 2>/dev/null | head -1 | grep tar &>/dev/null); then
exec tar "$@"
else
echo You don\'t have GNU tar installed. >&2
exit 1
fi
#!/usr/bin/env bash
report-missing-executables tar "GNU tr" || exit 1
if (/bin/tr --version 2>/dev/null | head -1 | grep tr &>/dev/null); then
exec /bin/tr "$@"
elif (/usr/bin/tr --version 2>/dev/null | head -1 | grep tr &>/dev/null); then
exec /usr/bin/tr "$@"
else
echo You don\'t have GNU tr installed. >&2
exit 1
fi
#!/usr/bin/env sh
exec gnu-tr '[:lower:]' '[:upper:]'
#!/usr/bin/env sh
exec gnu-tr '[:upper:]' '[:lower:]'
#!/usr/bin/env tclsh
set page [lindex $::argv 0]
exec rmacs --new-frame eval "(let ((frame (selected-frame)))
(man \"$page\")
(delete-frame frame)
(setq-local local/delete-frame-on-close t))" <@ stdin >@ stdout 2>@ stderr
TODO: Description and potential improvement
#!/usr/bin/env tclsh
package require Tcl 8
package require fileutil 1.15
set tempPath [::fileutil::tempfile]
set tempBufferName [exec random-string]
if {$::argc == 0} {
set serverName pager
} else {
set serverName [lindex $::argv 0]
}
exec cat > $tempPath <@ stdin
exec rmacs --name $serverName --new-frame eval "(with-current-buffer (get-buffer-create \"$tempBufferName\")
(insert-file \"$tempPath\")
(delete-file \"$tempPath\" nil)
(setq-local local/delete-frame-on-close (selected-frame))
(Man-cleanup-manpage)
;; (Man-fontify-manpage)
(switch-to-buffer (current-buffer)))" >@ stdout 2>@ stderr
#!/usr/bin/env bash
#
# Finds full path executables and displays the content.
#
for exec_ in "$@"; do
if $(which "${exec_}" &>/dev/null); then
cat $(which "${exec_}")
else
echo "${exec_} not found" >&2
fi
done
#!/usr/bin/env bash
#
# Checks for broken symlinks.
#
for file_ in "$@" ; do
if [ -L "${file_}" ]; then
if readlink -q "${file_}" >/dev/null ; then
echo "Good link: ${file_}"
else
echo "${file_}: bad link" >/dev/stderr
fi
else
echo "${file_} is not a symlink"
fi
done
#!/usr/bin/env sh
if [ "$#" -eq 1 ] && [ "${1}" = "--help" ]; then
echo "Usage: ${0} <source> <destination>
Symlink <source> to <destination>. If <destination> ends with a slash (/), it indicates a directory and the symlink is put in the directory. Otherwise, prompt for overwriting <destination> if exists. In case that <source> is a symlink, it is not followed."
exit 0
fi
if [ "$#" -ne 2 ]; then
echo "${0} requires 2 arguments: <source> and <destination>" >&2
exit 2
fi
if [ -d "${2}" ] && [ ! -L "${2}" ]; then
exec ln --interactive --verbose --symbolic "${1}" "${2}"
else
exec ln --interactive --verbose --symbolic --no-target-directory "${1}" "${2}"
fi
TODO: --help
#!/usr/bin/env sh
dir_=$(readlink -f "${1:-.}")
for file_ in "${dir_}/"*; do
if [ ! -e "${file_}" ]; then
echo "${file_}"
fi
done
#!/usr/bin/env bash
set -o nounset
DISPLAY=${DISPLAY:-:0}
exec xinput list | grep "$@" >/dev/null 2>&1
TODO: Docstring
#!/usr/bin/env bash
set -o nounset
DISPLAY=${DISPLAY:-:0}
timeout_=${TIMEOUT:-0.1}
times_=${TIMES:-50}
counter_=0
while ! $(check-xinput "$@"); do
counter_=$((counter_ + 1))
if [[ "${counter_}" = "${times_}" ]]; then
exit 1
fi
sleep "${timeout_}"
done
chmod
a directory recursively, 755 for files and 644 for directories. By
default, dir
is current working directory.
#!/usr/bin/env bash
test -z "$1" && dir_="." || dir_="$1"
find "${dir_}" -type d -print0 | xargs -0 chmod 0755
find "${dir_}" -type f -print0 | xargs -0 chmod 0644
Comments code by prefixing them with line comment character string by the first argument passed in this script. By default, prefix code with `# `.
#!/usr/bin/env 9-rc
#
# Comments a piece of code.
#
if (~ $1 '') {
comment_char='#'
}
if not {
comment_char=$1
}
prefix $comment_char^' '
Notes:
-
Pressing a button → kernel generates a keycode → X receives the keycode and looks up a keysym that is mapped to that keycode
-
When using the
xmodmap
command to modify the keyboard layout, note that:-
clear
,add
, andremove
are for modifiers -
To remap modifiers, first we need to remove the old mapping, then assign them again.
-
Swapping modifiers general involves 3 steps:
-
Removing the current mapping for the modifiers
-
Swapping the keysyms - it’s generally better to not touch the keycodes (to maintain compatibility with different vendors, e.g. one keysym might produced from different keycodes from different keyboard vendors)
-
Re-adding the same mapping for the modifiers
-
-
An example to demonstrate how the key mapping and translation work:
! To map a physical key to a targeted key keysym <physical-key> = <targeted-key> ! After this key, pressing the physical key will generate keysym for the targeted key ! When mapping a modifier, we only care about the targeted key add <modifier> = <targeted-key>
-
Common modifier terms:
-
control
is for Control -
mod1
is for Alt/Meta -
mod2
is for NumLock -
mod4
is for Super -
mod5
is for ISO 3rd Level or Mode Switch
-
-
4.37.1. config-keymap-altgr
- keyboard layout: Programmer Dvorak + Right Alt as AltGr + Ctrl-Alt swapped + CapsLock-Escape swapped
#!/usr/bin/env bash
test -z "${DISPLAY}" && exit 0
##############################################################################
# Main
##############################################################################
#
# References
# * XKB rules: /usr/share/X11/xkb/rules/
# * Arch Linux XKB page: https://wiki.archlinux.org/index.php/X_keyboard_extension
#
##############################################################################
do-notify-short "Setting cmpitg's keyboard layout"
setxkbmap us -variant dvp
if [[ ! "$(hostname)" = "ifr-l" ]]; then
xmodmap <( cat <<EOF
! -*- mode: xmodmap-generic -*-
!
! Notes:
!
! * Press a button → keyboard sends scancode → kernel generates a keycode → keyboard layout maps to a keysym
!
! * 'clear', 'add', and 'remove' commands are for modifiers
!
! * 'keysym' command is to map keysym.
!
! * To remap modifiers, first we need to remove the old keysyms, then assign them again. That's why swapping is three-step:
! - Remove current mapping for modifiers
! - Swap the keysyms
! - Re-add the same mapping for modifiers
!
! * Modifiers:
! - 'control' is for Control
! - 'mod1' is for Alt/Meta
! - 'mod2' is for NumLock
! - 'mod3' is for Hyper
! - 'mod4' is for Super
! - 'mod5' is for ISO 3rd Level or Mode Switch
!
! evdev defs
!
! | Keycode | Keysym | XKB symbol |
!--------------|----------|------------------|------------|
! Left Ctrl | 37 | Control_L | LCTL |
! Right Ctrl | 105 | Control_R | RCTL |
! Left Alt | 64 | Alt_L | LALT |
! Right Alt | 108 | Alt_R | RALT |
! Left Hyper | 207 | Hyper_L | HYPR |
! Right Hyper | 207 | Hyper_R | HYPR |
! Left Super | 206 | Super_L | SUPR |
! Right Super | 206 | Super_R | SUPR |
! Capslock | 66 | Caps_Lock | CAPS |
! Escape | 9 | Escape | ESC |
! Compose | 203 | Multi_key | MDSW |
! Level3 | 92 | ISO_Level3_Shift | LVL3 |
!
! * References
! - Keyboard input: https://wiki.archlinux.org/index.php/Keyboard_input
! - Keycodes: /usr/share/X11/xkb/keycodes/
!
!
! Swap Escape and Capslock {keycode → keysym} mapping
!
! Pressing Capslock emits Escape
keycode 66 = Escape
! Pressing Escape emits Capslock
keycode 9 = Caps_Lock
! ! Pressing Escape emits Compose
! keycode 9 = Multi_key
! Keycode 66 still triggers Lock modifier, let's rebind it
clear Lock
add Lock = Caps_Lock
!
! Other modifiers
!
! Pressing Left Alt emits Left Ctrl
keycode 64 = Control_L
! Pressing Left Ctrl emits Left Alt
keycode 37 = Alt_L
! Pressing Right Alt emits Right Ctrl
! keycode 108 = Control_R
! Pressing Right Alt emits Right Hyper
keycode 108 = Hyper_R
! ! Pressing Right Ctrl emits Level3
! keycode 105 = ISO_Level3_Shift
! Pressing Right Ctrl emits Compose
keycode 105 = Multi_key
! Now, rearrange the modifiers
clear control
clear mod1
clear mod3
clear mod4
clear mod5
add control = Control_L Control_R
add mod1 = Alt_L Alt_R
add mod3 = Hyper_L Hyper_R
add mod4 = Super_L Super_R
add mod5 = ISO_Level3_Shift
EOF
)
else
# Running inside a virtual machine, where modifiers are already swapped.
# Thus we don't swap, just assign.
xmodmap <( cat <<EOF
! -*- mode: xmodmap-generic -*-
!
! Notes:
!
! * Press a button → keyboard sends scancode → kernel generates a keycode → keyboard layout maps to a keysym
!
! * 'clear', 'add', and 'remove' commands are for modifiers
!
! * 'keysym' command is to map keysym.
!
! * To remap modifiers, first we need to remove the old keysyms, then assign them again. That's why swapping is three-step:
! - Remove current mapping for modifiers
! - Swap the keysyms
! - Re-add the same mapping for modifiers
!
! * Modifiers:
! - 'control' is for Control
! - 'mod1' is for Alt/Meta
! - 'mod2' is for NumLock
! - 'mod3' is for Hyper
! - 'mod4' is for Super
! - 'mod5' is for ISO 3rd Level or Mode Switch
!
! evdev defs
!
! | Keycode | Keysym | XKB symbol |
!--------------|----------|------------------|------------|
! Left Ctrl | 37 | Control_L | LCTL |
! Right Ctrl | 105 | Control_R | RCTL |
! Left Alt | 64 | Alt_L | LALT |
! Right Alt | 108 | Alt_R | RALT |
! Left Hyper | 207 | Hyper_L | HYPR |
! Right Hyper | 207 | Hyper_R | HYPR |
! Left Super | 206 | Super_L | SUPR |
! Right Super | 206 | Super_R | SUPR |
! Capslock | 66 | Caps_Lock | CAPS |
! Escape | 9 | Escape | ESC |
! Compose | 203 | Multi_key | MDSW |
! Level3 | 92 | ISO_Level3_Shift | LVL3 |
!
! * References
! - Keyboard input: https://wiki.archlinux.org/index.php/Keyboard_input
! - Keycodes: /usr/share/X11/xkb/keycodes/
!
! Pressing Right Ctrl emits Right Hyper
keycode 105 = Hyper_R
! Pressing Right Alt emits Compose
keycode 108 = Multi_key
! Now, rearrange the modifiers
clear control
clear mod3
clear mod4
add control = Control_L Control_R
add mod3 = Hyper_L Hyper_R
add mod4 = Super_L Super_R
EOF
)
fi
set_layout_for_keyboard() {
local keyboard_name_="${1}"
local layout_="${2}"
local id_=$(xinput list --id-only keyboard:"${keyboard_name_}" 2>/dev/null)
if [[ -n "${id_}" ]]; then
echo setxkbmap -device "${id_}" -layout "${layout_}"
setxkbmap -device "${id_}" -layout "${layout_}"
fi
}
# set_layout_for_keyboard "SOFT/HRUF Splitography" us
xmodmap
#!/usr/bin/env bash
# config-keymap-altgr
config-logitech-trackball-marble-righty
# config-logitech-trackball-marble-lefty
config-logitech-mx-ergo-right
config-logitech-g300-mouse
config-logitech-g502-mouse
config-logitech-g903-mouse
config-lenovo-n700-mouse
config-touchpad
config-extra-peripherals
Because Steam doesn’t work with swapped modifiers.
#!/usr/bin/env bash
test -z "${DISPLAY}" && exit 0
do-notify-short "Setting keyboard layout for Steam"
newline
setxkbmap us -variant dvp
xmodmap <( cat <<EOF
!
! No mod5 by default
!
clear mod5
!
! Swap left Ctrl and Alt
!
remove control = Control_L
remove mod1 = Alt_L Meta_L
keysym Control_L = Alt_L
keysym Alt_L = Control_L
add control = Control_L
add mod1 = Alt_L
!
! Set right Ctrl as right Alt and right Alt as ISO 3rd level
!
remove control = Control_R
remove mod1 = Alt_R Meta_R
keysym Alt_R = Control_R
keysym Control_R = ISO_Level3_Shift
add control = Control_R
add mod5 = ISO_Level3_Shift
EOF
)
config-logitech-g502-mouse
config-logitech-g903-mouse
config-logitech-mx-ergo-right
Enables natural scrolling and tweaks acceleration profile.
#!/usr/bin/env bash
check-xinput 'DELL Laser Mouse' && (
do-notify "Setting natural scrolling for Dell mouse"
(
xinput set-prop 'DELL Laser Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
xinput set-prop 'DELL Laser Mouse' 'libinput Accel Speed' 0.2 &>/dev/null
) || (
xinput set-prop 'DELL Laser Mouse' 'Evdev Scrolling Distance' -1, -1, 1 &>/dev/null
)
)
check-xinput 'Logitech USB Optical Mouse' && (
do-notify 'Setting accel profile for Logitech USB Optical Mouse'
# Polynomial - very usable, recommended
xinput set-prop 'PS/2 Synaptics TouchPad' 'Device Accel Profile' 2
xinput set-prop 'Logitech USB Optical Mouse' 'Device Accel Profile' 2
do-notify "Setting natural scrolling for Logitech USB Optical Mouse"
(
xinput set-prop 'Logitech USB Optical Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'Logitech USB Optical Mouse' 'Evdev Scrolling Distance' -1, -1, 1
)
)
check-xinput 'Kingsis Peripherals Evoluent VerticalMouse 4' && (
do-notify "Setting natural scrolling for Evoluent Vertical 4"
(
xinput set-prop 'Kingsis Peripherals Evoluent VerticalMouse 4' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'Kingsis Peripherals Evoluent VerticalMouse 4' 'Evdev Scrolling Distance' -1, -1, 1 &>/dev/null
)
)
check-xinput 'MOSART Semi. 2.4G Wireless Mouse' && (
do-notify 'Setting accel profile for Anker Vertical Mouse'
# Polynomial - very usable, recommended
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse' 'Device Accel Profile' 2
do-notify "Setting natural scrolling for Anker Vertical mouse"
(
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse' 'Evdev Scrolling Distance' -1, -1, 1 &>/dev/null
)
)
check-xinput 'MOSART Semi. 2.4G Wireless Mouse Mouse' && (
do-notify 'Setting accel profile for Anker Vertical Mouse'
# Polynomial - very usable, recommended
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse Mouse' 'Device Accel Profile' 2
do-notify "Setting natural scrolling for Anker Vertical mouse"
(
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'MOSART Semi. 2.4G Wireless Mouse Mouse' 'Evdev Scrolling Distance' -1, -1, 1 &>/dev/null
)
)
check-xinput 'TPPS/2 IBM TrackPoint' && (
do-notify "Setting natural scrolling for TPPS/2 IBM TrackPoint"
(
xinput set-prop 'TPPS/2 IBM TrackPoint' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'TPPS/2 IBM TrackPoint' 'Evdev Scrolling Distance' -1, -1, 1 &>/dev/null
)
)
check-xinput 'PS/2 Synaptics TouchPad' && (
do-notify 'Setting accel profile for PS/2 Synaptics TouchPad'
# Polynomial - very usable, recommended
xinput set-prop 'PS/2 Synaptics TouchPad' 'Device Accel Profile' 2
do-notify 'Setting natural scrolling for PS/2 Synaptics TouchPad'
(
xinput set-prop 'PS/2 Synaptics TouchPad' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'PS/2 Synaptics TouchPad' 'Evdev Wheel Emulation' 1
xinput set-prop 'PS/2 Synaptics TouchPad' 'Evdev Wheel Emulation Button' 2
xinput set-prop 'PS/2 Synaptics TouchPad' 'Evdev Wheel Emulation Axes' 7, 6, 5, 4
)
)
check-xinput 'Logitech MX Vertical Advanced Ergonomic Mouse' && (
do-notify 'Setting accel profile for Logitech MX Vertical Advanced Ergonomic Mouse'
# Polynomial - very usable, recommended
xinput set-prop 'Logitech MX Vertical Advanced Ergonomic Mouse' 'Device Accel Profile' 2
do-notify 'Setting natural scrolling for Logitech MX Vertical Advanced Ergonomic Mouse'
(
xinput set-prop 'Logitech MX Vertical Advanced Ergonomic Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'Logitech MX Vertical Advanced Ergonomic Mouse' 'Evdev Scrolling Distance' -1, -1, -1
)
)
check-xinput 'Logitech MX Vertical' && (
do-notify 'Setting accel profile for Logitech MX Vertical'
# Polynomial - very usable, recommended
xinput set-prop 'Logitech MX Vertical' 'Device Accel Profile' 2
do-notify 'Setting natural scrolling for Logitech MX Vertical'
(
xinput set-prop 'Logitech MX Vertical' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'Logitech MX Vertical' 'Evdev Scrolling Distance' -1, -1, -1
)
)
check-xinput 'Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse' && (
do-notify 'Setting accel profile for Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse'
# Polynomial - very usable, recommended
xinput set-prop 'Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse' 'Device Accel Profile' 2
do-notify 'Setting natural scrolling for Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse'
(
xinput set-prop 'Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse' 'libinput Natural Scrolling Enabled' 1 &>/dev/null
) || (
xinput set-prop 'Microsoft Microsoft® 2.4GHz Transceiver v8.0 Mouse' 'Evdev Scrolling Distance' -1, -1, -1
)
)
#!/bin/zsh
setopt shwordsplit
id_=$( \
xinput list 2>/dev/null \
| grep "Dual Mode WL Touch Mouse N700" \
| head -1 \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${id_}" && exit 0
##############################################################################
do-notify-short "Configuring Dual Mode WL Touch Mouse N700
* Set natural scrolling
* Set pointer acceleration
"
{
xinput set-prop "${id_}" "Evdev Scrolling Distance" -1, -1, 1 &>/dev/null
} || {
xinput set-prop "${mouse_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
}
xinput set-prop "${id_}" "Device Accel Profile" 7
Also, resets keyboard layout for G300 back to US QWERTY, so that Ctrl+X/C/V works as expected.
#!/bin/zsh
setopt shwordsplit
mouse_=$( \
xinput list \
| grep "Logitech Gaming Mouse G300" \
| head -1 \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
keyboard_=$( \
xinput list \
| grep "Logitech Gaming Mouse G300" \
| tail -1 \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${mouse_}" && exit 0
test -z "${keyboard_}" && exit 0
##############################################################################
do-notify-short "Configuring Logitech G300 mouse
* Set natural scrolling
* Reset keyboard layout
"
{
xinput set-prop "${mouse_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
} || {
xinput set-prop "${mouse_}" "Evdev Scrolling Distance" -1, -1, 1 &>/dev/null
}
setxkbmap us -device "${keyboard_}"
#!/bin/zsh
setopt shwordsplit
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
##############################################################################
ids_=$( \
xinput list \
| grep "Logitech Gaming Mouse G502" \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${ids_}" && exit 0
##############################################################################
do-notify "Configuring Logitech G502 mouse
* Set natural scrolling
* Tuning mouse movement"
for mouse_ in ${ids_}; do
echo "${mouse_}"
{
xinput set-prop "${mouse_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
} || {
xinput set-prop "${mouse_}" "Evdev Scrolling Distance" -1, -1, 1 &>/dev/null
}
xinput set-prop "${mouse_}" "Device Accel Profile" 7
xinput set-prop "${mouse_}" "Device Accel Constant Deceleration" 2
xinput set-prop "${mouse_}" "Device Accel Adaptive Deceleration" 1
done
#!/bin/zsh
setopt shwordsplit
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
##############################################################################
ids_=$( \
xinput list \
| grep "Logitech G903 LS" \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${ids_}" && exit 0
##############################################################################
do-notify "Configuring Logitech G903 mouse
* Set natural scrolling
* Tuning mouse movement"
for mouse_ in ${ids_}; do
echo "${mouse_}"
{
xinput set-prop "${mouse_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
} || {
xinput set-prop "${mouse_}" "Evdev Scrolling Distance" -1, -1, 1 &>/dev/null
}
# xinput set-prop "${mouse_}" "Device Accel Profile" 3
# xinput set-prop "${mouse_}" "Device Accel Constant Deceleration" 0.75
# xinput set-prop "${mouse_}" "Device Accel Adaptive Deceleration" 0.75
done
#!/bin/zsh
setopt shwordsplit
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
##############################################################################
ids_=$( \
xinput list \
| rg "(Logitech MX Ergo|MX Ergo Mouse)" \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${ids_}" && exit 0
##############################################################################
do-notify "Configuring Logitech MX Ergo
* Set natural scrolling
* Tuning mouse movement"
# Ref: https://www.x.org/wiki/Development/Documentation/PointerAcceleration/
for mouse_ in ${ids_}; do
echo "${mouse_}"
{
xinput set-prop "${mouse_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
xinput set-prop "${mouse_}" "Coordinate Transformation Matrix" -1 0 1 0 -1 1 0 0 1 &>/dev/null
} || {
xinput set-prop "${mouse_}" "Evdev Scrolling Distance" -1, -1, 1 &>/dev/null
xinput set-prop "${mouse_}" "Evdev Axis Inversion" 1, 1 &>/dev/null
}
xinput set-prop "${mouse_}" "Device Accel Profile" 7
# xinput set-prop "${mouse_}" "Device Accel Constant Deceleration" 1.05
# xinput set-prop "${mouse_}" "Device Accel Adaptive Deceleration" 1.05
xinput set-prop "${mouse_}" "Device Accel Constant Deceleration" 0.8
xinput set-prop "${mouse_}" "Device Accel Adaptive Deceleration" 1.5
# xinput set-prop "${mouse_}" "Device Accel Profile" 2
# xinput set-prop "${mouse_}" "Device Accel Constant Deceleration" 1.7
# xinput set-prop "${mouse_}" "Device Accel Adaptive Deceleration" 1.5
done
#!/usr/bin/env bash
# Sources:
# https://wiki.archlinux.org/index.php/Logitech_Marble_Mouse
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
# http://www.x.org/archive/X11R7.5/doc/man/man4/evdev.4.html
# man evdev
id_=$( \
xinput list \
| grep "Logitech USB Trackball" \
| head -1 \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${id_}" && exit 0
# ID Hardware Action Result
# 1 Large button left normal click
# 2 Both large buttons middle-click †
# 3 Large button right right-click
# 4 (not a button) -
# 5 (not a button) -
# 6 (not a button) -
# 7 (not a button) -
# 8 Small button left browser back
# 9 Small button right browser forward
# * big-left: Primary click
# * big-right: Secondary click
# * small-left: Scrolling
# * small-right: Middle click
do-notify-short """Config buttons for lefties:
large-left [1]: Right click
large-right [3]: Left click
small-left [8]: Middle click
small-right [9]: Scrolling + Middle click"""
newline
# xinput set-button-map "${id_}" 1 9 3 4 5 6 7 2 9
xinput set-button-map "${id_}" 3 9 1 4 5 6 7 2 2
# small-left
# xinput set-prop "${id_}" "Evdev Wheel Emulation Button" 8
xinput set-prop "${id_}" "Evdev Wheel Emulation Button" 9
# Enable wheel emulation
xinput set-prop "${id_}" "Evdev Wheel Emulation" 1
##############################################################################
do-notify-short "Config inverted and horizontial scrolling"
# For normal scrolling
# xinput set-prop "${id_}" "Evdev Wheel Emulation Axes" 6 7 4 5
# Inverted scrolling
xinput set-prop "${id_}" "Evdev Wheel Emulation Axes" 7 6 5 4
# Inverted direction
xinput set-prop "${id_}" "Evdev Axis Inversion" 1 1
##############################################################################
do-notify-short "Config profile: Fast movement but more control at pixel-level"
newline
# Default
# Debian
# xinput set-prop "${id_}" "Device Accel Constant Deceleration" 1.5
xinput set-prop "${id_}" "Device Accel Constant Deceleration" 1.5
# More precision
# xinput set-prop "${id_}" "Device Accel Adaptive Deceleration" 5
xinput set-prop "${id_}" "Device Accel Adaptive Deceleration" 1
# Acceleration
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
# xinput set-prop "${id_}" "Device Accel Profile" -1
# xinput set-prop "${id_}" "Device Accel Profile" 6
xinput set-prop "${id_}" "Device Accel Profile" 2
# Debian
xinput set-prop "${id_}" "Device Accel Velocity Scaling" 5
# xinput set-prop "${id_}" "Device Accel Velocity Scaling" 1
# xinput set-prop "${id_}" "Device Accel Velocity Scaling" 1
#!/usr/bin/env bash
# Sources:
# https://wiki.archlinux.org/index.php/Logitech_Marble_Mouse
# http://www.x.org/wiki/Development/Documentation/PointerAcceleration/
# http://www.x.org/archive/X11R7.5/doc/man/man4/evdev.4.html
# man evdev
id_=$( \
xinput list \
| grep "Logitech USB Trackball" \
| head -1 \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
test -z "${id_}" && exit 0
# ID Hardware Action Result
# 1 Large button left normal click
# 2 Both large buttons middle-click †
# 3 Large button right right-click
# 4 (not a button) -
# 5 (not a button) -
# 6 (not a button) -
# 7 (not a button) -
# 8 Small button left browser back
# 9 Small button right browser forward
# * big-left: Primary click
# * big-right: Secondary click
# * small-left: Scrolling
# * small-right: Middle click
do-notify-short """Config buttons for righties:
large-left [1]: Left click
large-right [3]: Right click
small-left [8]: Middle click
small-right [9]: Scrolling + Middle click"""
newline
xinput set-button-map "${id_}" 1 9 3 4 5 6 7 2 9
# xinput set-button-map "${id_}" 3 9 1 4 5 6 7 2 2
# small-left
xinput set-prop "${id_}" "Evdev Wheel Emulation Button" 8
# xinput set-prop "${id_}" "Evdev Wheel Emulation Button" 9
# Enable wheel emulation
xinput set-prop "${id_}" "Evdev Wheel Emulation" 1
##############################################################################
do-notify-short "Config inverted and horizontial scrolling"
newline
# For normal scrolling
# xinput set-prop "${id_}" "Evdev Wheel Emulation Axes" 6 7 4 5
# Inverted scrolling
xinput set-prop "${id_}" "Evdev Wheel Emulation Axes" 7 6 5 4
# Inverted direction
xinput set-prop "${id_}" "Evdev Axis Inversion" 1 1
# xinput set-prop "${id_}" "Evdev Axis Inversion" 0 1
##############################################################################
do-notify-short "Config profile: Fast movement but more control at pixel-level"
newline
# Default
# Debian
# xinput set-prop "${id_}" "Device Accel Constant Deceleration" 1.5
# xinput set-prop "${id_}" "Device Accel Constant Deceleration" 1.5
# More precision
# xinput set-prop "${id_}" "Device Accel Adaptive Deceleration" 5
# xinput set-prop "${id_}" "Device Accel Adaptive Deceleration" 1
# Acceleration
# xinput set-prop "${id_}" "Device Accel Profile" -1
# xinput set-prop "${id_}" "Device Accel Profile" 6
xinput set-prop "${id_}" "Device Accel Profile" 2
# Debian
xinput set-prop "${id_}" "Device Accel Velocity Scaling" 5
# xinput set-prop "${id_}" "Device Accel Velocity Scaling" 1.5
# xinput set-prop "${id_}" "Device Accel Velocity Scaling" 1
Lots of tweaks, the code should be self-explanatory though.
#!/usr/bin/env bash
check-xinput -i "touchpad" || exit 0
id_=$( \
xinput list \
| grep -i 'synaptics touchpad' \
| cut -d'=' -f2 \
| awk '{ print $1 }' \
)
scrolling_distance_2_=$(xinput list-props ${id_} \
| grep 'Synaptics Scrolling Distance' \
| gawk '{ print $NF }' \
| sed 's/-//g' \
)
scrolling_distance_1_=$(xinput list-props ${id_} \
| grep 'Synaptics Scrolling Distance' \
| gawk '{ print $(NF - 1) }' \
| cut -d',' -f1 \
| sed 's/-//g' \
)
##############################################################################
do-notify-short """Configuring touchpad
* Setting natural scrolling
* Enabling tapping
* Enabling two-finger tapping as secondary click"""
newline
# Edge
# synclient LeftEdge=1200
# synclient RightEdge=5100
# synclient TopEdge=1000
# synclient BottomEdge=4600
# synclient LeftEdge=1000
# synclient RightEdge=5200
# synclient TopEdge=1000
# synclient BottomEdge=5000
# Palm detection
## Wed, 27 Jul 2016 23:22:03 +0300 - Disable because it's no longer relevant
# synclient PalmDetect=1
# Tap
## Wed, 27 Jul 2016 23:22:03 +0300 - Disable because it's no longer relevant
# synclient MaxTapTime=180
# synclient MaxTapMove=221
# synclient MaxDoubleTapTime=100
# synclient SingleTapTimeout=180
# synclient EmulateTwoFingerMinZ=1
# synclient EmulateTwoFingerMinW=7
# synclient VertEdgeScroll=1
# synclient HorizEdgeScroll=1
# Corner
## Wed, 27 Jul 2016 23:22:03 +0300 - Disable because it's no longer relevant
# synclient RTCornerButton=0
# synclient RBCornerButton=0
# synclient LTCornerButton=1
# synclient LBCornerButton=0
# synclient TapButton1=1
# synclient TapButton2=3
# synclient TapButton3=2
# synclient ClickFinger1=1
# synclient ClickFinger2=1
# synclient ClickFinger3=2
# synclient CircularScrolling=0
# Natural scrolling
# synclient VertScrollDelta=-111
# synclient HorizScrollDelta=-111
# synclient VertEdgeScroll=0
# synclient HorizEdgeScroll=0
##############################################################################
(
xinput set-prop "${id_}" "libinput Tapping Enabled" 0 &>/dev/null
xinput set-prop "${id_}" "libinput Natural Scrolling Enabled" 1 &>/dev/null
) || (
xinput set-prop "${id_}" "Synaptics Scrolling Distance" "-${scrolling_distance_1_}" "-${scrolling_distance_2_}" &>/dev/null
xinput set-prop "${id_}" "Synaptics Two-Finger Scrolling" 1, 1 &>/dev/null
)
# xinput get-button-map "SynPS/2 Synaptics TouchPad" 1 2 3 4 5 6 7 8 9 10 11 12
Extracts from a video file, creating the same file name with appropriate extension.
#!/bin/zsh
setopt shwordsplit
report-missing-executables ffmpeg Ffmpeg || exit 1
file_="$1"
ffmpeg -i "${file_}" -vn -acodec copy \
"$file_:r.$(ffprobe ${file_} 2>&1 | grep Audio | sed -rn 's/.*Audio: ([^ ]*).*/\1/p')"
This script takes a list of files as arguments.
#!/usr/bin/env 9-rc
report-missing-executables ffmpeg Ffmpeg || exit 1
for (f in $*) {
new_name=`{echo $f | replace-extension mp3}
ffmpeg -i $f -vn -aq 1 $"new_name
}
This script takes a list of files as arguments.
#!/usr/bin/env 9-rc
report-missing-executables ffmpeg Ffmpeg || exit 1
for (f in $*) {
new_name=`{echo $f | replace-extension ogg}
ffmpeg -i $f -vn -aq 1 $"new_name
}
Returns the average CPU usage measured in 3 consecutive seconds, using mpstat
.
#!/usr/bin/env bash
#
# Using `mpstat', calculates average CPU usage in 3 seconds.
#
report-missing-executables mpstat Sysstat || exit 1
mpstat 3 1 | tail -1 | gawk '$12 ~ /[0-9.]+/ { print 100 - $12"%" }'
#!/usr/bin/env bash
#
# Creates a tags file named TAGS using ctags.
#
report-missing-executables tags "Ctags or Exuberant Ctags" || exit 1
if test -z "$1"; then
cat <<EOF
Usage: $0 <directory> [ctags-options]*
Creates a tags file named TAGS using ctags.
EOF
exit 2
fi
dir_name_="$1"
shift
ctags "$@" -f "${dir_name_}"/TAGS -R "${dir_name_}"/*
#!/usr/bin/env bash
report-missing-executables notify-send Libnotify || exit 1
echo "$@"
# qdbus org.freedesktop.Notifications &>/dev/null && notify-send "$@"
notify-send "$@"
#!/usr/bin/env bash
report-missing-executables notify-send Libnotify || exit 1
echo "$@"
# qdbus org.freedesktop.Notifications &>/dev/null && notify-send -t 2000 "$@"
# qdbus org.freedesktop.Notifications &>/dev/null && notify-send "$@"
notify-send "$@"
#!/usr/bin/env 9-rc
#
# Drops the first $1 lines.
#
n_lines=$1
n_lines=`{echo $n_lines + 1 | bc}
tail -n +$n_lines
This script is particularly helpful when using with Emacs/Acme, e.g. called with a text selection.
#!/usr/bin/env sh
epoch_=$(cat)
exec date --date="@${epoch_}" -R
#!/usr/bin/env 9-rc
#
# Translates from English to Finnish with Google Translate, using
# soimort/translate-shell tool.
#
report-missing-executables trans soimort/translate-shell || exit 1
TARGET_LANG=fi gtranslate $*
#!/usr/bin/env 9-rc
#
# Translates from Finnish to English with Google Translate, using
# soimort/translate-shell tool.
#
report-missing-executables trans soimort/translate-shell || exit 1
TARGET_LANG=en gtranslate $*
#!/usr/bin/env 9-rc
#
# Formats text from stdin using Emacs's fill-paragraph.
#
input=`{cat}
sexpr=`{echo `{cat <<EOF}}
(with-temp-buffer
(set-fill-column 78)
(insert "$input")
(end-of-buffer)
(fill-region 0 (point))
(princ (buffer-string)))
EOF
emacs --batch --eval $"sexpr $* >[2]/dev/null
#!/usr/bin/env 9-rc
input=`{cat}
if (~ $1 '') {
echo Usage: $0 '<'replacement'>' >[1=2]
exit 1
}
rev_replacement=`{echo $1 | rev}
echo $input | rev | sed 's/^[^.]*\./'$rev_replacement'./' | rev
#!/usr/bin/env 9-rc
#
# Runs a Git server.
#
program=`{basename $0}
if (~ $1 '-h' '--help') {
cat <<USAGE
exit 0
}
Usage:
Runs a Git server.
$program :: Take current directory as Git repository
$program <git-repo> :: Take a specific Git repository
By default, the Git server is opened on port 4242. This could be overriden by
setting the environment variable GIT_PORT. For example: run a Git server on
port 5454, serving content from Git repo at /m/bin:
GIT_PORT=5454 $program /m/bin
Then, you can clone the repo with: git clone git://<host>:<port>/ <repo-name>
Note that this method is a quick way to share Git repository and it's not at
all secure. In practice, you might want to Git server behind a reverse proxy.
USAGE
(test $#GIT_PORT -eq 0) && git_port=4242 || git_port=$GIT_PORT
(test $#1 -eq 0) && git_path='.' || git_path=$1
exec git daemon --reuseaddr '--base-path='$git_path --export-all --verbose '--port='$git_port
#!/usr/bin/env bash
report-missing-executables xrandr XRandR ag Ag awk "GNU Awk" || exit 1
xrandr | awk '/connected/ { print $1 }'
#!/usr/bin/env tclsh
package require Tcl 8
package require cmdline
if {[catch {exec report-missing-executables find "GNU Find" sort Coreutils parallel "GNU Parallel" with-workdir cmpitg-scripts <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{fd stdout}} {
puts $fd {get-all-execs [<max-depth>]
Get all executables from the PATH environment variables, sort in ascending order, deduplicate, and return them one line per entry. max-depth defines how deep we traverse from a path. If not specified, max-depth is 1.}
}
proc getAccessiblePaths {maxDepth} {
set rawPaths [split [string trim [exec dedup-PATH]] ":"]
set paths {}
foreach path $rawPaths {
if {$path ne "." && $path ne "./" && [file exists $path] && [file isdirectory $path]} {
# lappend cmds [list with-workdir "$path/" find . -maxdepth $maxDepth -type f,l -executable | cut -c 3-]
lappend paths "$path/"
}
}
return $paths
}
if {$::argv == "--help"} {
usage
}
if {$::argc > 2} {
usage stderr
exit 1
}
if {$::argv eq {}} {
set maxDepth 1
} else {
set maxDepth [lindex $::argv 0]
}
set paths [getAccessiblePaths $maxDepth]
# With GNU Parallel - Slowest
# catch { puts [exec parallel --link with-workdir ::: {*}$paths ::: fdfind ::: . ::: --maxdepth ::: $maxDepth ::: --type ::: x ::: --type ::: l ::: --hidden ::: --no-ignore ::: --color ::: never | rev | cut -d/ -f 1 | rev | sort -u | rg -v {^$}]}
# Without GNU Parallel
# basename -> Slow
# catch { puts [exec fdfind . --maxdepth $maxDepth --type x --type l --hidden --no-ignore {*}$paths --color never --exec basename | sort -u | rg -v {^$}]}
# cut + rev: fast
catch { puts [exec fdfind . --maxdepth $maxDepth --type x --type l --hidden --no-ignore {*}$paths --color never | rev | cut -d/ -f 1 | rev | sort -u | rg -v {^$}]}
# Awk: fast but a bit slower
# catch { puts [exec fdfind . --maxdepth $maxDepth --type x --type l --hidden --no-ignore {*}$paths --color never | awk --field-separator=/ {{ print $NF }} | sort -u | rg -v {^$}]}
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables sensors lm-sensors acpi acpi >@ stdout 2>@ stderr}]} {
exit 1
}
package require Tcl 8.4
package require json 1.3.3
set deviceMapping {
{
label CPU
command {::json::json2dict [exec sensors -j <@ stdin 2> /dev/null]}
groups {
{
unit °C
paths {
{k10temp-pci-00c3 Tdie temp1_input}
{k10temp-pci-00c3 Tdie temp2_input}
{k10temp-pci-00c3 Tccd1 temp3_input}
{coretemp-isa-0000 "Core 0" temp2_input}
{coretemp-isa-0000 "Core 1" temp3_input}
{coretemp-isa-0000 "Core 2" temp4_input}
{coretemp-isa-0000 "Core 3" temp5_input}
}
}
{
unit " RPM"
paths {
{thinkpad-isa-0000 fan1 fan1_input}
}
}
}
}
{
label GPU
command {::json::json2dict [exec sensors -j <@ stdin 2> /dev/null]}
groups {
{
unit °C
paths {
{amdgpu-pci-0500 junction temp2_input}
{amdgpu-pci-0600 edge temp1_input}
}
}
{
unit " RPM"
paths {
{amdgpu-pci-0500 fan1 fan1_input}
}
}
}
}
{
label Bats
command {join [exec acpi -b | cut -d: -f 2 | awk {BEGIN { FS="," } { print $2 $1 }} | sed {s/[:,]//g ; s/ Not charging//g ; s/ Charging/+/g ; s/ Discharging/-/g ; s/ Unknown//g} 2> /dev/null]}
}
}
set sensorData [::json::json2dict [exec sensors -j <@ stdin 2> /dev/null]]
set resultList {}
proc getValueForPath {data path} {
if {![dict exists $data {*}$path]} {
return {}
} else {
return [format "%.1f" [dict get $data {*}$path]]
}
}
proc getReadingForGroup {sensorData group} {
set currentReadingList {}
set unit [dict get $group unit]
foreach path [dict get $group paths] {
set number [getValueForPath $sensorData $path]
if {$number ne {}} {
lappend currentReadingList "$number$unit"
}
}
return $currentReadingList
}
proc getReadingForDevice {dev} {
set label [dict get $dev label]
set sensorData [eval [dict get $dev command]]
if {[dict exists $dev groups]} {
set reading {}
foreach group [dict get $dev groups] {
set groupReading [getReadingForGroup $sensorData $group]
if {$groupReading ne {}} {
lappend reading [join $groupReading ", "]
}
}
} else {
# If the 'groups' key doesn't exist, the reading is $sensorData
set reading $sensorData
}
if {$reading ne {}} {
return "$label: [join $reading ", "]"
} else {
return {}
}
}
foreach dev $deviceMapping {
set reading [getReadingForDevice $dev]
if {$reading ne {}} {
lappend resultList $reading
}
}
puts [join $resultList " | "]
#!/usr/bin/env sh
exec echo "${PATH}" | awk -v RS=: -v ORS=: '!seen[$0]++' | head -1
#!/usr/bin/env bash
#
# Converts HTML to text. HTML is read from stdin.
#
report-missing-executables lynx Lynx || exit 1
exec lynx -dump -stdin "$@"
4.59. i3-exec-command
- executes an i3 command
#!/usr/bin/env bash
i3-input -f 'pango:Noto Sans 10' "$@"
4.60. i3-move-to-workspace
- moves a window to a workspace with i3
#!/usr/bin/env bash
i3-input \
-f 'pango:Noto Sans 10' \
-F 'move workspace "%s"' \
-P 'Move window to workspace: ' %s
4.61. i3-rename-workspace
- renames current workspace in i3
#!/usr/bin/env bash
i3-input \
-f 'pango:Noto Sans 10' \
-F 'rename workspace to "%s"' \
-P 'Rename workspace: ' %s
Requirement: rofi
.
#!/usr/bin/env sh
report-missing-executables run-menu run-menu || exit 1
exec run-menu -modi window -show window
#!/usr/bin/env tclsh
package require Tcl 8
package require Tclx
if {[catch {exec report-missing-executables get-all-randr-outputs get-all-randr-outputs >@ stdout 2>@ stderr}]} {
exit 1
}
proc getSetMonitors {} {
set res {}
set takeNow 0
foreach cmdArg $::argv {
# We take the argument right after the --output argument
if {$cmdArg eq "--output"} {
set takeNow 1
} elseif {$takeNow} {
lappend res $cmdArg
set takeNow 0
}
}
return $res
}
proc filterMonitors {setMonitors monitors} {
set res {}
foreach monitor $monitors {
if {[lsearch $setMonitors $monitor] == -1} {
lappend res $monitor
}
}
return $res
}
set setMonitors [getSetMonitors]
set allMonitors [string trim [exec get-all-randr-outputs]]
set monitors [filterMonitors $setMonitors $allMonitors]
set cmd [list {*}$::argv]
foreach monitor $monitors {
set cmd [list {*}$cmd "--output" $monitor "--off"]
}
puts "xrandr $cmd"
execl "xrandr" $cmd
#!/usr/bin/env bash
report-missing-executables xrandr XRandR get-all-randr-outputs get-all-randr-outputs sed "GNU Sed" tr Coreutils || exit 1
get-all-randr-outputs | sed 's/$/ --auto/g; s/^/--output /g' | tr "\n" " " | xargs xrandr
#!/usr/bin/env tclsh
package require Tcl 8
package require Tclx
if {[catch {exec report-missing-executables set-only-monitors set-only-monitors >@ stdout 2>@ stderr}]} {
exit 1
}
if {[info exists ::env(MY_MAIN_MONITOR_OUTPUT)]} {
set mainMonitor $::env(MY_MAIN_MONITOR_OUTPUT)
set mainMode $::env(MY_MAIN_MONITOR_MODE)
} else {
set mainMonitor [lindex $monitors 0]
set mainMode [lindex $monitors 1]
}
execl "set-only-monitors" [list "--output" $mainMonitor "--mode" $mainMode "--primary"]
#!/usr/bin/env tclsh
set cmd [lindex $::argv 0]
proc playTestSound {} {
exec paplay /usr/share/sounds/freedesktop/stereo/audio-volume-change.oga <@ stdin >@ stdout 2>@ stderr
}
proc setVol {amount} {
exec pactl set-sink-volume "@DEFAULT_SINK@" $amount <@ stdin >@ stdout 2>@ stderr
}
proc setMute {flag} {
exec pactl set-sink-mute @DEFAULT_SINK@ $flag <@ stdin >@ stdout 2>@ stderr
}
proc getVol {} {
# return [join [exec amixer -c 1 -M -D pulse get Master | grep -o -E {[[:digit:]]+%}]]
return [join [exec amixer -M -D pulse get Master | grep -o -E {[[:digit:]]+%}]]
}
proc showVol {} {
set vol [getVol]
puts $vol
exec notify-send "Volume: $vol" <@ stdin >@ stdout 2>@ stderr
}
switch $cmd {
get {
showVol
}
up -
+ {
setVol +5%
playTestSound
showVol
}
down -
"-" {
setVol -5%
playTestSound
showVol
}
toggle-mute {
setMute toggle
playTestSound
showVol
}
mute {
setMute 1
}
unmute {
setMute 0
playTestSound
showVol
}
}
#!/usr/bin/env sh
pgrep --full 'udiskie*.*tray' >/dev/null 2>&1 || exec udiskie --no-automount --tray
#!/usr/bin/env sh
# pidof clipit >/dev/null 2>&1 || exec clipit
pidof greenclip >/dev/null 2>&1 || exec greenclip daemon
#!/usr/bin/env sh
exec run-menu -modi "clipboard:greenclip print" -show clipboard -run-command '{cmd}'
#!/usr/bin/env sh
pidof xbindkeys >/dev/null 2>&1 || exec xbindkeys --nodaemon --poll-rc
#!/usr/bin/env sh
pkill xfce4-power-manager
pkill mate-power-manager
# exec xfce4-power-manager --no-daemon
exec mate-power-manager
#!/usr/bin/env sh
# pkill xfce4-volumed
# exec xfce4-volumed --no-daemon
# pidof kmix >/dev/null 2>&1 || kmix
pkill pasystray ; pasystray
+ .file::run-ibus-daemon
#!/usr/bin/env sh
exec ibus-daemon -xvr
#!/usr/bin/env sh
pidof xscreensaver >/dev/null 2>&1 || exec xscreensaver
#!/usr/bin/env sh
pkill nm-applet
exec nm-applet --sm-disable
#!/usr/bin/env bash
gnome-settings-daemon -h >/dev/null 2>&1 && (
pidof gnome-settings-daemon >/dev/null 2>&1 || gnome-settings-daemon
)
[[ -e /usr/lib/gnome-settings-daemon/gsd-xsettings ]] && (
pidof gsd-xsettings >/dev/null 2>&1 || /usr/lib/gnome-settings-daemon/gsd-xsettings
)
#!/usr/bin/env sh
# pidof insync >/dev/null 2>&1 || exec insync start
insync quit
exec insync start
#!/usr/bin/env sh
exec rofi -l 40 \
-width 85 \
-i \
-multi-select \
-font "Cascadia Code 11" \
-kb-row-select Tab \
-kb-row-tab "" \
-kb-row-left "" \
-kb-row-right "" \
-kb-row-up Super+c,Up,Control+p \
-kb-row-down Super+t,Down,Control+n \
-kb-row-left Super+h,Left,Control+b \
-kb-row-right Super+n,Right,Control+f \
-kb-move-front Super+d,Control+a \
-kb-move-end Super+Shift+d,Control+e \
-kb-move-word-back Super+g,Alt+b \
-kb-move-word-forward Super+r,Alt+f \
-kb-accept-custom Shift+Return \
-kb-accept-alt Control+Return \
"$@"
#!/usr/bin/env sh
# deep-exec guix/with-env report-missing-executables sawfish Sawfish || exit 1
# menu_path_=$(deep-exec guix/with-env which sawfish \
# | xargs -n 1 -I "{}" readlink -f "{}" \
# | xargs -n 1 -I "{}" dirname "{}" \
# | xargs -n 1 -I "{}" readlink -f "{}/../lib/sawfish/sawfish-menu")
# exec deep-exec guix/with-env "${menu_path_}" "$@"
report-missing-executables sawfish Sawfish || exit 1
exec /usr/lib/x86_64-linux-gnu/sawfish/sawfish-menu "$@"
TODO: Note about dispatch-action
in the docstring
#!/usr/bin/env tclsh
package require Tcl 8
package require Tclx
package require cmdline
if {[catch {exec report-missing-executables get-all-execs get-all-execs run-menu run-menu add-to-history add-to-history <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
try {
array set cmdArgs [::cmdline::getoptions ::argv {
{history-file.arg "~/.local/app-runner-history" "Application history file"}
{max-history.arg 256 "Maximum number of entries in the history"}
{prefix.arg "! " "String with which each executable is prefixed"}
{max-depth.arg 10 "How deep paths from PATHS are traversed"}
} {[--history-file <app-runner-history>] [--prefix <prefix>]
Run a fuzzy searcher tool with all the executables found in the PATH environment variable and from a history file. The result of the search is then executed based on predefined patterns as follows.
* '!@ <command> [args...]' :: Run the command in a terminal emulator
* '!! <command> [args...]' :: Run the command in a terminal emulator, pause and prompt for exiting when after the command finishes
* '<url>' :: Open the URL with 'web-browser-gui'
* '<file-path>' :: Open the file path with a text editor
* 'dir:<dir-path>' :: Open the directory using 'dir-browser-gui'
TODO: More patterns are later supported using Plan9port's Plumber.
}]} trap {CMDLINE USAGE} {msg _o} {
if {[llength $::argv] == 0} {
puts $msg
exit 0
} else {
puts stderr $msg
exit 1
}
}
proc readHistory {path} {
if {[file exists $path]} {
set fd [open $path r]
set data [read $fd]
close $fd
return $data
} else {
return ""
}
}
proc addToHistory {path maxHistory text} {
set fd [open "| add-to-history --max-history $maxHistory $path" w]
puts $fd $text
close $fd
}
proc getChoice {history execs} {
set fd [open "| run-menu -dmenu -p Text " r+]
puts -nonewline $fd $history
puts -nonewline $fd $execs
flush $fd
chan close $fd write
set res [read $fd]
catch {close $fd}
return [string trim $res]
}
set historyFile [file normalize $cmdArgs(history-file)]
set maxDepth $cmdArgs(max-depth)
set maxHistory $cmdArgs(max-history)
set prefix $cmdArgs(prefix)
set execs [exec get-all-execs $maxDepth | sed "s/^/$prefix/g"]
set history [readHistory $historyFile]
set choice [getChoice $history $execs]
if {$choice ne ""} {
addToHistory $historyFile $maxHistory $choice
execl dispatch-action [list $choice]
}
#!/usr/bin/env dash
exec wihack -type toolbar rmacs --shape utils --new-frame eval '(rocket:show-command-runner-with-dedicated-frame)'
# exec rmacs --shape utils --new-frame eval '(prog1 (rocket:show-command-runner-with-dedicated-frame) (~wmii/set-frame-floating))'
# exec run-menu -modi run,drun -show run -sidebar-mode "$@"
#!/usr/bin/env sh
# exec google-chrome --remote-debugging-port=${CHROME_REMOTE_DEBUGGING_PORT:-9222} "$@"
exec google-chrome "$@"
#!/usr/bin/env sh
# exec run-chrome --app=https://web.whatsapp.com/ "$@"
exec chromium --app=https://web.whatsapp.com/ "$@"
#!/usr/bin/env sh
report-missing-executables tdrop tdrop || exit 1
# exec tdrop -h 60% --auto-detect-wm --monitor-aware term-emu zsh-user
exec tdrop --remember --monitor-aware "$@"
This executable is supposed to be used with tdrop, which, in turn, uses the executable name to perform various hacks in order to set X window properties. Hence, its name is prefixed with emacsclient
.
#!/usr/bin/env dash
# TODO: Help text
commander_path_=${1:-/m/scratch/commander}
emacs_socket_name_=${EMACS_SOCKET_NAME:-edit}
exec emacsclient --socket-name="${emacs_socket_name_}" --no-wait --create-frame --eval "(~smart-open-file \"${commander_path_}\")"
#!/usr/bin/env dash
exec run-tdrop term-emu zsh-user "$@"
#!/usr/bin/env tclsh
# TODO: Documentation
# TODO: Help
# TODO: Read stdin?
# TODO: report-missing-executables
# TODO: Declarative configuration?
# TODO: Samples
# dispatch-action 'ssh://<foobar>!' 'w'
# dispatch-action 'ssh://<username>@<foobar>' '!' 'w'
# dispatch-action 'ssh://<username>@<foobar>:<port>' '!' 'w'
# dispatch-action 'ssh://<foobar>:/tmp/'
# dispatch-action 'ssh://<foobar>:/tmp/foobar'
# dispatch-action 'ssh://<username>@<foobar>:/tmp/'
# dispatch-action 'ssh://<username>@<foobar>:<port>:/tmp/foobar'
package require Tclx
##############################################################################
# Helpers
##############################################################################
## TODO: Documentation
proc stripPrefix {text prefix} {
return [string range $text [string length $prefix] end]
}
proc orString {str elseStr} {
if {[string trim $str] eq ""} {
return $elseStr
} else {
return $str
}
}
proc substEnvVars {str} {
return [exec echo $str | envsubst]
}
## TODO: Documentation
proc splitString {text str {startIndex 0}} {
set index [string first $str $text $startIndex]
if {$index != -1} {
set i1 [expr {$index - 1}]
set i2 [expr {$index + [string length $str]}]
return [list [string range $text 0 $i1] [string range $text $i2 end]]
} else {
return [list $text ""]
}
}
proc constructEnrichedPathCmd {cmd} {
return "deep-exec $cmd"
}
proc copyToClipboard {text} {
set fd [open "| xsel -b" w]
puts $fd $text
close $fd
}
#
# Try opening a file. TODO: Documentation for file pattern.
#
# \(~file-pattern? \"/tmp/aoeu\"\) ⇒ t
# \(~file-pattern? \"/tmp/aoeu:10\"\) ⇒ t
# \(~file-pattern? \"/tmp/aoeu:/hello world/\"\) ⇒ t
# \(~file-pattern? \"/tmp/non-existent\"\) ⇒ nil
# /tmp/aoeu -> /tmp/aoeu
# /tmp/aoeu:10
# /tmp/aoeu /hello/
# /tmp/aoeu:10 /hello/
# /tmp/aoeu +10 /hello/
proc tryOpeningFile {serverName inNewFrameP rest} {
# Visit a file and return its buffer
proc visitFile {serverName path} {
return [exec rmacs --client-opts --alternate-editor=vim --name $serverName --no-wait visit $path <@ stdin 2>@ stderr]
}
proc gotoLine {serverName buffer number} {
return [exec rmacs --client-opts --alternate-editor=vim --name $serverName --with-buffer $buffer eval "(goto-line $number)" <@ stdin >@ stdout 2>@ stderr]
}
proc gotoPattern {serverName buffer pattern} {
return [exec rmacs --client-opts --alternate-editor=vim --name $serverName --with-buffer $buffer eval "(re-search-forward \"$pattern\")" <@ stdin >@ stdout 2>@ stderr]
}
set possiblePath [file normalize [lindex $rest 0]]
if {[file exists $possiblePath]} {
set buffer [visitFile $serverName $possiblePath]
} else {
set lastSepIndex [string last ":" $possiblePath]
if {$lastSepIndex == -1} {
return 0
}
set possibleRealPath [string range $possiblePath 0 $lastSepIndex-1]
if {![file exists $possibleRealPath]} {
return 0
}
set buffer [visitFile $serverName $possibleRealPath]
set lineNumber [string range $possiblePath $lastSepIndex+1 end]
set possiblePath $possibleRealPath
catch {gotoLine $serverName $buffer $lineNumber}
}
foreach arg [lrange $rest 1 end] {
switch -glob $arg {
"+*" {
set lineNumber [stripPrefix $arg {+}]
catch {gotoLine $serverName $buffer $lineNumber}
}
"/*/" {
set pattern [string range $arg 1 end-1]
catch {gotoPattern $serverName $buffer $pattern}
}
default {
puts stderr "ERROR: Unrecognized pattern for file path: $arg"
}
}
}
if {$inNewFrameP} {
execl rmacs [list --client-opts --alternate-editor=vim --name $serverName --no-wait --new-frame open $possiblePath]
} else {
exec rmacs --client-opts --alternate-editor=vim --name $serverName open $possiblePath <@ stdin >@ stdout 2>@ stderr
}
return 1
}
proc callWMClientMenu {wmName client} {
switch $wmName {
"awesome" {
execl awesome-client [list "display_client_menu_by_actionable_title('[string trim $client]')"]
}
"herbstluftwm" {
set winID [lindex [split [string trim $client] " "] end]
execl enrich-path [list wm herbstluft - call-menu client $winID]
}
default {
execl run-menu [list "-e" "Error: Unrecognized window manager wmName=$wmName for client menu"]
}
}
}
proc callWMDesktopMenu {wmName desktop} {
switch $wmName {
"herbstluftwm" {
execl enrich-path [list wm herbstluft - call-menu tag $desktop]
}
default {
execl run-menu [list "-e" "Error: Unrecognized window manager wmName=$wmName for desktop menu"]
}
}
}
##############################################################################
# Main
##############################################################################
set text [string trim [join $::argv " "]]
if {[info exists ::env(RMACS_NAME)]} {
set rmacsServerName $::env(RMACS_NAME)
} else {
set rmacsServerName "edit"
}
if {$text ne ""} {
switch -glob $text {
"mux://*!!!*" {
set rest [splitString [stripPrefix $text {mux://}] "!!!"]
set muxSessionName [string trim [orString [substEnvVars [lindex $rest 0]] ":."]]
set cmd [constructEnrichedPathCmd [lindex $rest 1]]
execl with-mux-session [list $muxSessionName "-" {*}$cmd]
}
"mux://*!!*" {
set rest [splitString [stripPrefix $text {mux://}] "!!"]
set muxSessionName [string trim [orString [substEnvVars [lindex $rest 0]] ":."]]
set cmd [constructEnrichedPathCmd [lindex $rest 1]]
execl with-mux-session [list $muxSessionName "-" with-pause {*}$cmd]
}
"mux://*!*" {
set rest [splitString [stripPrefix $text {mux://}] "!"]
set muxSessionName [string trim [orString [substEnvVars [lindex $rest 0]] ":."]]
set cmd [constructEnrichedPathCmd [lindex $rest 1]]
exec tmux send-keys -l -t $muxSessionName $cmd
execl tmux [list send-keys -t $muxSessionName Enter]
}
"ssh://*!*" {
# Execute an SSH command
set parts [splitString $text "!"]
set hostExpr [string trim [substEnvVars [lindex $parts 0]]]
set cmd [lindex $parts 1]
execl ssh [list $hostExpr {*}$cmd]
}
"ssh://*:/*/" {
# Expand a remote dir via SSH
set parts [splitString [string trim $text] ":/" [string length "ssh://"]]
set hostExpr [string trim [substEnvVars [lindex $parts 0]]]
set path "/[lindex $parts 1]"
# execl ssh [list $hostExpr ls -1 --indicator-style=slash --dereference --all --group-directories-first $path]
execl ssh [list $hostExpr ls -1aHF --group-directories-first $path]
}
"ssh://*:/*" {
# Edit a remote file via SSH
# TODO: Make the edit functionality work
set parts [splitString [string trim $text] ":/" [string length "ssh://"]]
set hostExpr [string trim [substEnvVars [lindex $parts 0]]]
set path "/[lindex $parts 1]"
execl ssh [list $hostExpr cat $path]
}
"edit!*" {
set path [exec which [stripPrefix $text {edit!}]]
execl ffn [list $path]
}
"copy!*" {
set text [stripPrefix $text {copy!}]
copyToClipboard $text
}
"!%*" {
set cmd [stripPrefix $text {!%}]
execl with-env-user [list with-term-emu-sh --without-termux - $cmd]
}
"!@*" {
set cmd [stripPrefix $text {!@}]
execl with-env-user [list with-term-emu-sh --detach-termux - $cmd]
}
"!!!*" {
set cmd [stripPrefix $text {!!!}]
execl with-env-user [list with-term-emu-sh - {*}$cmd]
}
"!!*" {
set cmd [stripPrefix $text {!!}]
execl with-env-user [list with-term-emu-sh --pause-after-exec - $cmd]
}
"!*" {
set cmd [stripPrefix $text {!}]
execl with-env-user [list $::env(SHELL) -c $cmd]
}
"tmux :: *" {
set tmuxSessionName [string trim [stripPrefix $text "tmux :: "]]
execl bring-termux-session $tmuxSessionName
}
"wind :: *" {
set wmName [exec deep-exec wm/get-wm-name]
callWMClientMenu $wmName $text
}
"desktop :: *" {
set wmName [exec deep-exec wm/get-wm-name]
set desktop [stripPrefix $text {desktop :: }]
callWMDesktopMenu $wmName $desktop
}
"file :: *" {
set path [string trim [stripPrefix $text {file :: }]]
tryOpeningFile $rmacsServerName 1 [list $path]
}
"*/" {
set path [file nativename [substEnvVars [string trim $text]]]
execl ls [list -1 --indicator-style=slash --dereference --all --group-directories-first $path]
}
default {
if {![tryOpeningFile $rmacsServerName 0 $::argv]} {
execl run-menu [list "-e" "Error: Unrecognized pattern: $text"]
}
}
}
}
-
Run in a term emu without termux:
dispatch-action '!% aoeu && pwd ; with-pause true ; htop' dispatch-action '!%' aoeu '&&' pwd ';' with-pause true ';' htop
-
Run in a detached termux → see a flash of the term emu, could find the command from a termux session
dispatch-action '!@ aoeu && pwd ; with-pause true ; htop' dispatch-action '!@' aoeu '&&' pwd ';' with-pause true ';' htop
-
Run in a termux → see a term emu with a termux
dispatch-action '!!! aoeu && pwd ; with-pause true ; htop' dispatch-action '!!!' aoeu '&&' pwd ';' with-pause true ';' htop
-
Run in a termux → see a term emu with a termux, paused upon exiting
dispatch-action '!! aoeu && pwd ; with-pause true ; htop' dispatch-action '!!' aoeu '&&' pwd ';' with-pause true ';' htop
-
Run in a termux (subshell execution) → see a term emu with a termux, paused upon exiting
dispatch-action '!! ( aoeu && pwd ; with-pause true ; htop)' dispatch-action '!!' '(' aoeu '&&' pwd ';' with-pause true ';' htop ')'
4.70. run-menu-and-dispatch
- runs a menu program, allowing user to choose an item/input custom string, then dispatch an action based on the output
TODO: Documentation TODO: Support history file TODO: Make run-app-launcher depend on this
#!/usr/bin/env tclsh
package require Tcl 8
package require Tclx
if {[catch {exec report-missing-executables run-menu run-menu <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
proc spitToTempFile {input} {
file tempfile tempPath
set fd [open $tempPath w+]
puts -nonewline $fd $input
close $fd
return $tempPath
}
proc getChoices {prompt input} {
set fd [open "| run-menu -dmenu -p $prompt" r+]
puts -nonewline $fd $input
chan close $fd write
catch {set choices [string trim [read $fd]]}
catch {close $fd}
return [split $choices "\n"]
}
set ::PROMPT [lindex $::argv 0]
set ::INPUT [read stdin]
set ::CHOICES [getChoices $::PROMPT $::INPUT]
if {$::CHOICES ne ""} {
# execl dispatch-action [list $::CHOICES]
foreach choice $::CHOICES {
exec dispatch-action $choice
}
}
TODO: Help TODO: Note on standard input
#!/usr/bin/env zsh
report-missing-executables grep Grep run-menu-and-dispatch run-menu-and-dispatch tmux Tmux || exit 1
rmacs_server_name_=edit
if rmacs list 2>&1 | grep -e "^${rmacs_server_name_}$" >/dev/null 2>&1; then
rmacs_server_alive_p_=1
else
rmacs_server_alive_p_=0
fi
exec cat --squeeze-blank - \
<(tmux list-sessions -F "#{session_name}" | sed "s/^/tmux :: /g") \
<([[ "${rmacs_server_alive_p_}" = 1 ]] \
&& print $(rmacs --name "${rmacs_server_name_}" eval "(~format-opened-files)") | sed 's/^"//; s/"$//; s/^/file :: /g') \
| grep -v -e '^$' \
| run-menu-and-dispatch "Switch to"
4.72. call-wm-menu
- calls the main window manager menu, current window manager is detected automatically
TODO: Help
#!/usr/bin/env tclsh
package require Tclx
set wmName [exec deep-exec wm/get-wm-name]
set menuName [lindex $::argv 0]
switch -exact $wmName {
herbstluftwm {
execl deep-exec [list wm/herbstluft/with-env call-menu $menuName]
}
default {
execl run-menu [list -e "Menu: $menuName not supported"]
}
}
TODO: Add description
#!/usr/bin/env tclsh
package require Tcl 8
package require cmdline
if {[catch [exec report-missing-executables flock util-linux]]} {
exit 1
}
try {
array set cmdArgs [::cmdline::getoptions ::argv {
{max-history.arg 1000 "Maximum number of items stored in the history"}
} {[--max-history <max-history>] <history-file-path>
TODO: Documentation
}]} trap {CMDLINE USAGE} {msg _o} {
if {[llength $::argv] == 0} {
puts $msg
exit 0
} else {
puts stderr $msg
exit 127
}
}
# TODO: Handle errors or missing arguments
set maxHistory $cmdArgs(max-history)
set filePath [lindex $::argv 0]
##############################################################################
# Helpers
##############################################################################
proc slurpAndAdd {path line} {
# Awk is to filter out blank lines and trim spaces
set lines [split [exec echo $line | cat - $path | awk {NF { $1 = $1; print }} | dedup-lines] "\n"]
}
proc readLineFromStdin {} {
gets stdin line
return [string trim $line]
}
proc writeLines {path lines} {
set fd [open $path w]
puts $fd [join $lines "\n"]
close $fd
}
##############################################################################
# Main
##############################################################################
if {![file exists $filePath]} {
set baseDir [file dirname $filePath]
if {![file exists $baseDir]} {
file mkdir $baseDir
}
# Create the empty file
close [open $filePath w]
}
# Make sure the history file is locked
if {!([info exists ::env(_FLOCKER_HISTORY_PATH_)] && $::env(_FLOCKER_HISTORY_PATH_) eq $filePath)} {
package require Tclx
execl flock [list --exclusive $filePath env "_FLOCKER_HISTORY_PATH_=$filePath" $::argv0 {*}$::argv]
}
set newLine [readLineFromStdin]
set newLines [slurpAndAdd $filePath $newLine]
set finalLines [lrange $newLines 0 [expr {$maxHistory - 1}]]
writeLines $filePath $finalLines
puts $newLine
#!/usr/bin/env sh
exec echo "$*" | tr -d -C '[[:alnum:]][[:space:]]'
#!/usr/bin/env bash
dpkg-query -l '*dev' | grep "^.i" | awk '{ print $2 }' | grep "\-dev$"
#!/usr/bin/env bash
usage() {
cat <<EOF
add-deb-repo <sources-repo.list> <dest-repo.list>
Add a Debian-compatible sources.list file to global repository. Should there be a command to run after adding, put it as a comment on the first line of the sources.list file.
EOF
}
if [[ "${1}" = "--help" ]]; then
usage
exit 0
fi
if [[ "$#" -ne 2 ]]; then
usage >&2
exit 1
fi
repo_path_="${1}"
dest_="/etc/apt/sources.list.d/${2}"
with-sudo symlink "${repo_path_}" "${dest_}"
if [[ "$(cat ${repo_path_})" == "#"* ]]; then
eval $(head -1 "${repo_path_}" | cut -d'#' -f2)
fi
#!/usr/bin/env bash
if (test $# -eq 0); then
cat <<EOF
Usage: `basename $0` <port>
Determines if a local TCP port is open. Returns 0 if it is or 1 otherwise.
EOF
fi
report-missing-executables nc Netcat || exit 1
exec nc -z 127.0.0.1 "$1"
#!/usr/bin/env bash
# pgrep lightdm && gdmflexiserver || gnome-screensaver-command -l
xscreensaver-command -lock \
|| gnome-screensaver-command -l \
|| (sh -c "dbus-send --type=method_call --dest=org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver.Lock")
#!/usr/bin/env sh
report-missing-executables date Coreutils tr Coreutils || exit 1
exec date --rfc-3339=second | tr ' ' '_'
#!/usr/bin/env sh
#
# Determines if a file is a symbolic link
#
test -L "$@"
TODO: --help
#!/usr/bin/env bash
#
# Filter broken symlinks from the argument list
#
for file_ in "$@" ; do
if [ ! -e "${file_}" ]; then
echo "${file_}"
fi
done
#!/usr/bin/env tclsh
package require Tcl 8
package require Tclx
package require cmdline
if {[catch {exec report-missing-executables find "GNU Find" sort Coreutils <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{fd stdout}} {
puts $fd {find-deep-path <command>
TODO}
}
proc findExec {cmd} {
set rawPaths [exec echo $::env(PATH) | sed {s/:/\n/g} | sort | uniq]
set res {}
foreach path $rawPaths {
if {[file exists "$path/$cmd"]} {
return "$path/$cmd"
}
}
return {}
}
if {$::argv == "--help"} {
usage
}
if {$::argc != 1} {
usage stderr
exit 1
}
set path [findExec [lindex $::argv 0]]
if {$path eq ""} {
exit 2
} else {
puts $path
}
Prompts a yes/no answer, exiting with code 0 for yes and non-zero for no.
#!/usr/bin/env tclsh
proc getDefaultChoice {choice} {
if {[string equal "" $choice]} {
return "y"
} else {
return $choice
}
}
proc getAnswer {default} {
set answer [string trim [gets stdin]]
if {[string equal "" $answer]} {
return $default
} else {
return $answer
}
}
set prompt [string trim [lindex $argv 0]]
set defaultChoice [getDefaultChoice [string trim [lindex $argv 1]]]
puts -nonewline "$prompt \[y/n\] ($defaultChoice) "
flush stdout
set answer [getAnswer $defaultChoice]
if {[string equal "y" $answer]} {
exit 0
} else {
exit 1
}
Checks whether an executable exists in one of the `PATH`s, returning exit code 0 if it does and 127 otherwise.
#!/usr/bin/env sh
command -v "$@" >/dev/null 2>&1
Reports missing software by checking if their corresponding executables exist. If all executables are found, exit with status 0; otherwise, exit with status 1.
Sample usage:
report-missing-executables aria2c Aria2 wget Wget
# ⇨
# aria2c and wget not found
# Make sure Aria2, Wget are installed
report-missing-executables aria2c Aria2 wget Wget curl cURL
# ⇨
# aria2c, curl, and wget not found
# Make sure Aria2, Wget, cURL are installed
report-missing-executables aria2c Aria2 wget
# ⇨
# Invalid arguments. Number of arguments must be even.
#!/usr/bin/env tclsh
proc showHelp {} {
puts {Usage:
report-missing-executables <exec-1> <prog-1> ...
report-missing-executables --help
Reports missing software by checking if their corresponding executables exist.
If all executables are found, exit with status 0; otherwise, exit with status
1.
E.g.
report-missing-executables aria2c Aria2 wget Wget
# aria2c and wget not found
# Make sure Aria2, Wget are installed
report-missing-executables aria2c Aria2 wget Wget curl cURL
# aria2c, curl, and wget not found
# Make sure Aria2, Wget, cURL are installed
report-missing-executables aria2c Aria2 wget
# Invalid arguments. Number of arguments must be even.}
}
if {$::argc == 0 || $::argv eq {--help}} {
showHelp
exit 0
}
if {$::argc % 2 == 1} {
puts stderr "ERROR: Invalid arguments. Number of arguments must be even."
exit 2
}
proc notifyMissingExecs {execs} {
set execStr [join $execs ", "]
exec notify-send --urgency=critical "Missing Software" "Missing $execStr" <@ stdin >@ stdout 2>@ stderr
}
set missingExecs {}
set missingApps {}
for {set i 0} {$i < $::argc} {incr i 2} {
set execName [lindex $::argv $i]
set appName [lindex $::argv [expr {$i + 1}]]
if {[catch {exec get-exec $execName > /dev/null 2> /dev/null}]} {
set missingExecs [list {*}$missingExecs $execName]
set missingApps [list {*}$missingApps $appName]
}
}
switch [llength $missingApps] {
0 {
exit 0
}
1 {
puts stderr "$missingExecs not found"
puts stderr "Make sure $missingApps is installed"
notifyMissingExecs $missingExecs
}
default {
set execStr [join $missingExecs ", "]
set appStr [join $missingApps ", "]
puts stderr "$execStr not found"
puts stderr "Make sure $appStr are installed"
notifyMissingExecs $missingExecs
}
}
#!/usr/bin/env 9-rc
#
# Prefixes all lines read from stdin.
#
prefix=$1 {
if (test $#prefix -eq 0) {
prefix='# '
}
sed 's/^/'^$prefix^'/g'
}
#!/usr/bin/env 9-rc
#
# Prefixes all lines read from stdin.
#
suffix=$1 {
sed 's/$/'^$suffix^'/g'
}
The password is printed to stdout without an end-of-line character.
TODO: Help text
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables kwalletcli KWallet-CLI >@ stdout 2>@ stderr <@ stdin}]} {
exit 1
}
set folder Passwords
set entry [lindex $::argv 0]
if {[catch {exec kwalletcli -f $folder -e $entry >@ stdout <@ stdin}]} {
# Password doesn't exist
exec kwalletcli_getpin -p "Set password" -t "Password not yet set. Please set it now" -Y "_Set" | kwalletcli -f $folder -e $entry -P
exec kwalletcli -f $folder -e $entry >@ stdout <@ stdin
}
#!/usr/bin/env 9-rc
report-missing-executables \
tempfile "tempfile utility" \
qrencode Qrencode \
|| exit 1
# FIXME: Not working
if (test $#* -eq 0) {
echo No argument found
}
tmpfile=`{tempfile}^.png
qrencode -o $tmpfile -s 5 $*
do-notify-short $tmpfile' created'
display $tmpfile
#!/usr/bin/env bash
echo "-> Starting HDDTemp if necessary"
nc localhost 7634 &>/dev/null || (
exec sudo hddtemp -d /dev/sda
)
echo ""
echo "-> HDD temperature"
nc localhost 7634
echo "-> CPU temperature"
sensors
TODO: Remove due to not being reliable
#!/usr/bin/env sh
#
# Determines if a process is running using pgrep.
#
exec pgrep "$@" &>/dev/null
#!/usr/bin/env sh
report-missing-executables xset x11-xserver-utils
exec xset q >/dev/null 2>&1
#!/usr/bin/env bash
report-missing-executables key-mon key-mon || exit 1
key-mon --decorated --meta --theme modern "$@"
TODO: Help text
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables sudo sudo <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
package require Tclx
if {[lsearch $::argv "-"] == -1} {
set args [list "-" {*}$::argv]
} else {
set args $::argv
}
set sudoArgs {}
set cmd {}
set beforeDash 1
foreach arg $args {
if {$arg eq "-i"} {
execl sudo [list -i]
}
if {$arg eq "-"} {
set beforeDash 0
continue
}
if {$beforeDash == 1} {
lappend sudoArgs $arg
} else {
lappend cmd $arg
}
}
execl sudo [list {*}$sudoArgs -E env "PATH=$::env(PATH)" {*}$cmd]
TODO: Help text
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables sudo sudo query-sudo-password query-sudo-password <@ stdin >@ stdout 2>@ stderr}]} {
exit 1
}
package require Tclx
if {[lsearch $::argv "-"] == -1} {
set args [list "-" {*}$::argv]
} else {
set args $::argv
}
set sudoArgs {}
set cmd {}
set beforeDash 1
foreach arg $args {
if {$arg eq "-i"} {
puts stderr "ERROR: Cannot run interactive sudo (-i) with this command"
exit 2
}
if {$arg eq "-"} {
set beforeDash 0
continue
}
if {$beforeDash == 1} {
lappend sudoArgs $arg
} else {
lappend cmd $arg
}
}
set fd [open [list | query-sudo-password Sandwich] r]
set password [read $fd]
close $fd
exec echo $password | with-sudo -k --stdin --prompt "" {*}$sudoArgs - {*}$cmd <@ stdin >@ stdout 2>@ stderr
TODO Group all shell utils in one section
TODO Help text
#!/usr/bin/env sh
cd "${1}"
shift
exec "$@"
#!/usr/bin/env tclsh
proc usage {{outFD stdout}} {
puts $outFD {get-all-x-displays
Returns all Xorg displays.}
}
if {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set ::X11_SOCKET_PATH /tmp/.X11-unix
if {[file exists $::X11_SOCKET_PATH]} {
set ::DISPLAYS [string trim [exec ls $::X11_SOCKET_PATH | tr 'X' ':' <@ stdin]]
foreach display $::DISPLAYS {
puts $::display
}
}
#!/usr/bin/env tclsh
proc usage {{outFD stdout}} {
puts $outFD {get-all-logged-in-x-displays
Returns all Xorg displays of logged in users.}
}
if {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set ::X11_SOCKET_PATH /tmp/.X11-unix
if {[file exists $::X11_SOCKET_PATH]} {
set ::DISPLAYS [string trim [exec w | awk {{ print $3 }} | grep -e {^:} | sort | uniq <@ stdin]]
foreach display $::DISPLAYS {
puts $::display
}
}
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables get-all-x-displays get-all-x-displays >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{outFD stdout}} {
puts $outFD {with-all-x-displays <command> [args...]
Run a command with all Xorg displays.}
}
if {$::argc == 0} {
usage stderr
exit 1
} elseif {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set displays [string trim [exec get-all-x-displays <@ stdin]]
foreach display $displays {
exec env "DISPLAY=$display" {*}$::argv <@ stdin >@ stdout 2>@ stderr
}
#!/usr/bin/env tclsh
if {[catch {exec report-missing-executables get-all-logged-in-x-displays get-all-logged-in-x-displays >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{outFD stdout}} {
puts $outFD {with-all-logged-in-x-displays <command> [args...]
Run a command with all Xorg displays for logged in users.}
}
if {$::argc == 0} {
usage stderr
exit 1
} elseif {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set displays [string trim [exec get-all-logged-in-x-displays <@ stdin]]
foreach display $displays {
exec env "DISPLAY=$display" {*}$::argv <@ stdin >@ stdout 2>@ stderr
}
#!/usr/bin/env tclsh
# TODO: Doc: Works with all DISPLAYs
# TODO: Doc: One answer is enough
if {[catch {exec report-missing-executables zenity Zenity get-all-x-displays get-all-x-displays >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{outFD stdout}} {
puts $outFD {prompt-gui <question>
Prompts a yes/no answer in all current displays and exits as soon as an answer is received. Returns 0 in case of positive answer and 1 otherwise.}
}
if {$::argc == 0} {
usage stderr
exit 1
} elseif {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set ::QUESTION [join $argv " "]
set ::DISPLAYS [exec get-all-x-displays <@ stdin]
set ::THREADS {}
package require Tcl 8.4
package require Thread 2.8
foreach display $::DISPLAYS {
set t [thread::create {
package require BLT
package require Thread
proc exitNow args {
global ::EXIT_STATUS, ::MAIN_THREAD_ID
lassign $::EXIT_STATUS _ _ exitCode _
thread::send -async $::MAIN_THREAD_ID [list set ::ANSWER $exitCode]
}
trace add variable ::EXIT_STATUS write exitNow
thread::wait
}]
thread::send $t [list set ::MAIN_THREAD_ID [thread::id]]
thread::send -async $t [list blt::bgexec ::EXIT_STATUS zenity --question "--text=$::QUESTION" --display=$display]
lappend ::THREADS $t
}
vwait ::ANSWER
set ::SAVED_ANSWER $::ANSWER
foreach t $::THREADS {
if {[thread::exists $t]} {
thread::send $t [list set ::EXIT_STATUS {0 0 0 0}]
thread::cancel $t
thread::release $t
}
}
exit [expr {($::SAVED_ANSWER == 1)}]
4.104. prompt-y-n-gui-all-logged-in-displays
- prompts a yes/no answer in all current displays for logged in users
#!/usr/bin/env tclsh
# TODO: Doc: Works with all DISPLAYs
# TODO: Doc: One answer is enough
if {[catch {exec report-missing-executables zenity Zenity get-all-x-displays get-all-x-displays >@ stdout 2>@ stderr}]} {
exit 1
}
proc usage {{outFD stdout}} {
puts $outFD {prompt-gui <question>
Prompts a yes/no answer in all current displays for all logged in users and exits as soon as an answer is received. Returns 0 in case of positive answer and 1 otherwise.}
}
if {$::argc == 0} {
usage stderr
exit 1
} elseif {[lindex $::argv 0] eq "--help"} {
usage
exit 0
}
set ::QUESTION [join $argv " "]
set ::DISPLAYS [exec get-all-logged-in-x-displays <@ stdin]
set ::THREADS {}
package require Tcl 8.4
package require Thread 2.8
foreach display $::DISPLAYS {
set t [thread::create {
package require BLT
package require Thread
proc exitNow args {
global ::EXIT_STATUS, ::MAIN_THREAD_ID
lassign $::EXIT_STATUS _ _ exitCode _
thread::send -async $::MAIN_THREAD_ID [list set ::ANSWER $exitCode]
}
trace add variable ::EXIT_STATUS write exitNow
thread::wait
}]
thread::send $t [list set ::MAIN_THREAD_ID [thread::id]]
thread::send -async $t [list blt::bgexec ::EXIT_STATUS zenity --question "--text=$::QUESTION" --display=$display]
lappend ::THREADS $t
}
vwait ::ANSWER
set ::SAVED_ANSWER $::ANSWER
foreach t $::THREADS {
if {[thread::exists $t]} {
thread::send $t [list set ::EXIT_STATUS {0 0 0 0}]
thread::cancel $t
thread::release $t
}
}
exit [expr {($::SAVED_ANSWER == 1)}]
Collection of executables that get triggered in case of an event
4.105.1. event/watch-change <timeout> <get-state> - <trigger>…
- watches for a change (= 2 different states returned by <get-state>
) and triggers a script
TODO: Help text and example
#!/usr/bin/env tclsh
proc slurp {cmds} {
set fd [open [list | {*}$cmds] r]
set data [read $fd]
close $fd
return $data
}
proc lpop {listVar} {
upvar 1 $listVar l
set res [lindex $l 0]
set l [lreplace $l [set l 0] 0]
return $res
}
proc takeUntil {listVar element} {
upvar 1 $listVar l
set res {}
set x [lpop l]
while {$x ne $element && $x ne ""} {
lappend res $x
set x [lpop l]
}
return $res
}
set ::TIMEOUT [lpop ::argv]
set ::GET_STATE_CMD [takeUntil ::argv "-"]
set ::TRIGGER_CMD [lrange $::argv 0 end]
# puts "$::TIMEOUT"
# puts "$::GET_STATE_CMD"
# puts "$::TRIGGER_CMD"
# exit 0
if {$::TIMEOUT eq "" || $::GET_STATE_CMD eq "" || $::TRIGGER_CMD eq ""} {
puts stderr "ERROR: Timeout, get-state, and trigger must exist"
exit 2
}
set state [slurp $::GET_STATE_CMD]
while {1} {
after $::TIMEOUT
set newState [slurp $::GET_STATE_CMD]
if {$newState ne $state} {
catch {exec {*}$::TRIGGER_CMD <@ stdin >@ stdout 2>@ stderr}
}
set state $newState
}
#!/usr/bin/env tclsh
# /sys/class/drm/*/status
proc slurp {cmds} {
set fd [open [list | {*}$cmds] r]
set data [read $fd]
close $fd
return $data
}
proc getCurrentState {} {
set paths [glob /sys/class/drm/*/status]
set statuses [slurp [list cat {*}$paths]]
return "{$paths} {$statuses}"
}
puts [getCurrentState]
4.106. sudo-askpass
- runs sudo
with a graphical askpass program, also preserving some environment variables
#!/usr/bin/env sh
if ! sudo --help >/dev/null 2>&1; then
echo "sudo not found, please install sudo" >&2
exit 1
fi
export SUDO_ASKPASS=${SUDO_ASKPASS:-$(which ssh-askpass)}
export DISPLAY=${DISPLAY:-":0"}
exec sudo --askpass -tt -E env "PATH=${PATH}" "$@"
#!/usr/bin/env sh
# exec sudo pm-suspend && lockscreen
# exec sudo pm-suspend
exec systemctl suspend
#!/usr/bin/env 9-rc
#
# Takes all lines from a pattern (representing by $1), using GNU Awk.
#
gawk 'BEGIN {
found = 0
}
/'^$1^'/ {
found = 1
}
{
if (found == 1) {
print $0
}
}'
#!/usr/bin/env 9-rc
#
# Takes the first $1 lines using Plan 9's seq.
#
if (test $#* -eq 0) {
n_lines=1
}
if not {
n_lines=$1
}
sed $n_lines^q
#!/usr/bin/env bash
wget \
--recursive \
--no-clobber \
--page-requisites \
--html-extension \
--convert-links \
--timestamping \
--no-parent \
--mirror \
"$@"
#
# --recursive download the entire Web site.
# --domains website.org don't follow links outside website.org.
# --no-parent don't follow links outside the directory tutorials/html/.
# --page-requisites get all the elements that compose the page (images, CSS and so on).
# --html-extension save files with the .html extension.
# --convert-links convert links so that they work locally, off-line.
# --no-clobber don't overwrite any existing files (used in case the download is interrupted and
# resumed).
# --mirror create mirror
#
#!/usr/bin/env sh
exec date +'%A, %d %b %Y, %H:%M:%S %p'
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
wmctrl -m | grep 'Name: ' | cut -d' ' -f2-
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
exec wmctrl -lx \
| awk '{ for (i = 5; i <= NF; i++) printf $i" "; print "::", $3, "::", $1 }'
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
exec wmctrl -d \
| awk '{ print $NF }' \
| sort | uniq
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
exec wmctrl -d | awk '{ if ($2 == "*" ) print $NF }'
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
client_=$(pprint-client-list | run-menu -dmenu -p "Client" 2>/dev/null)
if [ -n "${client_}" ]; then
exec dispatch-action 'copy!'"${client_}"
fi
#!/usr/bin/env sh
report-missing-executables wmctrl wmctrl || exit 1
client_id_=$(pprint-client-list | run-menu -dmenu -p "Client" 2>/dev/null | awk '{ print $NF }')
if [ -n "${client_id_}" ]; then
exec dispatch-action 'copy!'"${client_id_}"
fi
#!/usr/bin/env zsh
report-missing-executables wmctrl wmctrl || exit 1
cat --squeeze-blank \
<(pprint-client-list | awk '{ print "wind ::", $0 }') \
<(pprint-desktop-list | awk '{ print "desktop ::", $0 }') \
| grep -v -e '^$' \
| call-omni-switcher-stdin
#!/usr/bin/env zsh
report-missing-executables wmctrl wmctrl || exit 1
cat --squeeze-blank \
<(pprint-desktop-list | awk '{ print "desktop ::", $0 }') \
| grep -v -e '^$' \
| run-menu-and-dispatch
TODO: assert argv
#!/usr/bin/env tclsh
package require Tclx
set menuName [lindex $::argv 0]
catch {puts "(popup-menu [exec $menuName-menu 2>@ stderr])"} err opts
if {[dict get $opts -code] != 0} {
set errStr "ERROR: Failed to get menu (menuName=$menuName): $err"
puts stderr $errStr
execl run-menu [list -e $errStr]
}
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(write '(("[_x] Execute client command" wm-exec-client-command)
("[_r] Reload" wm-reload)
("[_s] Restart" wm-wmexec)
("[_q] Quit" wm-quit)))
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(write '(("[_a] Config input" app-config-input)
("[_m] Terminal emulator" app-term-emu)
("[_c] Clipboard" app-clipboard)
("[_w] Web browser" app-web-browser)
("[_o] Edit TODOs" app-todo-editor)
("[_t] Open Toolbox" app-toolbox)
()
("[_l] Lock screen" app-lock-screen)
("[_s] Suspend" app-suspend)))
4.111.12. herbstluft/
- utils to manage HerbstluftWM
TODO: Document on a menu-driven/guiding UX
#!/usr/bin/env bash
exec enrich-path wm herbstluft menus - "$@"
TODO: Report errors after catch
#!/usr/bin/env tclsh
package require Tclx
set menuName [lindex $::argv 0]
if {[catch {set action [exec get-menu $menuName 2> /dev/null | run-context-menu]} err]} {
execl run-menu [list -e "Error when getting menu (menu=$menuName): $err"]
}
proc stripPrefix {text prefix} {
return [string range $text [string length $prefix] end]
}
proc callHC {args} {
return [exec herbstclient {*}$args]
}
proc getTagList {} {
return [split [string trim [callHC object_tree tags.by-name | tail -n +2 | awk '{ print $NF }']] "\n"]
}
proc getCurrentTag {} {
return [exec get-desktop-name]
}
switch -glob $action {
"main-menu-call-*" {
set menuType [stripPrefix $action "main-menu-call-"]
execl call-menu [list $menuType]
}
"wm-*" {
set cmd [stripPrefix $action "wm-"]
if {$cmd eq "exec-client-command"} {
catch {
set cmd [string trim [callHC list_commands | xargs -I{} echo "{}" | sort | run-menu -dmenu -p "Herbstluft command"]]
set output "$cmd output:\n[callHC {*}$cmd]"
puts -nonewline $output
execl run-menu [list -e $output]
}
} else {
callHC $cmd
}
}
"split-frame-*" {
set direction [stripPrefix $action "split-frame-"]
callHC split $direction
switch $direction {
"left" {
callHC focus left
}
"right" {
callHC focus right
}
"top" {
callHC focus up
}
"bottom" {
callHC focus down
}
}
}
"set-frame-layout-*" {
set layout [stripPrefix $action "set-frame-layout-"]
callHC set_layout $layout
}
"remove-frame" {
callHC remove
}
"app-*" {
set appName [stripPrefix $action "app-"]
switch $appName {
term-emu {
callHC spawn x-terminal-emulator
}
toolbox {
# TODO: Use env var
callHC spawn ffn /m/toolbox/Toolbox
}
todo-editor {
# TODO: Use env var
callHC spawn ffn /m/toolbox/TODO.org
}
config-input {
callHC spawn config-inputs-cmpitg
}
clipboard {
callHC spawn display-clipboard
}
lock-screen {
callHC spawn lockscreen
}
suspend {
callHC spawn suspend-me
}
}
}
"toggle-focused-client-*" {
set attr [stripPrefix $action "toggle-focused-client-"]
switch $attr {
floating {
callHC attr clients.focus.floating toggle
}
fullscreen {
# callHC fullscreen toggle
execl $::env(HOME)/.guix-profile/share/doc/herbstluftwm/examples/maximize.sh
}
}
}
"move-client-to-monitor-*" {
set direction [stripPrefix $action "move-client-to-monitor-"]
switch $direction {
up {
set arg "-u"
}
down {
set arg "-d"
}
left {
set arg "-l"
}
right {
set arg "-r"
}
}
callHC shift_to_monitor $arg ""
}
"move-client-*" {
set direction [stripPrefix $action "move-client-"]
callHC shift $direction
}
"bring-client-here" {
set clientID [lindex $::argv 1]
callHC bring $clientID
}
"jump-to-client" {
set clientID [lindex $::argv 1]
callHC jumpto $clientID
}
"untag-focused-client" {
callHC move default
}
"bring-tag-here" {
set tagName [lindex $::argv 1]
callHC use $tagName
}
"kill-tag" {
set tagName [lindex $::argv 1]
callHC merge_tag $tagName [getCurrentTag]
}
"add-tag" {
catch {
set tagName [string trim [exec yad {--title=Add tag} --entry {--entry-label=Tag name} 2>/dev/null]]
if {$tagName ne ""} {
callHC add $tagName
}
}
}
"rename-current-tag" {
catch {
set tagName [string trim [exec yad {--title=Rename tag} --entry {--entry-label=New name} 2>/dev/null]]
if {$tagName ne ""} {
callHC rename [getCurrentTag] $tagName
}
}
}
"kill-current-tag" {
callHC merge_tag [getCurrentTag] default
}
"()" {}
default {
execl run-menu [list -e "Unknown action=$action"]
}
}
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(write '(("[_r] Rename" rename-current-tag)
("[_k] Kill" kill-current-tag)))
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(write '(("[_f] Toggle fullscreen" toggle-focused-client-fullscreen)
("[_o] Toggle floating" toggle-focused-client-floating)
()
("[_n] Untag" untag-focused-client)
()
("[_u] Move up" move-client-up)
("[_d] Move down" move-client-down)
("[_l] Move left" move-client-left)
("[_r] Move right" move-client-right)
()
("[_a] Move to monitor above" move-client-to-monitor-up)
("[_b] Move to monitor below" move-client-to-monitor-down)
("[_e] Move to left monitor" move-client-to-monitor-left)
("[_i] Move to right monitor" move-client-to-monitor-right)))
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(write '(("[_u] Split (up)" split-frame-top)
("[_d] Split (down)" split-frame-bottom)
("[_l] Split (left)" split-frame-left)
("[_r] Split (right)" split-frame-right)
("[_a] Split (auto)" split-frame-auto)
()
("[_m] Layout max" set-frame-layout-max)
("[_v] Layout vertical" set-frame-layout-vertical)
("[_h] Layout horizontal" set-frame-layout-horizontal)
()
("[_k] Kill" remove-frame)))
#!/usr/bin/env -S guile -s
# -*- mode: scheme -*-
!#
(use-modules (ice-9 popen))
(define (read-output cmd)
(let* ((port (open-input-pipe cmd))
(data (read port)))
(close-pipe port)
data))
(write `(("[_g] Add tag" add-tag)
()
("[_a] App" . ,(read-output "app-menu"))
("[_f] Frame" . ,(read-output "frame-menu"))
("[_c] Focused client" . ,(read-output "focused-client-menu"))
("[_t] Current tag" . ,(read-output "current-tag-menu"))
()
("[_w] WM" . ,(read-output "wm-menu"))))
TODO: Review
TODO: Help text
#!/usr/bin/env bash
report-missing-executables Xephyr Xephyr || exit 1
resolution_=${RESOLUTION:-800x600}
Xephyr \
-ac \
-br \
-noreset \
-screen ${resolution_} \
:1 "$@" >/dev/null & disown
# export DISPLAY=:1.0
echo Display: $DISPLAY
#!/usr/bin/env bash
report-missing-executables zenity Zenity || exit 1
zenity --file-selection --filename `pwd` "$@" 2>/dev/null
#!/usr/bin/env dash
exec "${EDITOR}" $(which "${1}") "@$"
compute-checksum-url <checksum-tool> <uri> [curl-options] …
E.g.
-
Compute MD5 sum for https://picsum.photos/200:
compute-checksum-url md5sum https://picsum.photos/200
-
Compute SHA1 sum for https://picsum.photos/200:
compute-checksum-url sha1sum https://picsum.photos/200
#!/usr/bin/env sh
checksum_tool_="${1}"
shift
report-missing-executables curl cURL "${checksum_tool_}" "${checksum_tool_}" || exit 1
exec curl --silent --location "$@" | "${checksum_tool_}" | cut -d ' ' -f 1
Downloads a file, compares checksums, and prompts overwriting when necessary. This script returns the download destination as the last line upon a successful download with exit code 0. In case of error or checksums not matching, a non-zero exit code is returned. This script leverages the MD5 sum, SHA1 sum, SHA256 sum from Coreutils.
E.g.
-
Download to a temporary file:
safe-download https://picsum.photos/200
-
Download to
/tmp/aoeu
, prompt overwriting:safe-download --destination /tmp/aoeu https://picsum.photos/200
-
If the current MD5 sum of
/tmp/aoeu
doesn’t matchABC
, download and overwrite it, then compute and the check the MD5 sum for the downloaded file; otherwise, skip the download:safe-download --destination /tmp/aoeu --md5sum ABC https://picsum.photos/200
-
Same as the previous example except that the SHA1 sum is taken into account along with the MD5 sum:
safe-download --destination /tmp/aoeu --md5sum ABC --sha1sum DEF https://picsum.photos/200
#!/usr/bin/env tclsh
package require Tcl 8.2
package require fileutil
try {
array set args [::cmdline::getoptions ::argv {
{destination.arg "" "(optional) The download destination"}
{md5sum.arg "" "(optional) The MD5 checksum"}
{sha1sum.arg "" "(optional) The SHA1 checksum"}
{sha256sum.arg "" "(optional) The SHA256 checksum"}
{sha512sum.arg "" "(optional) The SHA512 checksum"}
} {[options] <uri>
Download a file, compare checksum if necessary, and return the path to the downloaded file. If the download destination is not specified, a temporary path is returned.
}]
} trap {CMDLINE USAGE} {msg _o} {
puts $msg
exit 0
}
if {[catch {exec report-missing-executables aria2c Aria2c >@ stdout 2>@ stderr}]} {
exit 1
}
proc getDestination {dest} {
if {$dest eq ""} {
set path [::fileutil::tempfile]
file delete -- $path
return $path
} else {
return $dest
}
}
proc getURI {uri} {
if {$uri eq ""} {
puts stderr "ERROR: Missing URI"
exit 1
} else {
return $uri
}
}
proc checksumsMatched {path checksumDict} {
proc checksumMatched {path type checksum} {
set checksum [string toupper $checksum]
if {$checksum eq ""} {
return 1
} else {
switch $type {
md5 {
return [expr {$checksum eq [string toupper [exec md5sum $path | cut -f1 -d " "]]}]
}
sha1 {
return [expr {$checksum eq [string toupper [exec sha1sum $path | cut -f1 -d " "]]}]
}
sha256 {
return [expr {$checksum eq [string toupper [exec sha256sum $path | cut -f1 -d " "]]}]
}
sha512 {
return [expr {$checksum eq [string toupper [exec sha512sum $path | cut -f1 -d " "]]}]
}
default {
return 1
}
}
}
}
return [expr {[checksumMatched $path md5 [dict get $checksumDict md5sum]]
&& [checksumMatched $path sha1 [dict get $checksumDict sha1sum]]
&& [checksumMatched $path sha256 [dict get $checksumDict sha256sum]]
&& [checksumMatched $path sha512 [dict get $checksumDict sha512sum]]}]
}
proc downloadFile {destDir destFile uri} {
return [exec aria2c --dir $destDir --out $destFile $uri >@ stdout 2>@ stderr]
}
set dest [getDestination $args(destination)]
set destDir [file dirname $dest]
set destFile [file tail $dest]
set uri [getURI [lindex $::argv 0]]
set checksumDict [dict create md5sum $args(md5sum) sha1sum $args(sha1sum) sha256sum $args(sha256sum) sha512sum $args(sha512sum)]
if {[lindex [array get ::env VERBOSE] 1] == 1} {
# Be verbose
parray args
puts "Destination: $dest"
puts "URI: $uri"
}
if {[file exists $dest]} {
if {[dict values $checksumDict] == {{} {} {}}} {
try {
exec prompt-y-n "$dest exists, would you like continue and overwrite it?" y <@ stdin >@ stdout 2>@ stderr
file delete -- $dest
downloadFile $destDir $destFile $uri
} trap CHILDSTATUS {_msg _options} {}
} elseif {![checksumsMatched $dest $checksumDict]} {
file delete -- $dest
downloadFile $destDir $destFile $uri
}
} else {
downloadFile $destDir $destFile $uri
}
if {![checksumsMatched $dest $checksumDict]} {
exit 1
} else {
puts $dest
}
# Local Variables:
# indent-tabs-mode: t
# End:
#!/usr/bin/env bash
length_=${1:-32}
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${length_} | head -n 1
TODO: docstring
#!/usr/bin/env bash
set -o nounset
DICT_PATH=${DICT_PATH:-/usr/share/dict/words}
counter_=${1:-1}
rig -c $(( (${counter_} + 1) / 2 )) | awk 'NR == 1 || NR % 5 == 1' | tr ' ' '\n' | head -${counter_} | paste --serial --delimiters=' ' -
TODO: docstring
#!/usr/bin/env sh
counter_="${1:-4}"
separator_="${2:--}"
exec normalize-filename $(gen-names ${counter_}) | downcase | tr ' ' "${separator_}"
#!/usr/bin/env tclsh
package require Tclx
# TODO: matching -h/--help
# TODO: help text
# TODO: command line arguments
## include::tcl-helpers
##############################################################################
# Helpers
##############################################################################
proc execCmd {args} {
return [exec {*}$args <@ stdin >@ stdout 2>@ stderr]
}
proc getRunsvdirLogContent {servicePath} {
return [format {#!/usr/bin/env sh
mkdir -p %s/log/main
exec svlogd -ttt %s/log/main
} $servicePath $servicePath]
}
proc getRunsvdirContent {user userDaemonEnableDir} {
return [format {#!/usr/bin/env sh
exec 2>&1
exec sudo -H -E -u %s runsvdir -P %s
} $user $userDaemonEnableDir]
}
proc createExecutableAsRoot {path content} {
if {[file exists $path]} {
puts -nonewline "$path exists, remove it? \[y/N\] "
flush stdout
set answer [string tolower [string trim [gets stdin]]]
if {$answer eq "y"} {
execCmd with-sudo rm -rvI $path
} else {
exit 0
}
}
file tempfile tempFile /tmp/tempfile
set f [open $tempFile w]
puts $f $content
close $f
execCmd with-sudo mv $tempFile $path
execCmd with-sudo chmod +x $path
}
##############################################################################
# Main
##############################################################################
if {$argc == 0} {
set user $::env(USER)
} else {
set user [lindex $argv 0]
}
if {[info exists ::env(MY_DAEMON_DIR)]} {
set userDaemonDir $::env(MY_DAEMON_DIR)
} else {
set userDaemonDir /home/$user/daemon
}
set userDaemonEnableDir $userDaemonDir/enabled
set userDaemonAvailableDir $userDaemonDir/available
set serviceName runsvdir-$user
set servicePath /etc/sv/$serviceName
puts "User: $user"
puts "Supervisor service path: $servicePath"
puts "User service path (available): $userDaemonAvailableDir"
puts "User service path (enabled): $userDaemonEnableDir"
puts ""
execCmd with-sudo mkdir -p $servicePath/log/main
file mkdir $userDaemonAvailableDir
file mkdir $userDaemonEnableDir
createExecutableAsRoot $servicePath/run [getRunsvdirContent $user $userDaemonEnableDir]
createExecutableAsRoot $servicePath/log/run [getRunsvdirLogContent $servicePath]
exec with-sudo symlink $servicePath /etc/service/$serviceName
puts "Done, please restart the Runit service"
#!/usr/bin/env tclsh
# TODO: matching -h/--help
# TODO: help text
set serviceName [lindex $argv 0]
if {[info exists ::env(MY_DAEMON_DIR)]} {
set userDaemonPath $::env(MY_DAEMON_DIR)
} else {
set userDaemonPath [file normalize ~/daemon]
}
set servicePath [glob -nocomplain $userDaemonPath/available/$serviceName]
set enablePath $userDaemonPath/enabled/$serviceName
if {$servicePath eq ""} {
puts stderr "ERROR: $servicePath doesn't exist"
exit 1
}
exec symlink $servicePath $enablePath <@ stdin >@ stdout 2>@ stderr
#!/usr/bin/env tclsh
# TODO: matching -h/--help
# TODO: help text
set serviceName [lindex $argv 0]
if {[info exists ::env(MY_DAEMON_DIR)]} {
set userDaemonPath $::env(MY_DAEMON_DIR)
} else {
set userDaemonPath [file normalize ~/daemon]
}
set enablePath [glob -nocomplain $userDaemonPath/enabled/$serviceName]
if {$enablePath ne ""} {
file delete $enablePath
}
#!/usr/bin/env tclsh
# TODO: matching -h/--help
# TODO: help text
# TODO: command line arguments
if {$::env(USER) eq "root"} {
set supervisedPath /etc/service
} else {
if {[info exists ::env(MY_DAEMON_DIR)]} {
set supervisedPath $::env(MY_DAEMON_DIR)/enabled
} else {
set supervisedPath [file normalize ~/daemon/enabled]
}
}
foreach dir [glob -nocomplain $supervisedPath/*] {
puts [file tail $dir]
}
#!/usr/bin/env tclsh
package require Tclx
# TODO: matching -h/--help
# TODO: help text
# TODO: command line arguments
set serviceName [lindex $::argv 0]
set logFile [lindex $::argv 1]
if {$::env(USER) eq "root"} {
set supervisedPath /etc/service
} else {
if {[info exists ::env(MY_DAEMON_DIR)]} {
set supervisedPath $::env(MY_DAEMON_DIR)/enabled
} else {
set supervisedPath [file normalize ~/daemon/enabled]
}
}
execl tail [list "-f" $supervisedPath/$serviceName/log/main/$logFile]
TODO: Help text
#!/usr/bin/env tclsh
set execFile [string trim $::argv]
set paths [split $::env(PATH) ":"]
foreach path $paths {
set possibleExec "$path/$execFile"
if {[file exists $possibleExec] && [file isfile $possibleExec] && [file executable $possibleExec]} {
puts $possibleExec
exit 0
}
}
exit 2
TODO: Help text
#!/usr/bin/env tclsh
set execFile [string trim $::argv]
set paths [split $::env(PATH) ":"]
foreach path $paths {
set possibleExec "$path/$execFile"
if {[file exists $possibleExec]} {
puts $possibleExec
exit 0
}
}
exit 2
TODO: Help text
#!/usr/bin/env sh
exec_path_=$(get-exec "$1")
shift 1
if [ "${exec_path_}" = "" ]; then
echo "Error: $1 not found" >&2
exit 1
fi
exec "${exec_path_}" "$@"
TODO: Help text
#!/usr/bin/env tclsh
package require Tcl 8
set input [read stdin]
set fd [file tempfile tempPath]
try {
puts -nonewline $fd $input
close $fd
file attribute $tempPath -permissions u+x
puts [exec $tempPath]
} finally {
file delete $tempPath
}
TODO: Help text
#!/usr/bin/env expect
log_user 0
spawn {*}$::argv
interact
TODO: Help text
TODO: Note on the rationale of this executable - To modularize scripting/shelling
TODO: Thinking wm wm/herbstluft wm/herbstluft/menus
vs. wm herbstluft menus
#!/usr/bin/env tclsh
package require Tclx
# TODO: Check argv length
# TODO: Help text
# TODO: Order of args - which overrides which
# TODO: The newly construct paths are prepended
##############################################################################
# Helper
##############################################################################
proc addSuffixToPaths {paths suffix} {
if {[string match "/*" $suffix]} {
return [list $suffix]
}
set res {}
foreach e $paths {
set newPath "$e/$suffix"
if {[file exists $newPath]} {
lappend res $newPath
}
}
return $res
}
##############################################################################
# Main
##############################################################################
# Mark the end of the list of envs if needed
if {[lsearch $::argv "-"] == -1} {
set addedPaths [lrange $::argv 0 0]
set args [lrange $::argv 1 end]
} else {
set index [lsearch $::argv "-"]
set addedPaths [lrange $::argv 0 [expr {$index - 1}]]
set args [lrange $::argv [expr {$index + 1}] end]
}
set paths [split [string trim [exec echo ".:$::env(PATH)" | awk -v RS=: {!($0 in a) {a[$0]; print}}]] "\n"]
foreach suffix $addedPaths {
set pathsWithSuffix [addSuffixToPaths $paths $suffix]
set paths [list {*}$pathsWithSuffix {*}$paths]
}
set ::env(PATH) [join $paths ":"]
if {[lindex $args 0] ne ""} {
execl [lindex $args 0] [lrange $args 1 end]
}
#!/usr/bin/env tclsh
package require Tclx
# TODO: Check argv length
# TODO: Help text
set envName [lindex $::argv 0]
set args [lrange $::argv 1 end]
proc addSuffixToPaths {paths suffix} {
set res {}
foreach e $paths {
lappend res "$e$suffix"
}
return [list {*}$res {*}$paths]
}
switch $envName {
pure {
execl with-env-pure $args
}
user -
u {
execl with-env-user $args
}
python -
conda -
py {
execl with-conda $args
}
default {
set paths [split $::env(PATH) ":"]
set newPaths [addSuffixToPaths $paths "/$envName"]
set ::env(PATH) [join $newPaths ":"]
execl [lindex $args 0] [lrange $args 1 end]
}
}
6.2. i3-switch-window
- window switcher for i3
Requirement: dmenu
.
#!/usr/bin/env python3
#
# Copyright (C) 2015-2016 Ha-Duong Nguyen <cmpitg@gmail.com>
#
# i3-switch-window is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# i3-switch-window is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# i3-switch-window. If not, see <http://www.gnu.org/licenses/>.
#
#
# Requirements:
# Python 3
# dmenu with Xft patch
#
import json
import subprocess
import sys
# dmenu_options = '-b -i -l 40 -fn "Noto Sans-10" -nf "#ffa077" -nb "#202020"'
dmenu_options = '-p Window -i -l 40 -fn "Noto Sans-10" -nf "#ffa077" -nb "#202020"'
title_format = "{} — {}"
cmd_get_tree = "i3-msg -t get_tree"
cmd_switch_window = "i3-msg '[con_id={}] focus'"
def main():
fail_if_dmenu_not_found()
global dmenu_options
global cmd_get_tree
global cmd_switch_window
tree = json.loads(subprocess.check_output(
cmd_get_tree,
stderr=subprocess.STDOUT,
shell=True
).decode('utf-8'))
windows = get_all_windows(tree)
lookup_table = build_lookup_table(windows)
chosen = dmenu(itemize(windows), dmenu_options)
switch_to_window(
chosen=chosen,
table=lookup_table,
cmd=cmd_switch_window
)
def fail_if_dmenu_not_found():
"""Check if dmenu exists and exit if it doesn't."""
if subprocess.call("which dmenu", shell=True) != 0:
sys.stdout.write("dmenu not found\n")
sys.stdout.write("Make sure you have dmenu installed\n")
sys.exit(1)
def switch_to_window(chosen, table, cmd):
"""Switch to the chosen window."""
window_id = table.get(chosen, -1)
if window_id != -1:
subprocess.check_call(cmd.format(window_id), shell=True)
def window_as_string(with_id=False):
global title_format
def helper(window):
title = title_format.format(window['class'], window['title'])
if with_id:
return title, window['id']
else:
return title
return helper
def build_lookup_table(windows):
stringifized = map(window_as_string(with_id=True), windows)
return dict(stringifized)
def itemize(windows):
"""Itemize windows list for dmenu."""
return "\n".join(map(window_as_string(with_id=False), windows))
def get_all_windows(tree):
"""Extracts all windows from i3 tree."""
# Add current window
if is_window(tree):
result = [standardize_window(tree)]
else:
result = []
# Add child windows
children = []
for window in tree['nodes']:
children += get_all_windows(window)
return result + children
def is_window(tree):
"""Determines if a tree is a window."""
return tree['window'] \
and tree['window_properties']['class'].lower().find('panel') == -1
def standardize_window(window):
"""Extracts necessary information for a window."""
return {
'id': window['id'],
'title': window['window_properties']['title'],
'class': window['window_properties']['class'],
'instance': window['window_properties']['instance']
}
def dmenu(items, dmenu_options):
"""Calls dmenu to display and menu for window switching."""
cmd = subprocess.Popen(
"dmenu {}".format(dmenu_options),
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, _ = cmd.communicate(items.encode('utf-8'))
return stdout.decode('utf-8').strip('\n')
if __name__ == '__main__':
main()