Add quickshell screenshot
This commit is contained in:
		
							parent
							
								
									36ff76a601
								
							
						
					
					
						commit
						5a70b9493c
					
				
					 9 changed files with 310 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,7 @@ in {
 | 
			
		|||
    qt6.qtimageformats # amog
 | 
			
		||||
    quickshell.packages.${system}.default
 | 
			
		||||
    pamtester # lockscreen
 | 
			
		||||
    grim imagemagick # screenshot
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  xdg.configFile."quickshell/manifest.conf".text = lib.generators.toKeyValue {} {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,6 @@ Item {
 | 
			
		|||
			yCurve.interpolate(p, collapsedLayerRect.y, expandedLayerRect.y),
 | 
			
		||||
			xCurve.interpolate(p, collapsedLayerRect.width, expandedLayerRect.width),
 | 
			
		||||
			yCurve.interpolate(p, collapsedLayerRect.height, expandedLayerRect.height),
 | 
			
		||||
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@ import QtQuick
 | 
			
		|||
import Quickshell
 | 
			
		||||
 | 
			
		||||
Singleton {
 | 
			
		||||
	readonly property string rtpath: "/run/user/1000/quickshell"
 | 
			
		||||
 | 
			
		||||
	readonly property var colors: QtObject {
 | 
			
		||||
		readonly property var bar: "#30c0ffff";
 | 
			
		||||
		readonly property var barOutline: "#50ffffff";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ import Quickshell.Io
 | 
			
		|||
 | 
			
		||||
Singleton {
 | 
			
		||||
	property bool termSelect: false;
 | 
			
		||||
	signal screenshot();
 | 
			
		||||
 | 
			
		||||
	SocketServer {
 | 
			
		||||
		active: true
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +16,9 @@ Singleton {
 | 
			
		|||
				onRead: message => {
 | 
			
		||||
					console.log(message)
 | 
			
		||||
					switch (message) {
 | 
			
		||||
					case "screenshot":
 | 
			
		||||
						screenshot();
 | 
			
		||||
						break;
 | 
			
		||||
					case "termselect:start":
 | 
			
		||||
						termSelect = true;
 | 
			
		||||
						break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										206
									
								
								modules/user/modules/quickshell/shell/screenshot/Controller.qml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								modules/user/modules/quickshell/shell/screenshot/Controller.qml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,206 @@
 | 
			
		|||
// very bad code DO NOT COPY
 | 
			
		||||
 | 
			
		||||
import QtQuick
 | 
			
		||||
import Quickshell
 | 
			
		||||
import Quickshell.Io
 | 
			
		||||
import Quickshell.Wayland
 | 
			
		||||
import ".."
 | 
			
		||||
 | 
			
		||||
Scope {
 | 
			
		||||
	id: root
 | 
			
		||||
	property bool shooting: false;
 | 
			
		||||
	property bool shootingComplete: false;
 | 
			
		||||
	property bool visible: false;
 | 
			
		||||
	readonly property string path: `${ShellGlobals.rtpath}/screenshot.png`;
 | 
			
		||||
 | 
			
		||||
	onShootingChanged: {
 | 
			
		||||
		if (shooting) {
 | 
			
		||||
			grimProc.running = true
 | 
			
		||||
		} else {
 | 
			
		||||
			visible = false
 | 
			
		||||
			shootingComplete = false
 | 
			
		||||
			cleanupProc.running = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Process {
 | 
			
		||||
		id: grimProc
 | 
			
		||||
		command: ["grim", "-l", "0", path]
 | 
			
		||||
		onExited: code => {
 | 
			
		||||
			if (code == 0) {
 | 
			
		||||
				root.visible = true
 | 
			
		||||
			} else {
 | 
			
		||||
				console.log("screenshot failed")
 | 
			
		||||
				cleanupProc.running = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Process {
 | 
			
		||||
		id: magickProc
 | 
			
		||||
		command: [
 | 
			
		||||
			"magick",
 | 
			
		||||
			path,
 | 
			
		||||
			"-crop",
 | 
			
		||||
			`${selection.w}x${selection.h}+${selection.x}+${selection.y}`,
 | 
			
		||||
			"-quality",
 | 
			
		||||
			"70",
 | 
			
		||||
			path,
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
		onExited: wlCopy.running = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Process {
 | 
			
		||||
		id: wlCopy
 | 
			
		||||
		command: ["sh", "-c", `wl-copy < '${path}'`]
 | 
			
		||||
 | 
			
		||||
		onExited: shootingComplete = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Process {
 | 
			
		||||
		id: cleanupProc
 | 
			
		||||
		command: ["rm", path]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	QtObject {
 | 
			
		||||
		id: selection
 | 
			
		||||
		property real x1;
 | 
			
		||||
		property real y1;
 | 
			
		||||
		property real x2;
 | 
			
		||||
		property real y2;
 | 
			
		||||
 | 
			
		||||
		readonly property real x: Math.min(x1, x2)
 | 
			
		||||
		readonly property real y: Math.min(y1, y2)
 | 
			
		||||
		readonly property real w: Math.max(x1, x2) - x
 | 
			
		||||
		readonly property real h: Math.max(y1, y2) - y
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LazyLoader {
 | 
			
		||||
		loading: root.shooting
 | 
			
		||||
		active: root.visible
 | 
			
		||||
		onLoadingChanged: console.log(`loading set to ${loading} at ${new Date()}`)
 | 
			
		||||
		onActiveChanged: console.log(`active set to ${active} at ${new Date()}`)
 | 
			
		||||
 | 
			
		||||
		Variants {
 | 
			
		||||
			model: Quickshell.screens
 | 
			
		||||
 | 
			
		||||
			property bool selectionComplete: false
 | 
			
		||||
 | 
			
		||||
			Component.onCompleted: {
 | 
			
		||||
				selection.x1 = 0
 | 
			
		||||
				selection.y1 = 0
 | 
			
		||||
				selection.x2 = 0
 | 
			
		||||
				selection.y2 = 0
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			PanelWindow {
 | 
			
		||||
				required property var modelData;
 | 
			
		||||
				screen: modelData
 | 
			
		||||
				visible: root.visible
 | 
			
		||||
				exclusionMode: ExclusionMode.Ignore
 | 
			
		||||
				WlrLayershell.namespace: "shell:screenshot"
 | 
			
		||||
				WlrLayershell.layer: WlrLayer.Overlay
 | 
			
		||||
 | 
			
		||||
				anchors {
 | 
			
		||||
					top: true
 | 
			
		||||
					left: true
 | 
			
		||||
					right: true
 | 
			
		||||
					bottom: true
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				MouseArea {
 | 
			
		||||
					id: area
 | 
			
		||||
					anchors.fill: parent
 | 
			
		||||
					cursorShape: selectionComplete ? Qt.WaitCursor : Qt.CrossCursor
 | 
			
		||||
					enabled: !selectionComplete
 | 
			
		||||
 | 
			
		||||
					onPressed: {
 | 
			
		||||
						selection.x1 = mouseX + screen.x;
 | 
			
		||||
						selection.x2 = selection.x1;
 | 
			
		||||
						selection.y1 = mouseY + screen.y;
 | 
			
		||||
						selection.y2 = selection.y1;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					onPositionChanged: {
 | 
			
		||||
						selection.x2 = mouseX + screen.x;
 | 
			
		||||
						selection.y2 = mouseY + screen.y;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					onReleased: {
 | 
			
		||||
						console.log(`area: ${selection.x} ${selection.y} ${selection.w} ${selection.h}`)
 | 
			
		||||
 | 
			
		||||
						if (selection.w > 0 && selection.h > 0) {
 | 
			
		||||
							magickProc.running = true
 | 
			
		||||
							selectionComplete = true
 | 
			
		||||
						} else {
 | 
			
		||||
							root.shooting = false
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					Image {
 | 
			
		||||
						parent: area
 | 
			
		||||
						anchors.fill: parent
 | 
			
		||||
						source: root.visible ? root.path : ""
 | 
			
		||||
						sourceClipRect: Qt.rect(screen.x, screen.y, screen.width, screen.height)
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					CutoutRect {
 | 
			
		||||
						id: cutoutRect
 | 
			
		||||
						anchors.fill: parent
 | 
			
		||||
						innerX: selection.x - screen.x
 | 
			
		||||
						innerY: selection.y - screen.y
 | 
			
		||||
						innerW: selection.w
 | 
			
		||||
						innerH: selection.h
 | 
			
		||||
 | 
			
		||||
						NumberAnimation {
 | 
			
		||||
							id: rectFlashIn
 | 
			
		||||
							target: cutoutRect
 | 
			
		||||
							property: "opacity"
 | 
			
		||||
							duration: 200
 | 
			
		||||
							easing.type: Easing.OutExpo
 | 
			
		||||
							from: 0.0
 | 
			
		||||
							to: 1.0
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						PropertyAnimation {
 | 
			
		||||
							running: selectionComplete
 | 
			
		||||
							target: cutoutRect
 | 
			
		||||
							property: "innerBorderColor"
 | 
			
		||||
							duration: 200
 | 
			
		||||
							to: "#00ff20"
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						NumberAnimation {
 | 
			
		||||
							running: selectionComplete
 | 
			
		||||
							target: cutoutRect
 | 
			
		||||
							property: "backgroundOpacity"
 | 
			
		||||
							duration: 200
 | 
			
		||||
							to: 0.0
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						NumberAnimation {
 | 
			
		||||
							running: shootingComplete
 | 
			
		||||
							target: cutoutRect
 | 
			
		||||
							property: "opacity"
 | 
			
		||||
							easing.type: Easing.OutCubic
 | 
			
		||||
							duration: 150
 | 
			
		||||
							to: 0.0
 | 
			
		||||
							onStopped: root.shooting = false
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					Connections {
 | 
			
		||||
						target: root
 | 
			
		||||
 | 
			
		||||
						function onVisibleChanged() {
 | 
			
		||||
							if (root.visible) {
 | 
			
		||||
								rectFlashIn.start();
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
import QtQuick
 | 
			
		||||
 | 
			
		||||
Item {
 | 
			
		||||
	id: root
 | 
			
		||||
	property color backgroundColor: "#20ffffff"
 | 
			
		||||
	property real backgroundOpacity: 1.0
 | 
			
		||||
	property alias innerBorderColor: center.border.color
 | 
			
		||||
	property alias innerX: center.x
 | 
			
		||||
	property alias innerY: center.y
 | 
			
		||||
	property alias innerW: center.width
 | 
			
		||||
	property alias innerH: center.height
 | 
			
		||||
 | 
			
		||||
	Rectangle {
 | 
			
		||||
		id: center
 | 
			
		||||
		border.color: "white"
 | 
			
		||||
		border.width: 2
 | 
			
		||||
		color: "transparent"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Rectangle {
 | 
			
		||||
		color: root.backgroundColor
 | 
			
		||||
		opacity: backgroundOpacity
 | 
			
		||||
 | 
			
		||||
		anchors {
 | 
			
		||||
			top: parent.top
 | 
			
		||||
			left: parent.left
 | 
			
		||||
			right: parent.right
 | 
			
		||||
			bottom: center.top
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Rectangle {
 | 
			
		||||
		color: root.backgroundColor
 | 
			
		||||
		opacity: backgroundOpacity
 | 
			
		||||
 | 
			
		||||
		anchors {
 | 
			
		||||
			top: center.bottom
 | 
			
		||||
			left: parent.left
 | 
			
		||||
			right: parent.right
 | 
			
		||||
			bottom: parent.bottom
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Rectangle {
 | 
			
		||||
		color: root.backgroundColor
 | 
			
		||||
		opacity: backgroundOpacity
 | 
			
		||||
 | 
			
		||||
		anchors {
 | 
			
		||||
			top: center.top
 | 
			
		||||
			left: parent.left
 | 
			
		||||
			right: center.left
 | 
			
		||||
			bottom: center.bottom
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Rectangle {
 | 
			
		||||
		color: root.backgroundColor
 | 
			
		||||
		opacity: backgroundOpacity
 | 
			
		||||
 | 
			
		||||
		anchors {
 | 
			
		||||
			top: center.top
 | 
			
		||||
			left: center.right
 | 
			
		||||
			right: parent.right
 | 
			
		||||
			bottom: center.bottom
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,8 +4,30 @@ import Quickshell.Wayland
 | 
			
		|||
import QtQuick
 | 
			
		||||
import QtQuick.Layouts
 | 
			
		||||
import ".."
 | 
			
		||||
import "screenshot" as Screenshot
 | 
			
		||||
 | 
			
		||||
ShellRoot {
 | 
			
		||||
	Process {
 | 
			
		||||
		command: ["mkdir", "-p", ShellGlobals.rtpath]
 | 
			
		||||
		running: true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LazyLoader {
 | 
			
		||||
		id: screenshot
 | 
			
		||||
		loading: true
 | 
			
		||||
 | 
			
		||||
		Screenshot.Controller {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Connections {
 | 
			
		||||
		target: ShellIpc
 | 
			
		||||
 | 
			
		||||
		function onScreenshot() {
 | 
			
		||||
			screenshot.item.shooting = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Variants {
 | 
			
		||||
		model: Quickshell.screens
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue