1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

screenshot: add window capture for Hyprland

This commit is contained in:
bbedward
2025-12-05 21:09:56 -05:00
parent 4d39169eb8
commit aedeab8a6a
3 changed files with 108 additions and 0 deletions

View File

@@ -35,6 +35,7 @@ Modes:
full - Capture the focused output full - Capture the focused output
all - Capture all outputs combined all - Capture all outputs combined
output - Capture a specific output by name output - Capture a specific output by name
window - Capture the focused window (Hyprland only)
last - Capture the last selected region last - Capture the last selected region
Output format (--format): Output format (--format):
@@ -47,6 +48,7 @@ Examples:
dms screenshot full # Full screen of focused output dms screenshot full # Full screen of focused output
dms screenshot all # All screens combined dms screenshot all # All screens combined
dms screenshot output -o DP-1 # Specific output dms screenshot output -o DP-1 # Specific output
dms screenshot window # Focused window (Hyprland)
dms screenshot last # Last region (pre-selected) dms screenshot last # Last region (pre-selected)
dms screenshot --no-clipboard # Save file only dms screenshot --no-clipboard # Save file only
dms screenshot --no-file # Clipboard only dms screenshot --no-file # Clipboard only
@@ -86,6 +88,14 @@ If no previous region exists, falls back to interactive selection.`,
Run: runScreenshotLast, Run: runScreenshotLast,
} }
var ssWindowCmd = &cobra.Command{
Use: "window",
Short: "Capture the focused window",
Long: `Capture the currently focused window.
Currently only supported on Hyprland.`,
Run: runScreenshotWindow,
}
var ssListCmd = &cobra.Command{ var ssListCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List available outputs", Short: "List available outputs",
@@ -117,6 +127,7 @@ func init() {
screenshotCmd.AddCommand(ssAllCmd) screenshotCmd.AddCommand(ssAllCmd)
screenshotCmd.AddCommand(ssOutputCmd) screenshotCmd.AddCommand(ssOutputCmd)
screenshotCmd.AddCommand(ssLastCmd) screenshotCmd.AddCommand(ssLastCmd)
screenshotCmd.AddCommand(ssWindowCmd)
screenshotCmd.AddCommand(ssListCmd) screenshotCmd.AddCommand(ssListCmd)
screenshotCmd.Run = runScreenshotRegion screenshotCmd.Run = runScreenshotRegion
@@ -347,6 +358,11 @@ func runScreenshotLast(cmd *cobra.Command, args []string) {
runScreenshot(config) runScreenshot(config)
} }
func runScreenshotWindow(cmd *cobra.Command, args []string) {
config := getScreenshotConfig(screenshot.ModeWindow)
runScreenshot(config)
}
func runScreenshotList(cmd *cobra.Command, args []string) { func runScreenshotList(cmd *cobra.Command, args []string) {
outputs, err := screenshot.ListOutputs() outputs, err := screenshot.ListOutputs()
if err != nil { if err != nil {

View File

@@ -0,0 +1,69 @@
package screenshot
import (
"encoding/json"
"fmt"
"os"
"os/exec"
)
type Compositor int
const (
CompositorUnknown Compositor = iota
CompositorHyprland
)
func DetectCompositor() Compositor {
if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" {
return CompositorHyprland
}
return CompositorUnknown
}
type WindowGeometry struct {
X int32
Y int32
Width int32
Height int32
}
func GetActiveWindow() (*WindowGeometry, error) {
compositor := DetectCompositor()
switch compositor {
case CompositorHyprland:
return getHyprlandActiveWindow()
default:
return nil, fmt.Errorf("window capture requires Hyprland (other compositors not yet supported)")
}
}
type hyprlandWindow struct {
At [2]int32 `json:"at"`
Size [2]int32 `json:"size"`
}
func getHyprlandActiveWindow() (*WindowGeometry, error) {
cmd := exec.Command("hyprctl", "-j", "activewindow")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("hyprctl activewindow: %w", err)
}
var win hyprlandWindow
if err := json.Unmarshal(output, &win); err != nil {
return nil, fmt.Errorf("parse activewindow: %w", err)
}
if win.Size[0] <= 0 || win.Size[1] <= 0 {
return nil, fmt.Errorf("no active window")
}
return &WindowGeometry{
X: win.At[0],
Y: win.At[1],
Width: win.Size[0],
Height: win.Size[1],
}, nil
}

View File

@@ -77,6 +77,8 @@ func (s *Screenshoter) Run() (*CaptureResult, error) {
return s.captureLastRegion() return s.captureLastRegion()
case ModeRegion: case ModeRegion:
return s.captureRegion() return s.captureRegion()
case ModeWindow:
return s.captureWindow()
case ModeOutput: case ModeOutput:
return s.captureOutput(s.config.OutputName) return s.captureOutput(s.config.OutputName)
case ModeFullScreen: case ModeFullScreen:
@@ -119,6 +121,27 @@ func (s *Screenshoter) captureRegion() (*CaptureResult, error) {
return result, nil return result, nil
} }
func (s *Screenshoter) captureWindow() (*CaptureResult, error) {
geom, err := GetActiveWindow()
if err != nil {
return nil, err
}
region := Region{
X: geom.X,
Y: geom.Y,
Width: geom.Width,
Height: geom.Height,
}
output := s.findOutputForRegion(region)
if output == nil {
return nil, fmt.Errorf("could not find output for window")
}
return s.captureRegionOnOutput(output, region)
}
func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) { func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
output := s.findFocusedOutput() output := s.findFocusedOutput()
if output == nil { if output == nil {