screenshots: clipboard-only, camera glyph instead of thumbnail

This commit is contained in:
rope 2026-08-21 17:13:12 +01:00
parent c3e8691c2c
commit 6bf3c3909e
3 changed files with 43 additions and 17 deletions

View file

@ -58,7 +58,7 @@ let
} }
''; '';
post = "nvd diff $OLD_SYSTEM /run/current-system && (command -v record-update &>/dev/null && record-update $OLD_SYSTEM /run/current-system || true); rm -f \"$HOME\"/Pictures/Screenshots/*"; post = "nvd diff $OLD_SYSTEM /run/current-system && (command -v record-update &>/dev/null && record-update $OLD_SYSTEM /run/current-system || true)";
# The server evaluates and builds for itself. # The server evaluates and builds for itself.
localUpdate = "bash -c 'set -o pipefail && OLD_SYSTEM=$(readlink /run/current-system) && sudo nixos-rebuild switch $@ --refresh --flake ${flake} -L${buildLog} && ${post}' --"; localUpdate = "bash -c 'set -o pipefail && OLD_SYSTEM=$(readlink /run/current-system) && sudo nixos-rebuild switch $@ --refresh --flake ${flake} -L${buildLog} && ${post}' --";

View file

@ -263,8 +263,6 @@ in
hl.env("QT_QPA_PLATFORM", "wayland;xcb") hl.env("QT_QPA_PLATFORM", "wayland;xcb")
hl.env("SDL_VIDEODRIVER", "wayland") hl.env("SDL_VIDEODRIVER", "wayland")
hl.env("_JAVA_AWT_WM_NONREPARENTING", "1") hl.env("_JAVA_AWT_WM_NONREPARENTING", "1")
-- hyprshot save dir; it mkdir -p's this itself. `update` wipes it.
hl.env("HYPRSHOT_DIR", "/home/fred/Pictures/Screenshots")
${lib.optionalString isGaming '' ${lib.optionalString isGaming ''
-- GPU pinning Navi 22 is card1 on the dual-GPU gaming box. -- GPU pinning Navi 22 is card1 on the dual-GPU gaming box.
hl.env("AQ_DRM_DEVICES", "/dev/dri/card1") hl.env("AQ_DRM_DEVICES", "/dev/dri/card1")
@ -524,8 +522,11 @@ in
-- Screenshots Shift+Super+S matches GNOME binding -- Screenshots Shift+Super+S matches GNOME binding
-- Pin/unpin quickshell's focus grab around the region select so an -- Pin/unpin quickshell's focus grab around the region select so an
-- open menu survives slurp's input grab (no-ops if qs isn't up). -- open menu survives slurp's input grab (no-ops if qs isn't up).
hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("sh -c 'qs ipc call screenshot pin; hyprshot -m region; qs ipc call screenshot unpin'")) -- --clipboard-only: nothing hits the disk, so there is no folder to
hl.bind("Print", hl.dsp.exec_cmd("hyprshot -m output")) -- fill and no file for the notification to thumbnail. The bar draws
-- a camera glyph instead of holding a decoded screen-sized texture.
hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("sh -c 'qs ipc call screenshot pin; hyprshot -m region --clipboard-only; qs ipc call screenshot unpin'"))
hl.bind("Print", hl.dsp.exec_cmd("hyprshot -m output --clipboard-only"))
-- Settings shortcut Super+I matches GNOME binding -- Settings shortcut Super+I matches GNOME binding
hl.bind(mod .. " + I", hl.dsp.exec_cmd("pavucontrol")) hl.bind(mod .. " + I", hl.dsp.exec_cmd("pavucontrol"))

View file

