Skip to content

Instantly share code, notes, and snippets.

@gkatev
Last active August 27, 2026 19:44
Show Gist options
  • Select an option

  • Save gkatev/f477f7f4bb0cfb2151cbfb7d6de0a511 to your computer and use it in GitHub Desktop.

Select an option

Save gkatev/f477f7f4bb0cfb2151cbfb7d6de0a511 to your computer and use it in GitHub Desktop.
Geany editor: Wrapper for per-workspace instances, opening in most recently focused window, and more
#!/bin/bash
GEANY_PATH="/usr/bin/geany"
main() {
OPTS=$(getopt -o hc:gPil:mnprstvV -l help,help-all,help-gtk,column:,config:,ft-names,generate-tags,no-preprocessing,new-instance,socket-file:,list-documents,line:,no-msgwin,no-ctags,no-plugins,print-prefix,read-only,no-session,no-terminal,vte-lib:,verbose,version,display: -n geanywl -- "$@")
eval set -- "$OPTS"
SKIP=
NEW_INSTANCE=
PARAMS=()
# https://gist.github.com/cosimo/3760587
while true; do
ARG1="$1"
ARG2=
case "$1" in
-h | --help | --help-all | --help-gtk) SKIP=true; shift ;;
--column ) ARG2="$2"; shift 2 ;;
-c | --config ) ARG2="$2"; shift 2 ;;
--ft-names ) SKIP=true; shift ;;
-g | --generates-tags ) SKIP=true; shift ;;
-P | --no-preprocessing ) shift ;;
-i | --new-instance ) NEW_INSTANCE=true; ARG1=""; shift ;;
--socket-file ) SKIP=true; ARG2="$2"; shift 2 ;;
--list-documents ) SKIP=true; shift ;;
-l | --line ) ARG2="$2"; shift 2 ;;
-m | --no-msgwin ) shift ;;
-n | --no-ctags ) shift ;;
-p | --no-plugins ) shift ;;
--print-prefix ) SKIP=true; shift ;;
-r | --read-only ) shift ;;
-s | --no-session ) shift ;;
-t | --no-terminal ) shift ;;
--vte-lib ) ARG2="$2"; shift 2 ;;
-v | --verbose ) shift ;;
-V | --version ) SKIP=true; shift ;;
--display ) ARG2="$2"; shift 2 ;;
-- ) shift; break ;;
* ) break ;;
esac
[ "$ARG1" ] && PARAMS+=("$ARG1")
[ "$ARG2" ] && PARAMS+=("$ARG2")
done
FILES="$@"
[ "$FILES" ] && PARAMS+=("$@")
if [ "$SKIP" = true ]; then
exec_geany
fi
if [ ! "$FILES" ]; then
NEW_INSTANCE=true
fi
# Unless a new instance is not requested, look for existing instances
if [ ! "$NEW_INSTANCE" = true ]; then
# Check for open instances in the current workspace, and get the
# PID of each. The list may contain false positives, and is sorted
# according to the stacking order (most recently focused).
for pid in $(get_geany_windows); do
# Verify the process user is the current, and that
# the PID corresponds to a process named geany
[[ "$(ps -o uid="" $pid)" -ne "$UID" ]] && continue
[[ $(ps -q $pid -o comm="") != "geany" ]] && continue
# Get the listening unix socket for the PID
socket=$(ss -lpxH | awk "/pid=$pid/"'{
for(i=1;i<=NF;i++) if($i ~ /^\/.*geany_socket/) print $i
}' | head -n 1)
if [[ "$socket" ]]; then
exec_geany "$socket"
fi
done
fi
# Launch new instance
for (( i=0; ; i++ )); do
socket="$HOME/.config/geany/geany_socket_${HOSTNAME}__$i"
# Get UID of owner
owner=$(stat -c "%u" "$socket" 2>/dev/null)
# If the owner var is empty, the file does not exist
if [ -z "$owner" ]; then
exec_geany "$socket"
fi
# If the user does not own the file, we cannot examine it
# If the -e check fails, while the stat earlier succeeded,
# it means the the file is broken symbolic link
if [[ "$owner" -eq "$UID" ]] && [ ! -e "$socket" ]; then
exec_geany "$socket"
fi
done
}
exec_geany() {
if [ "$1" ]; then
exec "$GEANY_PATH" --socket-file "$1" "${PARAMS[@]}"
else
exec "$GEANY_PATH" "${PARAMS[@]}"
fi
}
sort_recently_focused_x11() {
recent=$(xprop -root | grep "^_NET_CLIENT_LIST_STACKING" | tr "," " ")
recent=(${recent##*#})
wins=()
ids=()
while read -r line; do
wins+=("$line")
ids+=($(grep -o '0x[0-9a-fA-F]\+' <<< "$line"))
done
for (( i=${#recent[@]}-1 ; i>=0 ; i-- )); do
for (( j=0; j<${#ids[@]}; j++ )); do
if [ $((ids[j])) = $((recent[i])) ]; then
echo "${wins[j]}"
fi
done
done
}
get_geany_windows_x11() {
wmctrl -lp | grep '0x[0-9a-fA-F]\+ \+'$(wmctrl -d | grep '*' \
| sed -r 's/([0-9]+).*/\1/g') | grep Geany \
| sort_recently_focused_x11 \
| sed -r 's/0x[0-9a-fA-F]+ +[0-9]+ +([0-9]+).*/\1/g'
}
get_geany_windows_kde() {
kdotool kwinscript --inline '
var currentDesktop = workspace.currentDesktop.x11DesktopNumber;
var windows = workspace.stackingOrder.slice().reverse();
for(var i = 0; i < windows.length; i++) {
var w = windows[i];
if(w.resourceClass.toLowerCase() != "geany") continue;
var onCurrentDesktop = w.desktops.length == 0 || w.desktops.some(function(d) {
return d.x11DesktopNumber == currentDesktop;
});
if(!onCurrentDesktop) continue;
output_result(w.pid);
}
'
}
get_geany_windows() {
if [[ $XDG_SESSION_TYPE == "x11" ]]; then
get_geany_windows_x11
elif [[ $XDG_CURRENT_DESKTOP == "KDE" ]]; then
get_geany_windows_kde
fi
}
main "$@"
@gkatev

gkatev commented Oct 12, 2022

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment