diff --git a/home/common/programs/hyprland/default.nix b/home/common/programs/hyprland/default.nix index a977797..6f8dc2f 100644 --- a/home/common/programs/hyprland/default.nix +++ b/home/common/programs/hyprland/default.nix @@ -2,11 +2,49 @@ config, lib, pkgs, + inputs, ... }: let cfg = config.custom.hyprland; + hyprPkgs = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}; + + # xdph runs this instead of hyprland-share-picker (screencopy:custom_picker_binary). + # The contract is just "print [SELECTION] to stdout"; a leading "r" grants a + # restore token. Printing it immediately means the dialog never renders. + # + # Why bypass the picker at all: Slack opens a burst of screencast sessions per + # share and only asks for persist_mode=1, so the leading sessions race ahead of + # their restore token and re-prompt. allow_token_by_default alone still left + # ~3 dialogs per share. + # + # Escape hatch: `touch ~/.config/hypr/share-picker-manual` to get the real + # picker back when you need a window or region instead of a whole screen. + # + # Deliberately NOT the focused monitor: at the moment you hit "share screen" + # the focus is on the Slack/Chrome window, which is generally not the screen + # you actually want to show. Auto-picking it would silently share the wrong + # display. shareOutput is pinned per-host instead. + sharePicker = pkgs.writeShellScriptBin "xdph-auto-picker" '' + realPicker="${hyprPkgs.xdg-desktop-portal-hyprland}/bin/hyprland-share-picker" + + if [ -e "$HOME/.config/hypr/share-picker-manual" ]; then + exec "$realPicker" "$@" + fi + + want=${lib.escapeShellArg (toString cfg.shareOutput)} + + # Only auto-select if that output is actually connected right now; otherwise + # fall back to the picker rather than sharing something unexpected. + if ${hyprPkgs.hyprland}/bin/hyprctl monitors -j 2>/dev/null \ + | ${pkgs.jq}/bin/jq -e --arg o "$want" 'any(.[]; .name == $o)' >/dev/null 2>&1; then + printf '[SELECTION]r/screen:%s\n' "$want" + else + exec "$realPicker" "$@" + fi + ''; + # Floating webcam preview for the "corner-cam" recording trick: run `webcam`, # then capture the whole screen with kooha/wf-recorder — the preview is in the # recording. v4l2 only (won't work with the laptop's IPU6 cam). Optional device @@ -32,6 +70,16 @@ in default = 24; description = "Cursor size for XCURSOR_SIZE and HYPRCURSOR_SIZE"; }; + shareOutput = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "HDMI-A-1"; + description = '' + Output that screen-sharing auto-selects, bypassing the xdph picker + entirely. null keeps the normal picker. Falls back to the picker if + the named output isn't currently connected. + ''; + }; }; imports = [ @@ -63,6 +111,22 @@ in WEBKIT_DISABLE_DMABUF_RENDERER = "1"; }; + # xdph's share-picker is stateless: the "Allow a restore token" checkbox + # starts unticked, so no app ever receives a token and every single share + # re-prompts (journal showed 0 "Sent restore token" in 30 days, always + # re-selecting the same output). This pre-ticks it, so OBS/Chrome/Slack get + # a token on first share and skip the dialog thereafter. + # Note: xdph.conf is still hyprlang — the 0.55 Lua switch only hit the + # compositor config, not this separate binary. + xdg.configFile."hypr/xdph.conf".text = '' + screencopy { + allow_token_by_default = true + ${lib.optionalString ( + cfg.shareOutput != null + ) " custom_picker_binary = ${sharePicker}/bin/xdph-auto-picker"} + } + ''; + wayland.windowManager.hyprland = { enable = true; package = null; diff --git a/home/konishi/default.nix b/home/konishi/default.nix index 4d1a118..f304ebc 100644 --- a/home/konishi/default.nix +++ b/home/konishi/default.nix @@ -27,6 +27,8 @@ custom.hyprland = { yaziSize = "2400 1800"; cursorSize = 42; + # The screen actually shared in every call; skips the xdph picker entirely. + shareOutput = "HDMI-A-1"; }; custom.gaming = {