@ -2119,9 +2119,11 @@ in
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: _nc.bodyLines maximumLineCount: _nc.bodyLines
wrapMode: Text.Wrap wrapMode: Text.Wrap
// ponytail: hyprshot's body is a noisy file path; the // hyprshot's body used to be a noisy save path and was
// "Screenshot saved" summary + the preview say enough. // hidden. Under --clipboard-only it reads "Image copied
visible: text !== "" && !(_nc.notif && _nc.notif.appName === "Hyprshot") // to the clipboard", which is the only thing correcting
// its own "Screenshot saved" summary nothing is saved.
visible: text !== ""
} }
Row { Row {
spacing: 4 spacing: 4
@ -3888,7 +3890,7 @@ in
} }
// Preview image for a notification: its image hint, else a // Preview image for a notification: its image hint, else a
// file-path appIcon (e.g. hyprshot's saved shot). "" = none. // file-path appIcon (e.g. album art on disk). "" = none.
function notifPreviewSource(notif) { function notifPreviewSource(notif) {
if (!notif) return ""; if (!notif) return "";
if (notif.image && notif.image !== "") return notif.image; if (notif.image && notif.image !== "") return notif.image;
@ -3897,6 +3899,15 @@ in
if (a.startsWith("/")) return "file://" + a; if (a.startsWith("/")) return "file://" + a;
return ""; return "";
} }
// Glyph to fill the preview slot when there is no image to put
// there. Screenshots run --clipboard-only, so nothing is written
// to disk and there is no thumbnail to load which is the point:
// a decoded 3440x1440 texture cost ~19MB per retained shot.
function notifPreviewIcon(notif) {
if (!notif) return "";
return notif.appName === "Hyprshot" ? "camera" : "";
}
// Jump to the window that sent a notification. Toplevel.activate() // Jump to the window that sent a notification. Toplevel.activate()
// goes over wlr-foreign-toplevel the same path a taskbar uses // goes over wlr-foreign-toplevel the same path a taskbar uses
// so Hyprland follows it across workspaces. The app raising // so Hyprland follows it across workspaces. The app raising
@ -6969,6 +6980,7 @@ in
id: notifItem id: notifItem
required property var modelData required property var modelData
readonly property string previewSource: bar.notifPreviewSource(notifItem.modelData) readonly property string previewSource: bar.notifPreviewSource(notifItem.modelData)
readonly property string previewIcon: bar.notifPreviewIcon(notifItem.modelData)
width: parent.width width: parent.width
Item { Item {
@ -6994,10 +7006,11 @@ in
} }
} }
// Image preview (album art, screenshot thumb) // Image preview (album art), or a glyph for
// senders that have no image worth loading.
ClippingRectangle { ClippingRectangle {
id: notifPreview id: notifPreview
visible: notifItem.previewSource !== "" visible: notifItem.previewSource !== "" || notifItem.previewIcon !== ""
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
width: 48; height: 48 width: 48; height: 48
@ -7006,14 +7019,19 @@ in
Image { Image {
anchors.fill: parent anchors.fill: parent
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
// Decode to thumbnail height: without this a // Decode to thumbnail height: a full-resolution
// screenshot notification keeps a full-resolution // 3440x1440 texture is ~19MB resident for as long
// 3440x1440 texture (~19MB) resident for as long // as the notification is tracked 30 retained is
// as it is tracked 30 retained notifications is
// half a gigabyte of VRAM for 48px previews. // half a gigabyte of VRAM for 48px previews.
sourceSize.height: 96 sourceSize.height: 96
source: notifItem.previewSource source: notifItem.previewSource
} }
SIcon {
anchors.centerIn: parent
visible: notifItem.previewSource === ""
text: notifItem.previewIcon
font.pixelSize: 22
}
} }
NotifContent { NotifContent {
@ -7330,6 +7348,7 @@ in
color: Theme.cardBg color: Theme.cardBg
property string previewSource: bar.notifPreviewSource(toastItem.currentNotif) property string previewSource: bar.notifPreviewSource(toastItem.currentNotif)
property string previewIcon: bar.notifPreviewIcon(toastItem.currentNotif)
// First child, so the action chips and the dismiss // First child, so the action chips and the dismiss
// button still take their own clicks. // button still take their own clicks.
@ -7343,10 +7362,10 @@ in
} }
} }
// Image preview (screenshot thumb, album art) // Image preview (album art), else the sender's glyph.
ClippingRectangle { ClippingRectangle {
id: toastPreview id: toastPreview
visible: toastCard.previewSource !== "" visible: toastCard.previewSource !== "" || toastCard.previewIcon !== ""
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.margins: 8 anchors.margins: 8
@ -7360,6 +7379,12 @@ in
sourceSize.height: 96 sourceSize.height: 96
source: toastCard.previewSource source: toastCard.previewSource
} }
SIcon {
anchors.centerIn: parent
visible: toastCard.previewSource === ""
text: toastCard.previewIcon
font.pixelSize: 22
}
} }
NotifContent { NotifContent {