diff --git a/doc/qubes-video-companion.rst b/doc/qubes-video-companion.rst index 3cdd7bc..c8a18ea 100644 --- a/doc/qubes-video-companion.rst +++ b/doc/qubes-video-companion.rst @@ -20,6 +20,10 @@ The project emphasizes correctness and security all the while also sporting supe OPTIONS ======= +resolution + The video resolution to stream and receive video in. The format is [WIDTHxHEIGHTxFPS], meaning resolution is optional. If you set the environment variable "QVC_MONITOR" in the target, that monitor is going to be preferred and if not found, will fallback to the primary monitor. Example: "1920x1080x60" + + video_source The video source to stream and receive video from. Either "webcam" or "screenshare". diff --git a/qubes-rpc/services/qvc.ScreenShare b/qubes-rpc/services/qvc.ScreenShare index 6878f05..a350ed3 100755 --- a/qubes-rpc/services/qvc.ScreenShare +++ b/qubes-rpc/services/qvc.ScreenShare @@ -1,7 +1,31 @@ #!/bin/sh -- - # Copyright (C) 2021 Elliot Killick # Copyright (C) 2021 Demi Marie Obenour # Licensed under the MIT License. See LICENSE file for details. + +set -eu + +## DISPLAY variable used by: xrandr, zenity export DISPLAY=:0 + +true "${XDG_RUNTIME_DIR:="/run/user/$(id -u)"}" +true "${DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"}" +monitors="$(xrandr --listactivemonitors \ + | awk '/^ [0-9]+: \+/ { print "FALSE", $4, $3 }')" +monitor_count="$(echo "${monitors}" | wc -l)" +monitor_longest_line="$(echo "${monitors}" | wc -L)" +dialog_height="$((monitor_count*50+60))" +dialog_width="$((monitor_longest_line*10))" +if test "${monitor_count}" -gt 1; then + # shellcheck disable=SC2086 + QVC_MONITOR="$(zenity --list --radiolist \ + --height="${dialog_height}" --width="${dialog_width}" \ + --column "ID" --column "Name" --column "Resolution" \ + --title "Screen share" \ + --text "Select monitor to present to qube ${QREXEC_REMOTE_DOMAIN}" \ + ${monitors})" +fi +true "${QVC_MONITOR:=}" + +export XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS QVC_MONITOR exec python3 -- /usr/share/qubes-video-companion/sender/screenshare.py diff --git a/qubes-rpc/services/qvc.Webcam b/qubes-rpc/services/qvc.Webcam index 7878428..8f66f6f 100755 --- a/qubes-rpc/services/qvc.Webcam +++ b/qubes-rpc/services/qvc.Webcam @@ -1,7 +1,9 @@ #!/bin/sh -- - # Copyright (C) 2021 Elliot Killick # Copyright (C) 2021 Demi Marie Obenour # Licensed under the MIT License. See LICENSE file for details. -export DISPLAY=:0 -exec python3 -- /usr/share/qubes-video-companion/sender/webcam.py ${1:+"$1"} +set -eu +true "${XDG_RUNTIME_DIR:="/run/user/$(id -u)"}" +true "${DBUS_SESSION_BUS_ADDRESS:="unix:path=${XDG_RUNTIME_DIR}/bus"}" +export DISPLAY=:0 XDG_RUNTIME_DIR DBUS_SESSION_BUS_ADDRESS +exec python3 -- /usr/share/qubes-video-companion/sender/webcam.py "${1:+"$1"}" diff --git a/receiver/qubes-video-companion b/receiver/qubes-video-companion index 6c7d310..6dcef46 100755 --- a/receiver/qubes-video-companion +++ b/receiver/qubes-video-companion @@ -12,7 +12,8 @@ unset GETOPT_COMPATIBLE name=${0##*/} usage() { - printf '%s: Usage: qubes-video-companion [--resolution=WIDTHxHEIGHTxFPS] [--] webcam|screenshare [destination qube]\n' "$name" + echo "Usage: $name [--resolution=[WIDTHxHEIGHTxFPS]] [--] webcam|screenshare [destination qube]" >&2 + echo "Resolution example: 1920x1080x60" exit "$1" } @@ -23,14 +24,14 @@ while :; do case $1 in -r|--resolution) if [[ -z "$2" ]]; then - printf '%s: Empty resolution argument\n' "$name" - usage 0 - fi >&2 + echo "$name: Empty resolution argument" >&2 + usage 1 + fi resolution=${2//@/+} resolution=${resolution//x/+} shift 2 ;; - --help) usage 0;; + -h|--help) usage 0;; --) shift; break;; *) exit 1;; # cannot happen esac @@ -71,7 +72,7 @@ if ! [ -f "$qvc_lock_file" ]; then trap exit_clean EXIT sudo touch "$qvc_lock_file" else - echo "Qubes Video Companion is already running! Please stop the previous session before starting a new one." >&2 + echo "Qubes Video Companion is already running! Please stop the previous session before starting a new one. If you think this is an error, remove the lockfile $qvc_lock_file" >&2 exit 1 fi diff --git a/sender/screenshare.py b/sender/screenshare.py index 020df95..8f29130 100644 --- a/sender/screenshare.py +++ b/sender/screenshare.py @@ -10,6 +10,7 @@ # pylint: disable=wrong-import-position import gi +import os gi.require_version("Gdk", "3.0") from gi.repository import Gdk @@ -30,15 +31,24 @@ def icon(self) -> str: return "video-display" def parameters(self) -> Tuple[int, int, int]: - monitor = Gdk.Display().get_default().get_monitor(0).get_geometry() + display = Gdk.Display().get_default() + monitor_count = display.get_n_monitors() + monitor_wanted = os.environ["QVC_MONITOR"] + ## If wanted monitor is not found, use the primary monitor (0). + monitor_index = 0 + for m in range(monitor_count): + if display.get_monitor(m).get_model() == monitor_wanted: + monitor_index = m + break + geometry = display.get_monitor(monitor_index).get_geometry() screen = Gdk.Screen().get_default() kwargs = { - "crop_t": monitor.y, - "crop_l": monitor.x, - "crop_r": screen.width() - monitor.x - monitor.width, - "crop_b": screen.height() - monitor.y - monitor.height, + "crop_t": geometry.y, + "crop_l": geometry.x, + "crop_r": screen.width() - geometry.x - geometry.width, + "crop_b": screen.height() - geometry.y - geometry.height, } - return (monitor.width, monitor.height, 30, kwargs) + return (geometry.width, geometry.height, 30, kwargs) def pipeline(self, width: int, height: int, fps: int, **kwargs) -> List[str]: