mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 17:45:42 +00:00
ed96e94806
- Updated session control mechanisms to ensure proper handling of remote input and clipboard operations. - Introduced session authorization checks to validate operator permissions before starting desktop sessions. - Improved input injection logic to prevent unauthorized access during active sessions. - Added new capabilities for managing session flags and controls, ensuring a more robust and secure desktop experience. - Enhanced error handling and logging for better traceability of session-related actions.
162 lines
4.2 KiB
Go
162 lines
4.2 KiB
Go
//go:build linux
|
|
|
|
package agent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
)
|
|
|
|
// isWaylandSession returns true when running under a Wayland compositor.
|
|
func isWaylandSession() bool {
|
|
if os.Getenv("WAYLAND_DISPLAY") != "" {
|
|
return true
|
|
}
|
|
if strings.EqualFold(os.Getenv("XDG_SESSION_TYPE"), "wayland") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// hasX11Display returns true when an X11 display is available.
|
|
// This includes XWayland sessions running inside Wayland compositors.
|
|
func hasX11Display() bool {
|
|
return strings.TrimSpace(os.Getenv("DISPLAY")) != ""
|
|
}
|
|
|
|
// x11Display returns the X11 DISPLAY value. Callers must first confirm that
|
|
// an X11 display is present rather than guessing ":0" from a service context.
|
|
func x11Display() string {
|
|
return strings.TrimSpace(os.Getenv("DISPLAY"))
|
|
}
|
|
|
|
const desktopPortalBusName = "org.freedesktop.portal.Desktop"
|
|
|
|
type waylandPortalReadiness struct {
|
|
Portal bool
|
|
PipeWire bool
|
|
}
|
|
|
|
// detectWaylandPortalReadiness probes only local session prerequisites. A
|
|
// ready portal/PipeWire session is not a capture capability by itself: this
|
|
// agent still needs an in-process OpenPipeWireRemote file-descriptor bridge
|
|
// before it can offer a live portal stream.
|
|
func detectWaylandPortalReadiness() waylandPortalReadiness {
|
|
return waylandPortalReadiness{
|
|
Portal: desktopPortalServiceAvailable(),
|
|
PipeWire: pipeWireSocketAvailable(os.Getenv("XDG_RUNTIME_DIR")),
|
|
}
|
|
}
|
|
|
|
func pipeWireSocketAvailable(runtimeDir string) bool {
|
|
if strings.TrimSpace(runtimeDir) == "" {
|
|
return false
|
|
}
|
|
info, err := os.Stat(filepath.Join(runtimeDir, "pipewire-0"))
|
|
return err == nil && info.Mode()&os.ModeSocket != 0
|
|
}
|
|
|
|
func desktopPortalServiceAvailable() bool {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
|
|
// SessionBus returns a package-managed shared connection; it must not be
|
|
// closed here. The bounded calls below do not activate a portal or prompt.
|
|
conn, err := dbus.SessionBus()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
var owned bool
|
|
call := conn.BusObject().CallWithContext(
|
|
ctx,
|
|
"org.freedesktop.DBus.NameHasOwner",
|
|
0,
|
|
desktopPortalBusName,
|
|
)
|
|
if call.Err == nil && call.Store(&owned) == nil && owned {
|
|
return true
|
|
}
|
|
|
|
var activatable []string
|
|
call = conn.BusObject().CallWithContext(
|
|
ctx,
|
|
"org.freedesktop.DBus.ListActivatableNames",
|
|
0,
|
|
)
|
|
return call.Err == nil && call.Store(&activatable) == nil && portalServiceListed(activatable)
|
|
}
|
|
|
|
func portalServiceListed(names []string) bool {
|
|
for _, name := range names {
|
|
if name == desktopPortalBusName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// captureDevice returns a verified direct ffmpeg input format. Pure Wayland
|
|
// has no such path in this agent: a PipeWire portal node cannot be consumed
|
|
// safely without passing OpenPipeWireRemote's file descriptor to the capture
|
|
// process.
|
|
func captureDevice() string {
|
|
if !hasX11Display() {
|
|
return ""
|
|
}
|
|
return "x11grab"
|
|
}
|
|
|
|
// captureInput returns the direct ffmpeg input source, if one is available.
|
|
func captureInput() string {
|
|
if !hasX11Display() {
|
|
return ""
|
|
}
|
|
return x11Display()
|
|
}
|
|
|
|
// captureFFmpegInputArgs returns a verified direct ffmpeg input. Streaming
|
|
// uses captureFFmpegStrategies; pure Wayland deliberately returns nil rather
|
|
// than claiming a fictitious PipeWire node 0 is capturable.
|
|
func captureFFmpegInputArgs(fps int) []string {
|
|
if !hasX11Display() {
|
|
return nil
|
|
}
|
|
return []string{
|
|
"-f", "x11grab",
|
|
"-framerate", fmt.Sprintf("%d", fps),
|
|
"-i", x11Display(),
|
|
}
|
|
}
|
|
|
|
// captureFFmpegStrategies returns an ordered list of ffmpeg capture pipelines
|
|
// for the current Linux session. The streamer tries them in order until one
|
|
// produces frames.
|
|
//
|
|
// On pure Wayland, use streamFallback instead. It delegates to screenshot
|
|
// tools that can prove they captured a frame (grim/wayshot/portal-aware
|
|
// desktop tools) instead of assuming a PipeWire node or elevated DRM access.
|
|
func captureFFmpegStrategies(fps int, _ *DesktopStreamer) []CaptureStrategy {
|
|
if !hasX11Display() {
|
|
return nil
|
|
}
|
|
name := "x11grab"
|
|
if isWaylandSession() {
|
|
name = "x11grab(XWayland; X11 windows only)"
|
|
}
|
|
return []CaptureStrategy{{
|
|
Name: name,
|
|
Args: []string{
|
|
"-f", "x11grab",
|
|
"-framerate", fmt.Sprintf("%d", fps),
|
|
"-i", x11Display(),
|
|
},
|
|
}}
|
|
}
|