#!/usr/bin/env zsh setopt rcquotes # eprintf [args...] function eprintf { printf "${@}" >&2 } # die [args...] function die { eprintf '\e[91mfatal\e[m: '"${1}\n" "${(@)@:2}" exit 1 } # print_help function print_help { local exec_name="$(basename "${1}")" printf 'usage: %s [OPTIONS...] [ARGS...]\n' "${exec_name}" printf 'Run a game inside an Xwayland session with a window manager. This\n' printf 'is very similar to gamescope, except that there is a window\n' printf 'involved. The default window manager is OpenBox.\n' printf '\n' printf ' -h Print this message, then exit.\n' printf ' -w Specify the window manager to use. This argument to this option will be\n' printf ' passed to "sh -c".\n' printf ' -r Specify the resolution of the Xwayland display. This should in the form\n' printf ' of ''WxH''. By default, the resolution of the currently focused output\n' printf ' will be used.\n' printf ' -d Specify the current desktop environment (or standalone Wayland\n' printf ' compositor). This is used if -r is not specified to find the resolution.\n' exit 0 } # get_current_desktop function get_current_desktop { [[ -z "${XDG_CURRENT_DESKTOP}" ]] && die 'set $XDG_CURRENT_DESKTOP or pass -d top specify desktop' eprintf 'Detected desktop: "%s"\n' "${XDG_CURRENT_DESKTOP}" printf '%s' "${XDG_CURRENT_DESKTOP}" } # get_default_resolution [desktop] function get_default_resolution { case "${1}" in 'river') local output="$(ristate -o | jq -r '.outputs | keys.[] as $names | {output: $names, focused: .[$names].focused} | select(.focused) | .output')" eprintf 'Detected output: %s\n' "${output}" local resolution="$(wlr-randr --json | jq -r --arg output "${output}" \ '.[] | select(.name == $output).modes.[] | select(.current) | "\(.width)x\(.height)"')" ;; *) die 'unknown desktop session: "%s"' "${1}" ;; esac eprintf 'Detected resolution: %s\n' "${resolution}" printf '%s\n' "${resolution}" } while getopts ':hw:r:d:' OPTOPT; do case "${OPTOPT}" in 'h') print_help "${0}" ;; 'w') window_manager="${OPTARG}" ;; 'r') resolution="${OPTARG}" ;; 'd') current_desktop="${OPTARG}" ;; ':') die 'option requires an argument: %s' "${OPTARG}" ;; '?') die 'unknown option: %s' "${OPTARG}" ;; esac done (( "${OPTIND}" > ${#} )) && print_help "${0}" shift $(( "${OPTIND}" - 1)) function { emulate -L zsh setopt errexit [[ -v window_manager ]] || window_manager='openbox' [[ -v current_desktop ]] || current_desktop="$(get_current_desktop 2>&3)" [[ -v resolution ]] || resolution="$(get_default_resolution "${current_desktop}" 2>&3)" } 3>&2 function on_sigchld { eprintf 'Xwayland or window manager died! Exiting...\n' kill %sh %Xwayland 2>/dev/null wait ${cat_pid} exit 1 } trap on_sigchld CHLD coproc Xwayland -fullscreen -geometry "${resolution}" -displayfd 1 local xwayland_display read <&p xwayland_display export DISPLAY=":${xwayland_display}" unset WAYLAND_DISPLAY eprintf 'Xwayland display: %s\n' "${DISPLAY}" cat <&p >&2 let cat_pid="${!}" sh -c "exec -- ${window_manager}" & function { emulate -L sh eval "${@}" } "${@}" let child_error_code="${?}" if (( ${child_error_code} )); then printf 'Child exited with error code %d\n' "${child_error_code}" fi trap - kill %sh %Xwayland wait "${cat_pid}" eprintf 'Child died! Exiting...\n'