summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go694
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go172
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go180
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go859
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go274
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go326
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go1089
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go35
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go65
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go125
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go81
11 files changed, 0 insertions, 3900 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go
deleted file mode 100644
index 9bd5f0e..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/compositor.go
+++ /dev/null
@@ -1,694 +0,0 @@
-package screenshot
-
-import (
- "encoding/json"
- "fmt"
- "os"
- "os/exec"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/dwl_ipc"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_output_management"
- wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-type Compositor int
-
-const (
- CompositorUnknown Compositor = iota
- CompositorHyprland
- CompositorSway
- CompositorNiri
- CompositorDWL
- CompositorScroll
- CompositorMiracle
-)
-
-var detectedCompositor Compositor = -1
-
-func DetectCompositor() Compositor {
- if detectedCompositor >= 0 {
- return detectedCompositor
- }
-
- hyprlandSig := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE")
- niriSocket := os.Getenv("NIRI_SOCKET")
- swaySocket := os.Getenv("SWAYSOCK")
- scrollSocket := os.Getenv("SCROLLSOCK")
- miracleSocket := os.Getenv("MIRACLESOCK")
-
- switch {
- case niriSocket != "":
- if _, err := os.Stat(niriSocket); err == nil {
- detectedCompositor = CompositorNiri
- return detectedCompositor
- }
- case scrollSocket != "":
- if _, err := os.Stat(scrollSocket); err == nil {
- detectedCompositor = CompositorScroll
- return detectedCompositor
- }
- case miracleSocket != "":
- if _, err := os.Stat(miracleSocket); err == nil {
- detectedCompositor = CompositorMiracle
- return detectedCompositor
- }
- case swaySocket != "":
- if _, err := os.Stat(swaySocket); err == nil {
- detectedCompositor = CompositorSway
- return detectedCompositor
- }
- case hyprlandSig != "":
- detectedCompositor = CompositorHyprland
- return detectedCompositor
- }
-
- if detectDWLProtocol() {
- detectedCompositor = CompositorDWL
- return detectedCompositor
- }
-
- detectedCompositor = CompositorUnknown
- return detectedCompositor
-}
-
-func detectDWLProtocol() bool {
- display, err := client.Connect("")
- if err != nil {
- return false
- }
- ctx := display.Context()
- defer ctx.Close()
-
- registry, err := display.GetRegistry()
- if err != nil {
- return false
- }
-
- found := false
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- if e.Interface == dwl_ipc.ZdwlIpcManagerV2InterfaceName {
- found = true
- }
- })
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return false
- }
-
- return found
-}
-
-func SetCompositorDWL() {
- detectedCompositor = CompositorDWL
-}
-
-type WindowGeometry struct {
- X int32
- Y int32
- Width int32
- Height int32
- Output string
- Scale float64
- OutputX int32
- OutputY int32
- OutputTransform int32
-}
-
-func GetActiveWindow() (*WindowGeometry, error) {
- switch DetectCompositor() {
- case CompositorHyprland:
- return getHyprlandActiveWindow()
- case CompositorDWL:
- return getDWLActiveWindow()
- default:
- return nil, fmt.Errorf("window capture requires Hyprland or DWL")
- }
-}
-
-type hyprlandWindow struct {
- At [2]int32 `json:"at"`
- Size [2]int32 `json:"size"`
-}
-
-func getHyprlandActiveWindow() (*WindowGeometry, error) {
- output, err := exec.Command("hyprctl", "-j", "activewindow").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
-}
-
-type hyprlandMonitor struct {
- Name string `json:"name"`
- X int32 `json:"x"`
- Y int32 `json:"y"`
- Width int32 `json:"width"`
- Height int32 `json:"height"`
- Scale float64 `json:"scale"`
- Focused bool `json:"focused"`
-}
-
-func GetHyprlandMonitorScale(name string) float64 {
- output, err := exec.Command("hyprctl", "-j", "monitors").Output()
- if err != nil {
- return 0
- }
-
- var monitors []hyprlandMonitor
- if err := json.Unmarshal(output, &monitors); err != nil {
- return 0
- }
-
- for _, m := range monitors {
- if m.Name == name {
- return m.Scale
- }
- }
- return 0
-}
-
-func getHyprlandFocusedMonitor() string {
- output, err := exec.Command("hyprctl", "-j", "monitors").Output()
- if err != nil {
- return ""
- }
-
- var monitors []hyprlandMonitor
- if err := json.Unmarshal(output, &monitors); err != nil {
- return ""
- }
-
- for _, m := range monitors {
- if m.Focused {
- return m.Name
- }
- }
- return ""
-}
-
-func GetHyprlandMonitorGeometry(name string) (x, y, w, h int32, ok bool) {
- output, err := exec.Command("hyprctl", "-j", "monitors").Output()
- if err != nil {
- return 0, 0, 0, 0, false
- }
-
- var monitors []hyprlandMonitor
- if err := json.Unmarshal(output, &monitors); err != nil {
- return 0, 0, 0, 0, false
- }
-
- for _, m := range monitors {
- if m.Name == name {
- logicalW := int32(float64(m.Width) / m.Scale)
- logicalH := int32(float64(m.Height) / m.Scale)
- return m.X, m.Y, logicalW, logicalH, true
- }
- }
- return 0, 0, 0, 0, false
-}
-
-type swayWorkspace struct {
- Output string `json:"output"`
- Focused bool `json:"focused"`
-}
-
-func getSwayFocusedMonitor() string {
- output, err := exec.Command("swaymsg", "-t", "get_workspaces").Output()
- if err != nil {
- return ""
- }
-
- var workspaces []swayWorkspace
- if err := json.Unmarshal(output, &workspaces); err != nil {
- return ""
- }
-
- for _, ws := range workspaces {
- if ws.Focused {
- return ws.Output
- }
- }
- return ""
-}
-
-func getScrollFocusedMonitor() string {
- output, err := exec.Command("scrollmsg", "-t", "get_workspaces").Output()
- if err != nil {
- return ""
- }
-
- var workspaces []swayWorkspace
- if err := json.Unmarshal(output, &workspaces); err != nil {
- return ""
- }
-
- for _, ws := range workspaces {
- if ws.Focused {
- return ws.Output
- }
- }
- return ""
-}
-
-func getMiracleFocusedMonitor() string {
- output, err := exec.Command("miraclemsg", "-t", "get_workspaces").Output()
- if err != nil {
- return ""
- }
-
- var workspaces []swayWorkspace
- if err := json.Unmarshal(output, &workspaces); err != nil {
- return ""
- }
-
- for _, ws := range workspaces {
- if ws.Focused {
- return ws.Output
- }
- }
- return ""
-}
-
-type niriWorkspace struct {
- Output string `json:"output"`
- IsFocused bool `json:"is_focused"`
-}
-
-func getNiriFocusedMonitor() string {
- output, err := exec.Command("niri", "msg", "-j", "workspaces").Output()
- if err != nil {
- return ""
- }
-
- var workspaces []niriWorkspace
- if err := json.Unmarshal(output, &workspaces); err != nil {
- return ""
- }
-
- for _, ws := range workspaces {
- if ws.IsFocused {
- return ws.Output
- }
- }
- return ""
-}
-
-var dwlActiveOutput string
-
-func SetDWLActiveOutput(name string) {
- dwlActiveOutput = name
-}
-
-func getDWLFocusedMonitor() string {
- if dwlActiveOutput != "" {
- return dwlActiveOutput
- }
- return queryDWLActiveOutput()
-}
-
-func queryDWLActiveOutput() string {
- display, err := client.Connect("")
- if err != nil {
- return ""
- }
- ctx := display.Context()
- defer ctx.Close()
-
- registry, err := display.GetRegistry()
- if err != nil {
- return ""
- }
-
- var dwlManager *dwl_ipc.ZdwlIpcManagerV2
- outputs := make(map[uint32]*client.Output)
-
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- switch e.Interface {
- case dwl_ipc.ZdwlIpcManagerV2InterfaceName:
- mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil {
- dwlManager = mgr
- }
- case client.OutputInterfaceName:
- out := client.NewOutput(ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := registry.Bind(e.Name, e.Interface, version, out); err == nil {
- outputs[e.Name] = out
- }
- }
- })
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return ""
- }
-
- if dwlManager == nil || len(outputs) == 0 {
- return ""
- }
-
- outputNames := make(map[uint32]string)
- for name, out := range outputs {
- n := name
- out.SetNameHandler(func(e client.OutputNameEvent) {
- outputNames[n] = e.Name
- })
- }
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return ""
- }
-
- type outputState struct {
- name string
- active bool
- gotFrame bool
- }
- states := make(map[uint32]*outputState)
-
- for name, out := range outputs {
- dwlOut, err := dwlManager.GetOutput(out)
- if err != nil {
- continue
- }
- state := &outputState{name: outputNames[name]}
- states[name] = state
-
- dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) {
- state.active = e.Active != 0
- })
- dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) {
- state.gotFrame = true
- })
- }
-
- allFramesReceived := func() bool {
- for _, s := range states {
- if !s.gotFrame {
- return false
- }
- }
- return true
- }
-
- for !allFramesReceived() {
- if err := ctx.Dispatch(); err != nil {
- return ""
- }
- }
-
- for _, state := range states {
- if state.active {
- return state.name
- }
- }
-
- return ""
-}
-
-func GetFocusedMonitor() string {
- switch DetectCompositor() {
- case CompositorHyprland:
- return getHyprlandFocusedMonitor()
- case CompositorSway:
- return getSwayFocusedMonitor()
- case CompositorScroll:
- return getScrollFocusedMonitor()
- case CompositorMiracle:
- return getMiracleFocusedMonitor()
- case CompositorNiri:
- return getNiriFocusedMonitor()
- case CompositorDWL:
- return getDWLFocusedMonitor()
- }
- return ""
-}
-
-type outputInfo struct {
- x, y int32
- scale float64
- transform int32
-}
-
-func getAllOutputInfos() map[string]*outputInfo {
- display, err := client.Connect("")
- if err != nil {
- return nil
- }
- ctx := display.Context()
- defer ctx.Close()
-
- registry, err := display.GetRegistry()
- if err != nil {
- return nil
- }
-
- var outputManager *wlr_output_management.ZwlrOutputManagerV1
-
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- if e.Interface == wlr_output_management.ZwlrOutputManagerV1InterfaceName {
- mgr := wlr_output_management.NewZwlrOutputManagerV1(ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := registry.Bind(e.Name, e.Interface, version, mgr); err == nil {
- outputManager = mgr
- }
- }
- })
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return nil
- }
-
- if outputManager == nil {
- return nil
- }
-
- type headState struct {
- name string
- x, y int32
- scale float64
- transform int32
- }
- heads := make(map[*wlr_output_management.ZwlrOutputHeadV1]*headState)
- done := false
-
- outputManager.SetHeadHandler(func(e wlr_output_management.ZwlrOutputManagerV1HeadEvent) {
- state := &headState{}
- heads[e.Head] = state
- e.Head.SetNameHandler(func(ne wlr_output_management.ZwlrOutputHeadV1NameEvent) {
- state.name = ne.Name
- })
- e.Head.SetPositionHandler(func(pe wlr_output_management.ZwlrOutputHeadV1PositionEvent) {
- state.x = pe.X
- state.y = pe.Y
- })
- e.Head.SetScaleHandler(func(se wlr_output_management.ZwlrOutputHeadV1ScaleEvent) {
- state.scale = se.Scale
- })
- e.Head.SetTransformHandler(func(te wlr_output_management.ZwlrOutputHeadV1TransformEvent) {
- state.transform = te.Transform
- })
- })
- outputManager.SetDoneHandler(func(e wlr_output_management.ZwlrOutputManagerV1DoneEvent) {
- done = true
- })
-
- for !done {
- if err := ctx.Dispatch(); err != nil {
- return nil
- }
- }
-
- result := make(map[string]*outputInfo, len(heads))
- for _, state := range heads {
- if state.name == "" {
- continue
- }
- result[state.name] = &outputInfo{
- x: state.x,
- y: state.y,
- scale: state.scale,
- transform: state.transform,
- }
- }
- return result
-}
-
-func getOutputInfo(outputName string) (*outputInfo, bool) {
- infos := getAllOutputInfos()
- if infos == nil {
- return nil, false
- }
- info, ok := infos[outputName]
- return info, ok
-}
-
-func getDWLActiveWindow() (*WindowGeometry, error) {
- display, err := client.Connect("")
- if err != nil {
- return nil, fmt.Errorf("connect: %w", err)
- }
- ctx := display.Context()
- defer ctx.Close()
-
- registry, err := display.GetRegistry()
- if err != nil {
- return nil, fmt.Errorf("get registry: %w", err)
- }
-
- var dwlManager *dwl_ipc.ZdwlIpcManagerV2
- outputs := make(map[uint32]*client.Output)
-
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- switch e.Interface {
- case dwl_ipc.ZdwlIpcManagerV2InterfaceName:
- mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil {
- dwlManager = mgr
- }
- case client.OutputInterfaceName:
- out := client.NewOutput(ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := registry.Bind(e.Name, e.Interface, version, out); err == nil {
- outputs[e.Name] = out
- }
- }
- })
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return nil, fmt.Errorf("roundtrip: %w", err)
- }
-
- if dwlManager == nil {
- return nil, fmt.Errorf("dwl_ipc_manager not available")
- }
-
- if len(outputs) == 0 {
- return nil, fmt.Errorf("no outputs found")
- }
-
- outputNames := make(map[uint32]string)
- for name, out := range outputs {
- n := name
- out.SetNameHandler(func(e client.OutputNameEvent) {
- outputNames[n] = e.Name
- })
- }
-
- if err := wlhelpers.Roundtrip(display, ctx); err != nil {
- return nil, fmt.Errorf("roundtrip: %w", err)
- }
-
- type dwlOutputState struct {
- output *dwl_ipc.ZdwlIpcOutputV2
- name string
- active bool
- x, y int32
- w, h int32
- scalefactor uint32
- gotFrame bool
- }
-
- dwlOutputs := make(map[uint32]*dwlOutputState)
- for name, out := range outputs {
- dwlOut, err := dwlManager.GetOutput(out)
- if err != nil {
- continue
- }
- state := &dwlOutputState{output: dwlOut, name: outputNames[name]}
- dwlOutputs[name] = state
-
- dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) {
- state.active = e.Active != 0
- })
- dwlOut.SetXHandler(func(e dwl_ipc.ZdwlIpcOutputV2XEvent) {
- state.x = e.X
- })
- dwlOut.SetYHandler(func(e dwl_ipc.ZdwlIpcOutputV2YEvent) {
- state.y = e.Y
- })
- dwlOut.SetWidthHandler(func(e dwl_ipc.ZdwlIpcOutputV2WidthEvent) {
- state.w = e.Width
- })
- dwlOut.SetHeightHandler(func(e dwl_ipc.ZdwlIpcOutputV2HeightEvent) {
- state.h = e.Height
- })
- dwlOut.SetScalefactorHandler(func(e dwl_ipc.ZdwlIpcOutputV2ScalefactorEvent) {
- state.scalefactor = e.Scalefactor
- })
- dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) {
- state.gotFrame = true
- })
- }
-
- allFramesReceived := func() bool {
- for _, s := range dwlOutputs {
- if !s.gotFrame {
- return false
- }
- }
- return true
- }
-
- for !allFramesReceived() {
- if err := ctx.Dispatch(); err != nil {
- return nil, fmt.Errorf("dispatch: %w", err)
- }
- }
-
- for _, state := range dwlOutputs {
- if !state.active {
- continue
- }
- if state.w <= 0 || state.h <= 0 {
- return nil, fmt.Errorf("no active window")
- }
- scale := float64(state.scalefactor) / 100.0
- if scale <= 0 {
- scale = 1.0
- }
-
- geom := &WindowGeometry{
- X: state.x,
- Y: state.y,
- Width: state.w,
- Height: state.h,
- Output: state.name,
- Scale: scale,
- }
-
- if info, ok := getOutputInfo(state.name); ok {
- geom.OutputX = info.x
- geom.OutputY = info.y
- geom.OutputTransform = info.transform
- }
-
- return geom, nil
- }
-
- return nil, fmt.Errorf("no active output found")
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go
deleted file mode 100644
index 8b40dca..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/encode.go
+++ /dev/null
@@ -1,172 +0,0 @@
-package screenshot
-
-import (
- "bufio"
- "fmt"
- "image"
- "image/jpeg"
- "image/png"
- "io"
- "os"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
-)
-
-func BufferToImage(buf *ShmBuffer) *image.RGBA {
- return BufferToImageWithFormat(buf, uint32(FormatARGB8888))
-}
-
-func BufferToImageWithFormat(buf *ShmBuffer, format uint32) *image.RGBA {
- img := image.NewRGBA(image.Rect(0, 0, buf.Width, buf.Height))
- data := buf.Data()
-
- var swapRB bool
- switch format {
- case uint32(FormatABGR8888), uint32(FormatXBGR8888):
- swapRB = false
- default:
- swapRB = true
- }
-
- for y := 0; y < buf.Height; y++ {
- srcOff := y * buf.Stride
- dstOff := y * img.Stride
- for x := 0; x < buf.Width; x++ {
- si := srcOff + x*4
- di := dstOff + x*4
- if si+3 >= len(data) || di+3 >= len(img.Pix) {
- continue
- }
- if swapRB {
- img.Pix[di+0] = data[si+2]
- img.Pix[di+1] = data[si+1]
- img.Pix[di+2] = data[si+0]
- } else {
- img.Pix[di+0] = data[si+0]
- img.Pix[di+1] = data[si+1]
- img.Pix[di+2] = data[si+2]
- }
- img.Pix[di+3] = 255
- }
- }
- return img
-}
-
-func EncodePNG(w io.Writer, img image.Image) error {
- enc := png.Encoder{CompressionLevel: png.BestSpeed}
- return enc.Encode(w, img)
-}
-
-func EncodeJPEG(w io.Writer, img image.Image, quality int) error {
- return jpeg.Encode(w, img, &jpeg.Options{Quality: quality})
-}
-
-func EncodePPM(w io.Writer, img *image.RGBA) error {
- bw := bufio.NewWriter(w)
- bounds := img.Bounds()
- if _, err := fmt.Fprintf(bw, "P6\n%d %d\n255\n", bounds.Dx(), bounds.Dy()); err != nil {
- return err
- }
- for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
- for x := bounds.Min.X; x < bounds.Max.X; x++ {
- off := (y-bounds.Min.Y)*img.Stride + (x-bounds.Min.X)*4
- if err := bw.WriteByte(img.Pix[off+0]); err != nil {
- return err
- }
- if err := bw.WriteByte(img.Pix[off+1]); err != nil {
- return err
- }
- if err := bw.WriteByte(img.Pix[off+2]); err != nil {
- return err
- }
- }
- }
- return bw.Flush()
-}
-
-func GenerateFilename(format Format) string {
- t := time.Now()
- ext := "png"
- switch format {
- case FormatJPEG:
- ext = "jpg"
- case FormatPPM:
- ext = "ppm"
- }
- return fmt.Sprintf("screenshot_%s.%s", t.Format("2006-01-02_15-04-05"), ext)
-}
-
-func GetOutputDir() string {
- if dir := os.Getenv("DMS_SCREENSHOT_DIR"); dir != "" {
- return dir
- }
-
- if xdgPics := getXDGPicturesDir(); xdgPics != "" {
- screenshotDir := filepath.Join(xdgPics, "Screenshots")
- if err := os.MkdirAll(screenshotDir, 0o755); err == nil {
- return screenshotDir
- }
- return xdgPics
- }
-
- if home := os.Getenv("HOME"); home != "" {
- return home
- }
- return "."
-}
-
-func getXDGPicturesDir() string {
- userConfigDir, err := os.UserConfigDir()
- if err != nil {
- log.Error("failed to get user config dir", "err", err)
- return ""
- }
- userDirsFile := filepath.Join(userConfigDir, "user-dirs.dirs")
- data, err := os.ReadFile(userDirsFile)
- if err != nil {
- return ""
- }
-
- for _, line := range strings.Split(string(data), "\n") {
- if len(line) == 0 || line[0] == '#' {
- continue
- }
- const prefix = "XDG_PICTURES_DIR="
- if !strings.HasPrefix(line, prefix) {
- continue
- }
- path := strings.Trim(line[len(prefix):], "\"")
- expanded, err := utils.ExpandPath(path)
- if err != nil {
- return ""
- }
- return expanded
- }
- return ""
-}
-
-func WriteToFile(buf *ShmBuffer, path string, format Format, quality int) error {
- return WriteToFileWithFormat(buf, path, format, quality, uint32(FormatARGB8888))
-}
-
-func WriteToFileWithFormat(buf *ShmBuffer, path string, format Format, quality int, pixelFormat uint32) error {
- f, err := os.Create(path)
- if err != nil {
- return err
- }
- defer f.Close()
-
- img := BufferToImageWithFormat(buf, pixelFormat)
- switch format {
- case FormatJPEG:
- return EncodeJPEG(f, img, quality)
- case FormatPPM:
- return EncodePPM(f, img)
- default:
- return EncodePNG(f, img)
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go
deleted file mode 100644
index 9633b42..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/notify.go
+++ /dev/null
@@ -1,180 +0,0 @@
-package screenshot
-
-import (
- "fmt"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "syscall"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/godbus/dbus/v5"
-)
-
-const (
- notifyDest = "org.freedesktop.Notifications"
- notifyPath = "/org/freedesktop/Notifications"
- notifyInterface = "org.freedesktop.Notifications"
-)
-
-type NotifyResult struct {
- FilePath string
- Clipboard bool
- ImageData []byte
- Width int
- Height int
-}
-
-func SendNotification(result NotifyResult) {
- conn, err := dbus.SessionBus()
- if err != nil {
- log.Debug("dbus session failed", "err", err)
- return
- }
-
- var actions []string
- if result.FilePath != "" {
- actions = []string{"default", "Open"}
- }
-
- hints := map[string]dbus.Variant{}
- if len(result.ImageData) > 0 && result.Width > 0 && result.Height > 0 {
- rowstride := result.Width * 3
- hints["image_data"] = dbus.MakeVariant(struct {
- Width int32
- Height int32
- Rowstride int32
- HasAlpha bool
- BitsPerSample int32
- Channels int32
- Data []byte
- }{
- Width: int32(result.Width),
- Height: int32(result.Height),
- Rowstride: int32(rowstride),
- HasAlpha: false,
- BitsPerSample: 8,
- Channels: 3,
- Data: result.ImageData,
- })
- } else if result.FilePath != "" {
- hints["image_path"] = dbus.MakeVariant(result.FilePath)
- }
-
- summary := "Screenshot captured"
- body := ""
- if result.Clipboard && result.FilePath != "" {
- body = fmt.Sprintf("Copied to clipboard\n%s", filepath.Base(result.FilePath))
- } else if result.Clipboard {
- body = "Copied to clipboard"
- } else if result.FilePath != "" {
- body = filepath.Base(result.FilePath)
- }
-
- obj := conn.Object(notifyDest, notifyPath)
- call := obj.Call(
- notifyInterface+".Notify",
- 0,
- "DMS",
- uint32(0),
- "",
- summary,
- body,
- actions,
- hints,
- int32(5000),
- )
-
- if call.Err != nil {
- log.Debug("notify call failed", "err", call.Err)
- return
- }
-
- var notificationID uint32
- if err := call.Store(&notificationID); err != nil {
- log.Debug("failed to get notification id", "err", err)
- return
- }
-
- if len(actions) == 0 || result.FilePath == "" {
- return
- }
-
- spawnActionListener(notificationID, result.FilePath)
-}
-
-func spawnActionListener(notificationID uint32, filePath string) {
- exe, err := os.Executable()
- if err != nil {
- log.Debug("failed to get executable", "err", err)
- return
- }
-
- cmd := exec.Command(exe, "notify-action", fmt.Sprintf("%d", notificationID), filePath)
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
- cmd.Start()
-}
-
-func RunNotifyActionListener(args []string) {
- if len(args) < 2 {
- return
- }
-
- notificationID, err := strconv.ParseUint(args[0], 10, 32)
- if err != nil {
- return
- }
-
- filePath := args[1]
-
- conn, err := dbus.SessionBus()
- if err != nil {
- return
- }
-
- if err := conn.AddMatchSignal(
- dbus.WithMatchObjectPath(notifyPath),
- dbus.WithMatchInterface(notifyInterface),
- ); err != nil {
- return
- }
-
- signals := make(chan *dbus.Signal, 10)
- conn.Signal(signals)
-
- for sig := range signals {
- switch sig.Name {
- case notifyInterface + ".ActionInvoked":
- if len(sig.Body) < 2 {
- continue
- }
- id, ok := sig.Body[0].(uint32)
- if !ok || id != uint32(notificationID) {
- continue
- }
- openFile(filePath)
- return
-
- case notifyInterface + ".NotificationClosed":
- if len(sig.Body) < 1 {
- continue
- }
- id, ok := sig.Body[0].(uint32)
- if !ok || id != uint32(notificationID) {
- continue
- }
- return
- }
- }
-}
-
-func openFile(filePath string) {
- cmd := exec.Command("xdg-open", filePath)
- cmd.SysProcAttr = &syscall.SysProcAttr{
- Setsid: true,
- }
- cmd.Start()
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go
deleted file mode 100644
index f296434..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region.go
+++ /dev/null
@@ -1,859 +0,0 @@
-package screenshot
-
-import (
- "fmt"
- "sync"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/keyboard_shortcuts_inhibit"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_layer_shell"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wp_viewporter"
- wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-type SelectionState struct {
- hasSelection bool // There's a selection to display (pre-loaded or user-drawn)
- dragging bool // User is actively drawing a new selection
- surface *OutputSurface // Surface where selection was made
- // Surface-local logical coordinates (from pointer events)
- anchorX float64
- anchorY float64
- currentX float64
- currentY float64
-}
-
-type RenderSlot struct {
- shm *ShmBuffer
- pool *client.ShmPool
- wlBuf *client.Buffer
- busy bool
-}
-
-type OutputSurface struct {
- output *WaylandOutput
- wlSurface *client.Surface
- layerSurf *wlr_layer_shell.ZwlrLayerSurfaceV1
- viewport *wp_viewporter.WpViewport
- screenBuf *ShmBuffer
- screenBufNoCursor *ShmBuffer
- screenFormat uint32
- logicalW int
- logicalH int
- configured bool
- yInverted bool
-
- // Triple-buffered render slots
- slots [3]*RenderSlot
- slotsReady bool
-}
-
-type PreCapture struct {
- screenBuf *ShmBuffer
- screenBufNoCursor *ShmBuffer
- format uint32
- yInverted bool
-}
-
-type RegionSelector struct {
- screenshoter *Screenshoter
-
- display *client.Display
- registry *client.Registry
- ctx *client.Context
-
- compositor *client.Compositor
- shm *client.Shm
- seat *client.Seat
- pointer *client.Pointer
- keyboard *client.Keyboard
- layerShell *wlr_layer_shell.ZwlrLayerShellV1
- screencopy *wlr_screencopy.ZwlrScreencopyManagerV1
- viewporter *wp_viewporter.WpViewporter
-
- shortcutsInhibitMgr *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1
- shortcutsInhibitor *keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitorV1
-
- outputs map[uint32]*WaylandOutput
- outputsMu sync.Mutex
- preCapture map[*WaylandOutput]*PreCapture
-
- surfaces []*OutputSurface
- activeSurface *OutputSurface
-
- // Cursor surface for crosshair
- cursorSurface *client.Surface
- cursorBuffer *ShmBuffer
- cursorWlBuf *client.Buffer
- cursorPool *client.ShmPool
-
- selection SelectionState
- pointerX float64
- pointerY float64
- preSelect Region
- showCapturedCursor bool
- shiftHeld bool
-
- running bool
- cancelled bool
- result Region
-
- capturedBuffer *ShmBuffer
- capturedRegion Region
-}
-
-func NewRegionSelector(s *Screenshoter) *RegionSelector {
- return &RegionSelector{
- screenshoter: s,
- outputs: make(map[uint32]*WaylandOutput),
- preCapture: make(map[*WaylandOutput]*PreCapture),
- showCapturedCursor: s.config.Cursor == CursorOn,
- }
-}
-
-func (r *RegionSelector) Run() (*CaptureResult, bool, error) {
- if r.screenshoter != nil && r.screenshoter.config.Reset {
- r.preSelect = Region{}
- } else {
- r.preSelect = GetLastRegion()
- }
-
- if err := r.connect(); err != nil {
- return nil, false, fmt.Errorf("wayland connect: %w", err)
- }
- defer r.cleanup()
-
- if err := r.setupRegistry(); err != nil {
- return nil, false, fmt.Errorf("registry setup: %w", err)
- }
-
- if err := r.roundtrip(); err != nil {
- return nil, false, fmt.Errorf("roundtrip after registry: %w", err)
- }
-
- switch {
- case r.screencopy == nil:
- return nil, false, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1")
- case r.layerShell == nil:
- return nil, false, fmt.Errorf("compositor does not support wlr-layer-shell-unstable-v1")
- case r.seat == nil:
- return nil, false, fmt.Errorf("no seat available")
- case r.compositor == nil:
- return nil, false, fmt.Errorf("compositor not available")
- case r.shm == nil:
- return nil, false, fmt.Errorf("wl_shm not available")
- case len(r.outputs) == 0:
- return nil, false, fmt.Errorf("no outputs available")
- }
-
- if err := r.roundtrip(); err != nil {
- return nil, false, fmt.Errorf("roundtrip after protocol check: %w", err)
- }
-
- if err := r.preCaptureAllOutputs(); err != nil {
- return nil, false, fmt.Errorf("pre-capture: %w", err)
- }
-
- if err := r.createSurfaces(); err != nil {
- return nil, false, fmt.Errorf("create surfaces: %w", err)
- }
-
- _ = r.createCursor()
-
- if err := r.roundtrip(); err != nil {
- return nil, false, fmt.Errorf("roundtrip after surfaces: %w", err)
- }
-
- r.running = true
- for r.running {
- if err := r.ctx.Dispatch(); err != nil {
- return nil, false, fmt.Errorf("dispatch: %w", err)
- }
- }
-
- if r.cancelled || r.capturedBuffer == nil {
- return nil, r.cancelled, nil
- }
-
- yInverted := false
- var format uint32
- if r.selection.surface != nil {
- yInverted = r.selection.surface.yInverted
- format = r.selection.surface.screenFormat
- }
-
- return &CaptureResult{
- Buffer: r.capturedBuffer,
- Region: r.result,
- YInverted: yInverted,
- Format: format,
- }, false, nil
-}
-
-func (r *RegionSelector) connect() error {
- display, err := client.Connect("")
- if err != nil {
- return err
- }
- r.display = display
- r.ctx = display.Context()
- return nil
-}
-
-func (r *RegionSelector) roundtrip() error {
- return wlhelpers.Roundtrip(r.display, r.ctx)
-}
-
-func (r *RegionSelector) setupRegistry() error {
- registry, err := r.display.GetRegistry()
- if err != nil {
- return err
- }
- r.registry = registry
-
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- r.handleGlobal(e)
- })
-
- registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) {
- r.outputsMu.Lock()
- delete(r.outputs, e.Name)
- r.outputsMu.Unlock()
- })
-
- return nil
-}
-
-func (r *RegionSelector) handleGlobal(e client.RegistryGlobalEvent) {
- switch e.Interface {
- case client.CompositorInterfaceName:
- comp := client.NewCompositor(r.ctx)
- if err := r.registry.Bind(e.Name, e.Interface, e.Version, comp); err == nil {
- r.compositor = comp
- }
-
- case client.ShmInterfaceName:
- shm := client.NewShm(r.ctx)
- if err := r.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil {
- r.shm = shm
- }
-
- case client.SeatInterfaceName:
- seat := client.NewSeat(r.ctx)
- if err := r.registry.Bind(e.Name, e.Interface, e.Version, seat); err == nil {
- r.seat = seat
- r.setupInput()
- }
-
- case client.OutputInterfaceName:
- output := client.NewOutput(r.ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil {
- r.outputsMu.Lock()
- r.outputs[e.Name] = &WaylandOutput{
- wlOutput: output,
- globalName: e.Name,
- scale: 1,
- fractionalScale: 1.0,
- }
- r.outputsMu.Unlock()
- r.setupOutputHandlers(e.Name, output)
- }
-
- case wlr_layer_shell.ZwlrLayerShellV1InterfaceName:
- ls := wlr_layer_shell.NewZwlrLayerShellV1(r.ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := r.registry.Bind(e.Name, e.Interface, version, ls); err == nil {
- r.layerShell = ls
- }
-
- case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName:
- sc := wlr_screencopy.NewZwlrScreencopyManagerV1(r.ctx)
- version := e.Version
- if version > 3 {
- version = 3
- }
- if err := r.registry.Bind(e.Name, e.Interface, version, sc); err == nil {
- r.screencopy = sc
- }
-
- case wp_viewporter.WpViewporterInterfaceName:
- vp := wp_viewporter.NewWpViewporter(r.ctx)
- if err := r.registry.Bind(e.Name, e.Interface, e.Version, vp); err == nil {
- r.viewporter = vp
- }
-
- case keyboard_shortcuts_inhibit.ZwpKeyboardShortcutsInhibitManagerV1InterfaceName:
- mgr := keyboard_shortcuts_inhibit.NewZwpKeyboardShortcutsInhibitManagerV1(r.ctx)
- if err := r.registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil {
- r.shortcutsInhibitMgr = mgr
- }
- }
-}
-
-func (r *RegionSelector) setupOutputHandlers(name uint32, output *client.Output) {
- output.SetGeometryHandler(func(e client.OutputGeometryEvent) {
- r.outputsMu.Lock()
- if o, ok := r.outputs[name]; ok {
- o.x = e.X
- o.y = e.Y
- o.transform = int32(e.Transform)
- }
- r.outputsMu.Unlock()
- })
-
- output.SetModeHandler(func(e client.OutputModeEvent) {
- if e.Flags&uint32(client.OutputModeCurrent) == 0 {
- return
- }
- r.outputsMu.Lock()
- if o, ok := r.outputs[name]; ok {
- o.width = e.Width
- o.height = e.Height
- }
- r.outputsMu.Unlock()
- })
-
- output.SetScaleHandler(func(e client.OutputScaleEvent) {
- r.outputsMu.Lock()
- if o, ok := r.outputs[name]; ok {
- o.scale = e.Factor
- o.fractionalScale = float64(e.Factor)
- }
- r.outputsMu.Unlock()
- })
-
- output.SetNameHandler(func(e client.OutputNameEvent) {
- r.outputsMu.Lock()
- if o, ok := r.outputs[name]; ok {
- o.name = e.Name
- }
- r.outputsMu.Unlock()
- })
-}
-
-func (r *RegionSelector) preCaptureAllOutputs() error {
- r.outputsMu.Lock()
- outputs := make([]*WaylandOutput, 0, len(r.outputs))
- for _, o := range r.outputs {
- outputs = append(outputs, o)
- }
- r.outputsMu.Unlock()
-
- pending := len(outputs) * 2
- done := make(chan struct{}, pending)
-
- for _, output := range outputs {
- pc := &PreCapture{}
- r.preCapture[output] = pc
-
- r.preCaptureOutput(output, pc, true, func() { done <- struct{}{} })
- r.preCaptureOutput(output, pc, false, func() { done <- struct{}{} })
- }
-
- for i := 0; i < pending; i++ {
- if err := r.ctx.Dispatch(); err != nil {
- return err
- }
- select {
- case <-done:
- default:
- i--
- }
- }
- return nil
-}
-
-func (r *RegionSelector) preCaptureOutput(output *WaylandOutput, pc *PreCapture, withCursor bool, onReady func()) {
- cursor := int32(0)
- if withCursor {
- cursor = 1
- }
-
- frame, err := r.screencopy.CaptureOutput(cursor, output.wlOutput)
- if err != nil {
- log.Error("screencopy capture failed", "err", err)
- onReady()
- return
- }
-
- var capturedBuf *ShmBuffer
-
- var capturedFormat PixelFormat
- frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) {
- capturedFormat = PixelFormat(e.Format)
- bpp := capturedFormat.BytesPerPixel()
- if int(e.Stride) < int(e.Width)*bpp {
- log.Error("invalid stride from compositor", "stride", e.Stride, "width", e.Width, "bpp", bpp)
- return
- }
- buf, err := CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride))
- if err != nil {
- log.Error("create screen buffer failed", "err", err)
- return
- }
-
- capturedBuf = buf
- buf.Format = capturedFormat
-
- pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size()))
- if err != nil {
- log.Error("create shm pool failed", "err", err)
- return
- }
-
- wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), e.Format)
- if err != nil {
- log.Error("create wl_buffer failed", "err", err)
- pool.Destroy()
- return
- }
-
- if err := frame.Copy(wlBuf); err != nil {
- log.Error("frame copy failed", "err", err)
- }
- pool.Destroy()
- })
-
- frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) {
- if withCursor {
- pc.yInverted = (e.Flags & 1) != 0
- }
- })
-
- frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
- frame.Destroy()
-
- if capturedBuf == nil {
- onReady()
- return
- }
-
- if capturedFormat.Is24Bit() {
- converted, newFormat, err := capturedBuf.ConvertTo32Bit(capturedFormat)
- if err != nil {
- log.Error("convert 24-bit to 32-bit failed", "err", err)
- } else if converted != capturedBuf {
- capturedBuf.Close()
- capturedBuf = converted
- capturedFormat = newFormat
- }
- }
-
- pc.format = uint32(capturedFormat)
-
- if pc.yInverted {
- capturedBuf.FlipVertical()
- pc.yInverted = false
- }
-
- if output.transform != TransformNormal {
- invTransform := InverseTransform(output.transform)
- transformed, err := capturedBuf.ApplyTransform(invTransform)
- if err != nil {
- log.Error("apply transform failed", "err", err)
- } else if transformed != capturedBuf {
- capturedBuf.Close()
- capturedBuf = transformed
- }
- }
-
- if withCursor {
- pc.screenBuf = capturedBuf
- } else {
- pc.screenBufNoCursor = capturedBuf
- }
-
- onReady()
- })
-
- frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) {
- log.Error("screencopy failed")
- frame.Destroy()
- onReady()
- })
-}
-
-func (r *RegionSelector) createSurfaces() error {
- r.outputsMu.Lock()
- outputs := make([]*WaylandOutput, 0, len(r.outputs))
- for _, o := range r.outputs {
- outputs = append(outputs, o)
- }
- r.outputsMu.Unlock()
-
- for _, output := range outputs {
- os, err := r.createOutputSurface(output)
- if err != nil {
- return fmt.Errorf("output %s: %w", output.name, err)
- }
- r.surfaces = append(r.surfaces, os)
- }
-
- return nil
-}
-
-func (r *RegionSelector) createCursor() error {
- const size = 24
- const hotspot = size / 2
-
- surface, err := r.compositor.CreateSurface()
- if err != nil {
- return fmt.Errorf("create cursor surface: %w", err)
- }
- r.cursorSurface = surface
-
- buf, err := CreateShmBuffer(size, size, size*4)
- if err != nil {
- return fmt.Errorf("create cursor buffer: %w", err)
- }
- r.cursorBuffer = buf
-
- // Draw crosshair
- data := buf.Data()
- for y := 0; y < size; y++ {
- for x := 0; x < size; x++ {
- off := (y*size + x) * 4
- // Vertical line
- if x >= hotspot-1 && x <= hotspot && y >= 2 && y < size-2 {
- data[off+0] = 255 // B
- data[off+1] = 255 // G
- data[off+2] = 255 // R
- data[off+3] = 255 // A
- continue
- }
- // Horizontal line
- if y >= hotspot-1 && y <= hotspot && x >= 2 && x < size-2 {
- data[off+0] = 255
- data[off+1] = 255
- data[off+2] = 255
- data[off+3] = 255
- continue
- }
- // Transparent
- data[off+0] = 0
- data[off+1] = 0
- data[off+2] = 0
- data[off+3] = 0
- }
- }
-
- pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size()))
- if err != nil {
- return fmt.Errorf("create cursor pool: %w", err)
- }
- r.cursorPool = pool
-
- wlBuf, err := pool.CreateBuffer(0, size, size, size*4, uint32(FormatARGB8888))
- if err != nil {
- return fmt.Errorf("create cursor wl_buffer: %w", err)
- }
- r.cursorWlBuf = wlBuf
-
- if err := surface.Attach(wlBuf, 0, 0); err != nil {
- return fmt.Errorf("attach cursor: %w", err)
- }
- if err := surface.Damage(0, 0, size, size); err != nil {
- return fmt.Errorf("damage cursor: %w", err)
- }
- if err := surface.Commit(); err != nil {
- return fmt.Errorf("commit cursor: %w", err)
- }
-
- return nil
-}
-
-func (r *RegionSelector) createOutputSurface(output *WaylandOutput) (*OutputSurface, error) {
- surface, err := r.compositor.CreateSurface()
- if err != nil {
- return nil, fmt.Errorf("create surface: %w", err)
- }
-
- layerSurf, err := r.layerShell.GetLayerSurface(
- surface,
- output.wlOutput,
- uint32(wlr_layer_shell.ZwlrLayerShellV1LayerOverlay),
- "dms-screenshot",
- )
- if err != nil {
- return nil, fmt.Errorf("get layer surface: %w", err)
- }
-
- os := &OutputSurface{
- output: output,
- wlSurface: surface,
- layerSurf: layerSurf,
- }
-
- if r.viewporter != nil {
- vp, err := r.viewporter.GetViewport(surface)
- if err == nil {
- os.viewport = vp
- }
- }
-
- if err := layerSurf.SetAnchor(
- uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorTop) |
- uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorBottom) |
- uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorLeft) |
- uint32(wlr_layer_shell.ZwlrLayerSurfaceV1AnchorRight),
- ); err != nil {
- return nil, fmt.Errorf("set anchor: %w", err)
- }
- if err := layerSurf.SetExclusiveZone(-1); err != nil {
- return nil, fmt.Errorf("set exclusive zone: %w", err)
- }
- if err := layerSurf.SetKeyboardInteractivity(uint32(wlr_layer_shell.ZwlrLayerSurfaceV1KeyboardInteractivityExclusive)); err != nil {
- return nil, fmt.Errorf("set keyboard interactivity: %w", err)
- }
-
- layerSurf.SetConfigureHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ConfigureEvent) {
- if err := layerSurf.AckConfigure(e.Serial); err != nil {
- log.Error("ack configure failed", "err", err)
- return
- }
- os.logicalW = int(e.Width)
- os.logicalH = int(e.Height)
- os.configured = true
- r.captureForSurface(os)
- r.ensureShortcutsInhibitor(os)
- })
-
- layerSurf.SetClosedHandler(func(e wlr_layer_shell.ZwlrLayerSurfaceV1ClosedEvent) {
- r.running = false
- r.cancelled = true
- })
-
- if err := surface.Commit(); err != nil {
- return nil, fmt.Errorf("surface commit: %w", err)
- }
-
- return os, nil
-}
-
-func (r *RegionSelector) ensureShortcutsInhibitor(os *OutputSurface) {
- if r.shortcutsInhibitMgr == nil || r.seat == nil || r.shortcutsInhibitor != nil {
- return
- }
- inhibitor, err := r.shortcutsInhibitMgr.InhibitShortcuts(os.wlSurface, r.seat)
- if err == nil {
- r.shortcutsInhibitor = inhibitor
- }
-}
-
-func (r *RegionSelector) captureForSurface(os *OutputSurface) {
- pc := r.preCapture[os.output]
- if pc == nil {
- return
- }
-
- os.screenBuf = pc.screenBuf
- os.screenBufNoCursor = pc.screenBufNoCursor
- os.screenFormat = pc.format
- os.yInverted = pc.yInverted
-
- if os.logicalW > 0 && os.screenBuf != nil {
- os.output.fractionalScale = float64(os.screenBuf.Width) / float64(os.logicalW)
- }
-
- r.initRenderBuffer(os)
- r.applyPreSelection(os)
- r.redrawSurface(os)
-}
-
-func (r *RegionSelector) initRenderBuffer(os *OutputSurface) {
- if os.screenBuf == nil {
- return
- }
-
- for i := 0; i < 3; i++ {
- slot := &RenderSlot{}
-
- buf, err := CreateShmBuffer(os.screenBuf.Width, os.screenBuf.Height, os.screenBuf.Stride)
- if err != nil {
- log.Error("create render slot buffer failed", "err", err)
- return
- }
- slot.shm = buf
-
- pool, err := r.shm.CreatePool(buf.Fd(), int32(buf.Size()))
- if err != nil {
- log.Error("create render slot pool failed", "err", err)
- buf.Close()
- return
- }
- slot.pool = pool
-
- wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), os.screenFormat)
- if err != nil {
- log.Error("create render slot wl_buffer failed", "err", err)
- pool.Destroy()
- buf.Close()
- return
- }
- slot.wlBuf = wlBuf
-
- slotRef := slot
- wlBuf.SetReleaseHandler(func(e client.BufferReleaseEvent) {
- slotRef.busy = false
- })
-
- os.slots[i] = slot
- }
- os.slotsReady = true
-}
-
-func (os *OutputSurface) acquireFreeSlot() *RenderSlot {
- for _, slot := range os.slots {
- if slot != nil && !slot.busy {
- return slot
- }
- }
- return nil
-}
-
-func (r *RegionSelector) applyPreSelection(os *OutputSurface) {
- if r.preSelect.IsEmpty() || os.screenBuf == nil || r.selection.hasSelection {
- return
- }
-
- if r.preSelect.Output != "" && r.preSelect.Output != os.output.name {
- return
- }
-
- scaleX := float64(os.logicalW) / float64(os.screenBuf.Width)
- scaleY := float64(os.logicalH) / float64(os.screenBuf.Height)
-
- x1 := float64(r.preSelect.X-os.output.x) * scaleX
- y1 := float64(r.preSelect.Y-os.output.y) * scaleY
- x2 := float64(r.preSelect.X-os.output.x+r.preSelect.Width) * scaleX
- y2 := float64(r.preSelect.Y-os.output.y+r.preSelect.Height) * scaleY
-
- r.selection.hasSelection = true
- r.selection.dragging = false
- r.selection.surface = os
- r.selection.anchorX = x1
- r.selection.anchorY = y1
- r.selection.currentX = x2
- r.selection.currentY = y2
- r.activeSurface = os
-}
-
-func (r *RegionSelector) getSourceBuffer(os *OutputSurface) *ShmBuffer {
- if !r.showCapturedCursor && os.screenBufNoCursor != nil {
- return os.screenBufNoCursor
- }
- return os.screenBuf
-}
-
-func (r *RegionSelector) redrawSurface(os *OutputSurface) {
- srcBuf := r.getSourceBuffer(os)
- if srcBuf == nil || !os.slotsReady {
- return
- }
-
- slot := os.acquireFreeSlot()
- if slot == nil {
- return
- }
-
- slot.shm.CopyFrom(srcBuf)
-
- // Draw overlay (dimming + selection) into this slot
- r.drawOverlay(os, slot.shm)
-
- if os.viewport != nil {
- _ = os.wlSurface.SetBufferScale(1)
- _ = os.viewport.SetSource(0, 0, float64(slot.shm.Width), float64(slot.shm.Height))
- _ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH))
- } else {
- bufferScale := os.output.scale
- if bufferScale <= 0 {
- bufferScale = 1
- }
- _ = os.wlSurface.SetBufferScale(bufferScale)
- }
-
- _ = os.wlSurface.Attach(slot.wlBuf, 0, 0)
- _ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH))
- _ = os.wlSurface.Commit()
-
- // Mark this slot as busy until compositor releases it
- slot.busy = true
-}
-
-func (r *RegionSelector) cleanup() {
- if r.cursorWlBuf != nil {
- r.cursorWlBuf.Destroy()
- }
- if r.cursorPool != nil {
- r.cursorPool.Destroy()
- }
- if r.cursorSurface != nil {
- r.cursorSurface.Destroy()
- }
- if r.cursorBuffer != nil {
- r.cursorBuffer.Close()
- }
-
- for _, os := range r.surfaces {
- for _, slot := range os.slots {
- if slot == nil {
- continue
- }
- if slot.wlBuf != nil {
- slot.wlBuf.Destroy()
- }
- if slot.pool != nil {
- slot.pool.Destroy()
- }
- if slot.shm != nil {
- slot.shm.Close()
- }
- }
- if os.viewport != nil {
- os.viewport.Destroy()
- }
- if os.layerSurf != nil {
- os.layerSurf.Destroy()
- }
- if os.wlSurface != nil {
- os.wlSurface.Destroy()
- }
- if os.screenBuf != nil {
- os.screenBuf.Close()
- }
- if os.screenBufNoCursor != nil {
- os.screenBufNoCursor.Close()
- }
- }
-
- if r.shortcutsInhibitor != nil {
- _ = r.shortcutsInhibitor.Destroy()
- }
- if r.shortcutsInhibitMgr != nil {
- _ = r.shortcutsInhibitMgr.Destroy()
- }
- if r.viewporter != nil {
- r.viewporter.Destroy()
- }
- if r.screencopy != nil {
- r.screencopy.Destroy()
- }
- if r.pointer != nil {
- r.pointer.Release()
- }
- if r.keyboard != nil {
- r.keyboard.Release()
- }
- if r.display != nil {
- r.ctx.Close()
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go
deleted file mode 100644
index e243443..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_input.go
+++ /dev/null
@@ -1,274 +0,0 @@
-package screenshot
-
-import (
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-func (r *RegionSelector) setupInput() {
- if r.seat == nil {
- return
- }
-
- r.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) {
- if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && r.pointer == nil {
- if pointer, err := r.seat.GetPointer(); err == nil {
- r.pointer = pointer
- r.setupPointerHandlers()
- }
- }
- if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && r.keyboard == nil {
- if keyboard, err := r.seat.GetKeyboard(); err == nil {
- r.keyboard = keyboard
- r.setupKeyboardHandlers()
- }
- }
- })
-}
-
-func (r *RegionSelector) setupPointerHandlers() {
- r.pointer.SetEnterHandler(func(e client.PointerEnterEvent) {
- if r.cursorSurface != nil {
- _ = r.pointer.SetCursor(e.Serial, r.cursorSurface, 12, 12)
- }
-
- r.activeSurface = nil
- for _, os := range r.surfaces {
- if os.wlSurface.ID() == e.Surface.ID() {
- r.activeSurface = os
- break
- }
- }
-
- r.pointerX = e.SurfaceX
- r.pointerY = e.SurfaceY
- })
-
- r.pointer.SetMotionHandler(func(e client.PointerMotionEvent) {
- if r.activeSurface == nil {
- return
- }
-
- r.pointerX = e.SurfaceX
- r.pointerY = e.SurfaceY
-
- if !r.selection.dragging {
- return
- }
-
- curX, curY := e.SurfaceX, e.SurfaceY
- if r.shiftHeld {
- dx := curX - r.selection.anchorX
- dy := curY - r.selection.anchorY
- adx, ady := dx, dy
- if adx < 0 {
- adx = -adx
- }
- if ady < 0 {
- ady = -ady
- }
- size := adx
- if ady > adx {
- size = ady
- }
- if dx < 0 {
- curX = r.selection.anchorX - size
- } else {
- curX = r.selection.anchorX + size
- }
- if dy < 0 {
- curY = r.selection.anchorY - size
- } else {
- curY = r.selection.anchorY + size
- }
- }
-
- r.selection.currentX = curX
- r.selection.currentY = curY
- for _, os := range r.surfaces {
- r.redrawSurface(os)
- }
- })
-
- r.pointer.SetButtonHandler(func(e client.PointerButtonEvent) {
- if r.activeSurface == nil {
- return
- }
-
- switch e.Button {
- case 0x110: // BTN_LEFT
- switch e.State {
- case 1: // pressed
- r.preSelect = Region{}
- r.selection.hasSelection = true
- r.selection.dragging = true
- r.selection.surface = r.activeSurface
- r.selection.anchorX = r.pointerX
- r.selection.anchorY = r.pointerY
- r.selection.currentX = r.pointerX
- r.selection.currentY = r.pointerY
- for _, os := range r.surfaces {
- r.redrawSurface(os)
- }
- case 0: // released
- r.selection.dragging = false
- for _, os := range r.surfaces {
- r.redrawSurface(os)
- }
- if r.screenshoter != nil && r.screenshoter.config.NoConfirm && r.selection.hasSelection {
- r.finishSelection()
- }
- }
- default:
- r.cancelled = true
- r.running = false
- }
- })
-}
-
-func (r *RegionSelector) setupKeyboardHandlers() {
- r.keyboard.SetModifiersHandler(func(e client.KeyboardModifiersEvent) {
- r.shiftHeld = e.ModsDepressed&1 != 0
- })
-
- r.keyboard.SetKeyHandler(func(e client.KeyboardKeyEvent) {
- if e.State != 1 {
- return
- }
-
- switch e.Key {
- case 1:
- r.cancelled = true
- r.running = false
- case 25:
- r.showCapturedCursor = !r.showCapturedCursor
- for _, os := range r.surfaces {
- r.redrawSurface(os)
- }
- case 28, 57, 96:
- if r.selection.hasSelection {
- r.finishSelection()
- }
- }
- })
-}
-
-func (r *RegionSelector) finishSelection() {
- if r.selection.surface == nil {
- r.running = false
- return
- }
-
- os := r.selection.surface
- srcBuf := r.getSourceBuffer(os)
- if srcBuf == nil {
- r.running = false
- return
- }
-
- x1, y1 := r.selection.anchorX, r.selection.anchorY
- x2, y2 := r.selection.currentX, r.selection.currentY
-
- if x1 > x2 {
- x1, x2 = x2, x1
- }
- if y1 > y2 {
- y1, y2 = y2, y1
- }
-
- scaleX, scaleY := 1.0, 1.0
- if os.logicalW > 0 {
- scaleX = float64(srcBuf.Width) / float64(os.logicalW)
- scaleY = float64(srcBuf.Height) / float64(os.logicalH)
- }
-
- bx1 := int(x1 * scaleX)
- by1 := int(y1 * scaleY)
- bx2 := int(x2 * scaleX)
- by2 := int(y2 * scaleY)
-
- // Clamp to buffer bounds
- if bx1 < 0 {
- bx1 = 0
- }
- if by1 < 0 {
- by1 = 0
- }
- if bx2 > srcBuf.Width {
- bx2 = srcBuf.Width
- }
- if by2 > srcBuf.Height {
- by2 = srcBuf.Height
- }
-
- w, h := bx2-bx1+1, by2-by1+1
- if r.shiftHeld && w != h {
- if w < h {
- h = w
- } else {
- w = h
- }
- }
- if w < 1 {
- w = 1
- }
- if h < 1 {
- h = 1
- }
-
- // Create cropped buffer and copy pixels directly
- cropped, err := CreateShmBuffer(w, h, w*4)
- if err != nil {
- r.running = false
- return
- }
-
- srcData := srcBuf.Data()
- dstData := cropped.Data()
- for y := 0; y < h; y++ {
- srcY := by1 + y
- if os.yInverted {
- srcY = srcBuf.Height - 1 - (by1 + y)
- }
- if srcY < 0 || srcY >= srcBuf.Height {
- continue
- }
- dstY := y
- if os.yInverted {
- dstY = h - 1 - y
- }
- for x := 0; x < w; x++ {
- srcX := bx1 + x
- if srcX < 0 || srcX >= srcBuf.Width {
- continue
- }
- si := srcY*srcBuf.Stride + srcX*4
- di := dstY*cropped.Stride + x*4
- if si+3 < len(srcData) && di+3 < len(dstData) {
- dstData[di+0] = srcData[si+0]
- dstData[di+1] = srcData[si+1]
- dstData[di+2] = srcData[si+2]
- dstData[di+3] = srcData[si+3]
- }
- }
- }
-
- r.capturedBuffer = cropped
- r.capturedRegion = Region{
- X: int32(bx1),
- Y: int32(by1),
- Width: int32(w),
- Height: int32(h),
- Output: os.output.name,
- }
-
- // Also store for "last region" feature with global coords
- r.result = Region{
- X: int32(bx1) + os.output.x,
- Y: int32(by1) + os.output.y,
- Width: int32(w),
- Height: int32(h),
- Output: os.output.name,
- }
-
- r.running = false
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go
deleted file mode 100644
index 4071f8a..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/region_render.go
+++ /dev/null
@@ -1,326 +0,0 @@
-package screenshot
-
-import "fmt"
-
-var fontGlyphs = map[rune][12]uint8{
- '0': {0x3C, 0x66, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00},
- '1': {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00},
- '2': {0x3C, 0x66, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x66, 0x7E, 0x00, 0x00},
- '3': {0x3C, 0x66, 0x06, 0x06, 0x1C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00},
- '4': {0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xCC, 0xFE, 0x0C, 0x0C, 0x1E, 0x00, 0x00},
- '5': {0x7E, 0x60, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00},
- '6': {0x1C, 0x30, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00},
- '7': {0x7E, 0x66, 0x06, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00},
- '8': {0x3C, 0x66, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00},
- '9': {0x3C, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x0C, 0x38, 0x00, 0x00},
- 'x': {0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0x00},
- 'E': {0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0x00},
- 'P': {0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00},
- 'S': {0x3C, 0x66, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0x00},
- 'a': {0x00, 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00},
- 'c': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, 0x00},
- 'd': {0x00, 0x00, 0x06, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00},
- 'e': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x7E, 0x60, 0x60, 0x3C, 0x00, 0x00},
- 'h': {0x00, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00},
- 'i': {0x00, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00},
- 'n': {0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00},
- 'o': {0x00, 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00},
- 'p': {0x00, 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x00, 0x00},
- 'r': {0x00, 0x00, 0x00, 0x6E, 0x76, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00},
- 's': {0x00, 0x00, 0x00, 0x3E, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x7C, 0x00, 0x00},
- 't': {0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00},
- 'u': {0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, 0x00},
- 'w': {0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, 0x00},
- 'l': {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00},
- ' ': {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- ':': {0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00},
- '/': {0x00, 0x02, 0x06, 0x0C, 0x18, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x00},
- '[': {0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00},
- ']': {0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00},
-}
-
-type OverlayStyle struct {
- BackgroundR, BackgroundG, BackgroundB, BackgroundA uint8
- TextR, TextG, TextB uint8
- AccentR, AccentG, AccentB uint8
-}
-
-var DefaultOverlayStyle = OverlayStyle{
- BackgroundR: 30, BackgroundG: 30, BackgroundB: 30, BackgroundA: 220,
- TextR: 255, TextG: 255, TextB: 255,
- AccentR: 100, AccentG: 180, AccentB: 255,
-}
-
-func (r *RegionSelector) drawOverlay(os *OutputSurface, renderBuf *ShmBuffer) {
- data := renderBuf.Data()
- stride := renderBuf.Stride
- w, h := renderBuf.Width, renderBuf.Height
- format := os.screenFormat
-
- // Dim the entire buffer
- for y := 0; y < h; y++ {
- off := y * stride
- for x := 0; x < w; x++ {
- i := off + x*4
- if i+3 >= len(data) {
- continue
- }
- data[i+0] = uint8(int(data[i+0]) * 3 / 5)
- data[i+1] = uint8(int(data[i+1]) * 3 / 5)
- data[i+2] = uint8(int(data[i+2]) * 3 / 5)
- }
- }
-
- r.drawHUD(data, stride, w, h, format)
-
- if !r.selection.hasSelection || r.selection.surface != os {
- return
- }
-
- scaleX := float64(w) / float64(os.logicalW)
- scaleY := float64(h) / float64(os.logicalH)
-
- bx1 := int(r.selection.anchorX * scaleX)
- by1 := int(r.selection.anchorY * scaleY)
- bx2 := int(r.selection.currentX * scaleX)
- by2 := int(r.selection.currentY * scaleY)
-
- if bx1 > bx2 {
- bx1, bx2 = bx2, bx1
- }
- if by1 > by2 {
- by1, by2 = by2, by1
- }
-
- bx1 = clamp(bx1, 0, w-1)
- by1 = clamp(by1, 0, h-1)
- bx2 = clamp(bx2, 0, w-1)
- by2 = clamp(by2, 0, h-1)
-
- srcBuf := r.getSourceBuffer(os)
- srcData := srcBuf.Data()
- for y := by1; y <= by2; y++ {
- rowOff := y * stride
- for x := bx1; x <= bx2; x++ {
- si := y*srcBuf.Stride + x*4
- di := rowOff + x*4
- if si+3 >= len(srcData) || di+3 >= len(data) {
- continue
- }
- data[di+0] = srcData[si+0]
- data[di+1] = srcData[si+1]
- data[di+2] = srcData[si+2]
- data[di+3] = srcData[si+3]
- }
- }
-
- selW, selH := bx2-bx1+1, by2-by1+1
- if r.shiftHeld && selW != selH {
- if selW < selH {
- selH = selW
- } else {
- selW = selH
- }
- }
- r.drawBorder(data, stride, w, h, bx1, by1, selW, selH, format)
- r.drawDimensions(data, stride, w, h, bx1, by1, selW, selH, format)
-}
-
-func (r *RegionSelector) drawHUD(data []byte, stride, bufW, bufH int, format uint32) {
- if r.selection.dragging {
- return
- }
-
- style := LoadOverlayStyle()
- const charW, charH, padding, itemSpacing = 8, 12, 12, 24
-
- cursorLabel := "hide"
- if !r.showCapturedCursor {
- cursorLabel = "show"
- }
- captureKey := "Space/Enter"
- if r.screenshoter != nil && r.screenshoter.config.NoConfirm {
- captureKey = "Drag+Release"
- }
-
- items := []struct{ key, desc string }{
- {captureKey, "capture"},
- {"P", cursorLabel + " cursor"},
- {"Esc", "cancel"},
- }
-
- totalW := 0
- for i, item := range items {
- totalW += len(item.key)*(charW+1) + 4 + len(item.desc)*(charW+1)
- if i < len(items)-1 {
- totalW += itemSpacing
- }
- }
-
- hudW := totalW + padding*2
- hudH := charH + padding*2
- hudX := (bufW - hudW) / 2
- hudY := bufH - hudH - 20
-
- r.fillRect(data, stride, bufW, bufH, hudX, hudY, hudW, hudH,
- style.BackgroundR, style.BackgroundG, style.BackgroundB, style.BackgroundA, format)
-
- tx, ty := hudX+padding, hudY+padding
- for i, item := range items {
- r.drawText(data, stride, bufW, bufH, tx, ty, item.key,
- style.AccentR, style.AccentG, style.AccentB, format)
- tx += len(item.key) * (charW + 1)
-
- r.drawText(data, stride, bufW, bufH, tx, ty, " "+item.desc,
- style.TextR, style.TextG, style.TextB, format)
- tx += (1 + len(item.desc)) * (charW + 1)
-
- if i < len(items)-1 {
- tx += itemSpacing
- }
- }
-}
-
-func (r *RegionSelector) drawBorder(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
- const thickness = 2
- for i := 0; i < thickness; i++ {
- r.drawHLine(data, stride, bufW, bufH, x-i, y-i, w+2*i, format)
- r.drawHLine(data, stride, bufW, bufH, x-i, y+h+i-1, w+2*i, format)
- r.drawVLine(data, stride, bufW, bufH, x-i, y-i, h+2*i, format)
- r.drawVLine(data, stride, bufW, bufH, x+w+i-1, y-i, h+2*i, format)
- }
-}
-
-func (r *RegionSelector) drawHLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) {
- if y < 0 || y >= bufH {
- return
- }
- rowOff := y * stride
- for i := 0; i < length; i++ {
- px := x + i
- if px < 0 || px >= bufW {
- continue
- }
- off := rowOff + px*4
- if off+3 >= len(data) {
- continue
- }
- data[off], data[off+1], data[off+2], data[off+3] = 255, 255, 255, 255
- }
-}
-
-func (r *RegionSelector) drawVLine(data []byte, stride, bufW, bufH, x, y, length int, _ uint32) {
- if x < 0 || x >= bufW {
- return
- }
- for i := 0; i < length; i++ {
- py := y + i
- if py < 0 || py >= bufH {
- continue
- }
- off := py*stride + x*4
- if off+3 >= len(data) {
- continue
- }
- data[off], data[off+1], data[off+2], data[off+3] = 255, 255, 255, 255
- }
-}
-
-func (r *RegionSelector) drawDimensions(data []byte, stride, bufW, bufH, x, y, w, h int, format uint32) {
- text := fmt.Sprintf("%dx%d", w, h)
-
- const charW, charH = 8, 12
- textW := len(text) * (charW + 1)
- textH := charH
-
- tx := x + (w-textW)/2
- ty := y + h + 8
-
- if ty+textH > bufH {
- ty = y - textH - 8
- }
- tx = clamp(tx, 0, bufW-textW)
-
- r.fillRect(data, stride, bufW, bufH, tx-4, ty-2, textW+8, textH+4, 0, 0, 0, 200, format)
- r.drawText(data, stride, bufW, bufH, tx, ty, text, 255, 255, 255, format)
-}
-
-func (r *RegionSelector) fillRect(data []byte, stride, bufW, bufH, x, y, w, h int, cr, cg, cb, ca uint8, format uint32) {
- alpha := float64(ca) / 255.0
- invAlpha := 1.0 - alpha
-
- c0, c2 := cb, cr
- if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) {
- c0, c2 = cr, cb
- }
-
- for py := y; py < y+h && py < bufH; py++ {
- if py < 0 {
- continue
- }
- for px := x; px < x+w && px < bufW; px++ {
- if px < 0 {
- continue
- }
- off := py*stride + px*4
- if off+3 >= len(data) {
- continue
- }
- data[off+0] = uint8(float64(data[off+0])*invAlpha + float64(c0)*alpha)
- data[off+1] = uint8(float64(data[off+1])*invAlpha + float64(cg)*alpha)
- data[off+2] = uint8(float64(data[off+2])*invAlpha + float64(c2)*alpha)
- data[off+3] = 255
- }
- }
-}
-
-func (r *RegionSelector) drawText(data []byte, stride, bufW, bufH, x, y int, text string, cr, cg, cb uint8, format uint32) {
- for i, ch := range text {
- r.drawChar(data, stride, bufW, bufH, x+i*9, y, ch, cr, cg, cb, format)
- }
-}
-
-func (r *RegionSelector) drawChar(data []byte, stride, bufW, bufH, x, y int, ch rune, cr, cg, cb uint8, format uint32) {
- glyph, ok := fontGlyphs[ch]
- if !ok {
- return
- }
-
- c0, c2 := cb, cr
- if format == uint32(FormatABGR8888) || format == uint32(FormatXBGR8888) {
- c0, c2 = cr, cb
- }
-
- for row := 0; row < 12; row++ {
- py := y + row
- if py < 0 || py >= bufH {
- continue
- }
- bits := glyph[row]
- for col := 0; col < 8; col++ {
- if (bits & (1 << (7 - col))) == 0 {
- continue
- }
- px := x + col
- if px < 0 || px >= bufW {
- continue
- }
- off := py*stride + px*4
- if off+3 >= len(data) {
- continue
- }
- data[off], data[off+1], data[off+2], data[off+3] = c0, cg, c2, 255
- }
- }
-}
-
-func clamp(v, lo, hi int) int {
- switch {
- case v < lo:
- return lo
- case v > hi:
- return hi
- default:
- return v
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go
deleted file mode 100644
index 42d3404..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/screenshot.go
+++ /dev/null
@@ -1,1089 +0,0 @@
-package screenshot
-
-import (
- "fmt"
- "math"
- "sync"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/wlr_screencopy"
- wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client"
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-type WaylandOutput struct {
- wlOutput *client.Output
- globalName uint32
- name string
- x, y int32
- width int32
- height int32
- scale int32
- fractionalScale float64
- transform int32
-}
-
-type CaptureResult struct {
- Buffer *ShmBuffer
- Region Region
- YInverted bool
- Format uint32
-}
-
-type Screenshoter struct {
- config Config
-
- display *client.Display
- registry *client.Registry
- ctx *client.Context
-
- compositor *client.Compositor
- shm *client.Shm
- screencopy *wlr_screencopy.ZwlrScreencopyManagerV1
-
- outputs map[uint32]*WaylandOutput
- outputsMu sync.Mutex
-}
-
-func New(config Config) *Screenshoter {
- return &Screenshoter{
- config: config,
- outputs: make(map[uint32]*WaylandOutput),
- }
-}
-
-func (s *Screenshoter) Run() (*CaptureResult, error) {
- if err := s.connect(); err != nil {
- return nil, fmt.Errorf("wayland connect: %w", err)
- }
- defer s.cleanup()
-
- if err := s.setupRegistry(); err != nil {
- return nil, fmt.Errorf("registry setup: %w", err)
- }
-
- if err := s.roundtrip(); err != nil {
- return nil, fmt.Errorf("roundtrip: %w", err)
- }
-
- if s.screencopy == nil {
- return nil, fmt.Errorf("compositor does not support wlr-screencopy-unstable-v1")
- }
-
- if err := s.roundtrip(); err != nil {
- return nil, fmt.Errorf("roundtrip: %w", err)
- }
-
- switch s.config.Mode {
- case ModeLastRegion:
- return s.captureLastRegion()
- case ModeRegion:
- return s.captureRegion()
- case ModeWindow:
- return s.captureWindow()
- case ModeOutput:
- return s.captureOutput(s.config.OutputName)
- case ModeFullScreen:
- return s.captureFullScreen()
- case ModeAllScreens:
- return s.captureAllScreens()
- default:
- return s.captureRegion()
- }
-}
-
-func (s *Screenshoter) captureLastRegion() (*CaptureResult, error) {
- lastRegion := GetLastRegion()
- if lastRegion.IsEmpty() {
- return s.captureRegion()
- }
-
- output := s.findOutputForRegion(lastRegion)
- if output == nil {
- return s.captureRegion()
- }
-
- return s.captureRegionOnOutput(output, lastRegion)
-}
-
-func (s *Screenshoter) captureRegion() (*CaptureResult, error) {
- if s.config.Reset {
- if err := SaveLastRegion(Region{}); err != nil {
- log.Debug("failed to reset last region", "err", err)
- }
- }
-
- selector := NewRegionSelector(s)
- result, cancelled, err := selector.Run()
- if err != nil {
- return nil, fmt.Errorf("region selection: %w", err)
- }
- if cancelled || result == nil {
- return nil, nil
- }
-
- if err := SaveLastRegion(result.Region); err != nil {
- log.Debug("failed to save last region", "err", err)
- }
-
- 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,
- }
-
- var output *WaylandOutput
- if geom.Output != "" {
- output = s.findOutputByName(geom.Output)
- }
- if output == nil {
- output = s.findOutputForRegion(region)
- }
- if output == nil {
- return nil, fmt.Errorf("could not find output for window")
- }
-
- switch DetectCompositor() {
- case CompositorHyprland:
- return s.captureAndCrop(output, region)
- case CompositorDWL:
- return s.captureDWLWindow(output, region, geom)
- default:
- return s.captureRegionOnOutput(output, region)
- }
-}
-
-func (s *Screenshoter) captureDWLWindow(output *WaylandOutput, region Region, geom *WindowGeometry) (*CaptureResult, error) {
- result, err := s.captureWholeOutput(output)
- if err != nil {
- return nil, err
- }
-
- scale := geom.Scale
- if scale <= 0 || scale == 1.0 {
- if output.fractionalScale > 1.0 {
- scale = output.fractionalScale
- }
- }
- if scale <= 0 {
- scale = 1.0
- }
-
- localX := int(float64(region.X-geom.OutputX) * scale)
- localY := int(float64(region.Y-geom.OutputY) * scale)
- w := int(float64(region.Width) * scale)
- h := int(float64(region.Height) * scale)
-
- if localX < 0 {
- w += localX
- localX = 0
- }
- if localY < 0 {
- h += localY
- localY = 0
- }
- if localX+w > result.Buffer.Width {
- w = result.Buffer.Width - localX
- }
- if localY+h > result.Buffer.Height {
- h = result.Buffer.Height - localY
- }
-
- if w <= 0 || h <= 0 {
- result.Buffer.Close()
- return nil, fmt.Errorf("window not visible on output")
- }
-
- cropped, err := CreateShmBuffer(w, h, w*4)
- if err != nil {
- result.Buffer.Close()
- return nil, fmt.Errorf("create crop buffer: %w", err)
- }
-
- srcData := result.Buffer.Data()
- dstData := cropped.Data()
-
- for y := 0; y < h; y++ {
- srcY := localY + y
- if result.YInverted {
- srcY = result.Buffer.Height - 1 - (localY + y)
- }
- if srcY < 0 || srcY >= result.Buffer.Height {
- continue
- }
-
- dstY := y
- if result.YInverted {
- dstY = h - 1 - y
- }
-
- for x := 0; x < w; x++ {
- srcX := localX + x
- if srcX < 0 || srcX >= result.Buffer.Width {
- continue
- }
-
- si := srcY*result.Buffer.Stride + srcX*4
- di := dstY*cropped.Stride + x*4
-
- if si+3 >= len(srcData) || di+3 >= len(dstData) {
- continue
- }
-
- dstData[di+0] = srcData[si+0]
- dstData[di+1] = srcData[si+1]
- dstData[di+2] = srcData[si+2]
- dstData[di+3] = srcData[si+3]
- }
- }
-
- result.Buffer.Close()
- cropped.Format = PixelFormat(result.Format)
-
- return &CaptureResult{
- Buffer: cropped,
- Region: region,
- YInverted: false,
- Format: result.Format,
- }, nil
-}
-
-func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
- output := s.findFocusedOutput()
- if output == nil {
- s.outputsMu.Lock()
- for _, o := range s.outputs {
- output = o
- break
- }
- s.outputsMu.Unlock()
- }
-
- if output == nil {
- return nil, fmt.Errorf("no output available")
- }
-
- return s.captureWholeOutput(output)
-}
-
-func (s *Screenshoter) captureOutput(name string) (*CaptureResult, error) {
- s.outputsMu.Lock()
- var output *WaylandOutput
- for _, o := range s.outputs {
- if o.name == name {
- output = o
- break
- }
- }
- s.outputsMu.Unlock()
-
- if output == nil {
- return nil, fmt.Errorf("output %q not found", name)
- }
-
- return s.captureWholeOutput(output)
-}
-
-func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
- s.outputsMu.Lock()
- outputs := make([]*WaylandOutput, 0, len(s.outputs))
- for _, o := range s.outputs {
- outputs = append(outputs, o)
- }
- s.outputsMu.Unlock()
-
- if len(outputs) == 0 {
- return nil, fmt.Errorf("no outputs available")
- }
- if len(outputs) == 1 {
- return s.captureWholeOutput(outputs[0])
- }
-
- wlrInfos := getAllOutputInfos()
-
- type pendingOutput struct {
- result *CaptureResult
- logX float64
- logY float64
- scale float64
- }
- var pending []pendingOutput
- maxScale := 1.0
-
- for _, output := range outputs {
- result, err := s.captureWholeOutput(output)
- if err != nil {
- log.Warn("failed to capture output", "name", output.name, "err", err)
- continue
- }
-
- logX, logY := float64(output.x), float64(output.y)
- scale := float64(output.scale)
-
- switch DetectCompositor() {
- case CompositorHyprland:
- if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
- logX, logY = float64(hx), float64(hy)
- }
- if hs := GetHyprlandMonitorScale(output.name); hs > 0 {
- scale = hs
- }
- default:
- if wlrInfos != nil {
- if info, ok := wlrInfos[output.name]; ok {
- logX, logY = float64(info.x), float64(info.y)
- if info.scale > 0 {
- scale = info.scale
- }
- }
- }
- }
-
- if scale <= 0 {
- scale = 1.0
- }
-
- pending = append(pending, pendingOutput{result: result, logX: logX, logY: logY, scale: scale})
- if scale > maxScale {
- maxScale = scale
- }
- }
-
- if len(pending) == 0 {
- return nil, fmt.Errorf("failed to capture any outputs")
- }
- if len(pending) == 1 {
- return pending[0].result, nil
- }
-
- type layoutEntry struct {
- result *CaptureResult
- canvasX int
- canvasY int
- canvasW int
- canvasH int
- }
- entries := make([]layoutEntry, len(pending))
- var minX, minY, maxX, maxY int
-
- for i, p := range pending {
- cx := int(math.Round(p.logX * maxScale))
- cy := int(math.Round(p.logY * maxScale))
- cw := int(math.Round(float64(p.result.Buffer.Width) * maxScale / p.scale))
- ch := int(math.Round(float64(p.result.Buffer.Height) * maxScale / p.scale))
-
- entries[i] = layoutEntry{result: p.result, canvasX: cx, canvasY: cy, canvasW: cw, canvasH: ch}
-
- right := cx + cw
- bottom := cy + ch
- if i == 0 {
- minX, minY, maxX, maxY = cx, cy, right, bottom
- continue
- }
- if cx < minX {
- minX = cx
- }
- if cy < minY {
- minY = cy
- }
- if right > maxX {
- maxX = right
- }
- if bottom > maxY {
- maxY = bottom
- }
- }
-
- totalW := maxX - minX
- totalH := maxY - minY
- composite, err := CreateShmBuffer(totalW, totalH, totalW*4)
- if err != nil {
- for _, e := range entries {
- e.result.Buffer.Close()
- }
- return nil, fmt.Errorf("create composite buffer: %w", err)
- }
- composite.Clear()
-
- var format uint32
- for _, e := range entries {
- if format == 0 {
- format = e.result.Format
- }
- s.blitBufferScaled(composite, e.result.Buffer,
- e.canvasX-minX, e.canvasY-minY, e.canvasW, e.canvasH,
- e.result.YInverted)
- e.result.Buffer.Close()
- }
-
- return &CaptureResult{
- Buffer: composite,
- Region: Region{X: int32(minX), Y: int32(minY), Width: int32(totalW), Height: int32(totalH)},
- Format: format,
- }, nil
-}
-
-func (s *Screenshoter) blitBufferScaled(dst, src *ShmBuffer, dstX, dstY, dstW, dstH int, yInverted bool) {
- if dstW <= 0 || dstH <= 0 {
- return
- }
-
- srcData := src.Data()
- dstData := dst.Data()
-
- for dy := 0; dy < dstH; dy++ {
- canvasY := dstY + dy
- if canvasY < 0 || canvasY >= dst.Height {
- continue
- }
-
- srcY := dy * src.Height / dstH
- if yInverted {
- srcY = src.Height - 1 - srcY
- }
- if srcY < 0 || srcY >= src.Height {
- continue
- }
-
- srcRowOff := srcY * src.Stride
- dstRowOff := canvasY * dst.Stride
-
- for dx := 0; dx < dstW; dx++ {
- canvasX := dstX + dx
- if canvasX < 0 || canvasX >= dst.Width {
- continue
- }
-
- srcX := dx * src.Width / dstW
- if srcX >= src.Width {
- continue
- }
-
- si := srcRowOff + srcX*4
- di := dstRowOff + canvasX*4
-
- if si+3 >= len(srcData) || di+3 >= len(dstData) {
- continue
- }
-
- dstData[di+0] = srcData[si+0]
- dstData[di+1] = srcData[si+1]
- dstData[di+2] = srcData[si+2]
- dstData[di+3] = srcData[si+3]
- }
- }
-}
-
-func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult, error) {
- cursor := int32(s.config.Cursor)
-
- frame, err := s.screencopy.CaptureOutput(cursor, output.wlOutput)
- if err != nil {
- return nil, fmt.Errorf("capture output: %w", err)
- }
-
- result, err := s.processFrame(frame, Region{
- X: output.x,
- Y: output.y,
- Width: output.width,
- Height: output.height,
- Output: output.name,
- })
- if err != nil {
- return nil, err
- }
-
- if result.YInverted {
- result.Buffer.FlipVertical()
- result.YInverted = false
- }
-
- if output.transform == TransformNormal {
- return result, nil
- }
-
- invTransform := InverseTransform(output.transform)
- transformed, err := result.Buffer.ApplyTransform(invTransform)
- if err != nil {
- result.Buffer.Close()
- return nil, fmt.Errorf("apply transform: %w", err)
- }
-
- if transformed != result.Buffer {
- result.Buffer.Close()
- result.Buffer = transformed
- }
-
- result.Region.Width = int32(transformed.Width)
- result.Region.Height = int32(transformed.Height)
-
- return result, nil
-}
-
-func (s *Screenshoter) captureAndCrop(output *WaylandOutput, region Region) (*CaptureResult, error) {
- result, err := s.captureWholeOutput(output)
- if err != nil {
- return nil, err
- }
-
- outX, outY := output.x, output.y
- scale := float64(output.scale)
- if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
- outX, outY = hx, hy
- }
- if s := GetHyprlandMonitorScale(output.name); s > 0 {
- scale = s
- }
- if scale <= 0 {
- scale = 1.0
- }
-
- localX := int(float64(region.X-outX) * scale)
- localY := int(float64(region.Y-outY) * scale)
- w := int(float64(region.Width) * scale)
- h := int(float64(region.Height) * scale)
-
- cropped, err := CreateShmBuffer(w, h, w*4)
- if err != nil {
- result.Buffer.Close()
- return nil, fmt.Errorf("create crop buffer: %w", err)
- }
-
- srcData := result.Buffer.Data()
- dstData := cropped.Data()
-
- for y := 0; y < h; y++ {
- srcY := localY + y
- if result.YInverted {
- srcY = result.Buffer.Height - 1 - (localY + y)
- }
- if srcY < 0 || srcY >= result.Buffer.Height {
- continue
- }
-
- dstY := y
- if result.YInverted {
- dstY = h - 1 - y
- }
-
- for x := 0; x < w; x++ {
- srcX := localX + x
- if srcX < 0 || srcX >= result.Buffer.Width {
- continue
- }
-
- si := srcY*result.Buffer.Stride + srcX*4
- di := dstY*cropped.Stride + x*4
-
- if si+3 >= len(srcData) || di+3 >= len(dstData) {
- continue
- }
-
- dstData[di+0] = srcData[si+0]
- dstData[di+1] = srcData[si+1]
- dstData[di+2] = srcData[si+2]
- dstData[di+3] = srcData[si+3]
- }
- }
-
- result.Buffer.Close()
- cropped.Format = PixelFormat(result.Format)
-
- return &CaptureResult{
- Buffer: cropped,
- Region: region,
- YInverted: false,
- Format: result.Format,
- }, nil
-}
-
-func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) {
- if output.transform != TransformNormal {
- return s.captureRegionOnTransformedOutput(output, region)
- }
-
- scale := output.fractionalScale
- if scale <= 0 && DetectCompositor() == CompositorHyprland {
- scale = GetHyprlandMonitorScale(output.name)
- }
- if scale <= 0 {
- scale = float64(output.scale)
- }
- if scale <= 0 {
- scale = 1.0
- }
-
- localX := int32(float64(region.X-output.x) * scale)
- localY := int32(float64(region.Y-output.y) * scale)
- w := int32(float64(region.Width) * scale)
- h := int32(float64(region.Height) * scale)
-
- if DetectCompositor() == CompositorDWL {
- scaledOutW := int32(float64(output.width) * scale)
- scaledOutH := int32(float64(output.height) * scale)
- if localX >= scaledOutW {
- localX = localX % scaledOutW
- }
- if localY >= scaledOutH {
- localY = localY % scaledOutH
- }
- if localX+w > scaledOutW {
- w = scaledOutW - localX
- }
- if localY+h > scaledOutH {
- h = scaledOutH - localY
- }
- if localX < 0 {
- w += localX
- localX = 0
- }
- if localY < 0 {
- h += localY
- localY = 0
- }
- }
-
- cursor := int32(s.config.Cursor)
-
- frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
- if err != nil {
- return nil, fmt.Errorf("capture region: %w", err)
- }
-
- return s.processFrame(frame, region)
-}
-
-func (s *Screenshoter) captureRegionOnTransformedOutput(output *WaylandOutput, region Region) (*CaptureResult, error) {
- result, err := s.captureWholeOutput(output)
- if err != nil {
- return nil, err
- }
-
- scale := output.fractionalScale
- if scale <= 0 && DetectCompositor() == CompositorHyprland {
- scale = GetHyprlandMonitorScale(output.name)
- }
- if scale <= 0 {
- scale = float64(output.scale)
- }
- if scale <= 0 {
- scale = 1.0
- }
-
- localX := int(float64(region.X-output.x) * scale)
- localY := int(float64(region.Y-output.y) * scale)
- w := int(float64(region.Width) * scale)
- h := int(float64(region.Height) * scale)
-
- if localX < 0 {
- w += localX
- localX = 0
- }
- if localY < 0 {
- h += localY
- localY = 0
- }
- if localX+w > result.Buffer.Width {
- w = result.Buffer.Width - localX
- }
- if localY+h > result.Buffer.Height {
- h = result.Buffer.Height - localY
- }
-
- if w <= 0 || h <= 0 {
- result.Buffer.Close()
- return nil, fmt.Errorf("region not visible on output")
- }
-
- cropped, err := CreateShmBuffer(w, h, w*4)
- if err != nil {
- result.Buffer.Close()
- return nil, fmt.Errorf("create crop buffer: %w", err)
- }
-
- srcData := result.Buffer.Data()
- dstData := cropped.Data()
-
- for y := 0; y < h; y++ {
- srcOff := (localY+y)*result.Buffer.Stride + localX*4
- dstOff := y * cropped.Stride
- if srcOff+w*4 <= len(srcData) && dstOff+w*4 <= len(dstData) {
- copy(dstData[dstOff:dstOff+w*4], srcData[srcOff:srcOff+w*4])
- }
- }
-
- result.Buffer.Close()
- cropped.Format = PixelFormat(result.Format)
-
- return &CaptureResult{
- Buffer: cropped,
- Region: region,
- YInverted: false,
- Format: result.Format,
- }, nil
-}
-
-func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) {
- var buf *ShmBuffer
- var pool *client.ShmPool
- var wlBuf *client.Buffer
- var format PixelFormat
- var yInverted bool
- ready := false
- failed := false
-
- frame.SetBufferHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferEvent) {
- format = PixelFormat(e.Format)
- bpp := format.BytesPerPixel()
- if int(e.Stride) < int(e.Width)*bpp {
- log.Error("invalid stride from compositor", "stride", e.Stride, "width", e.Width, "bpp", bpp)
- return
- }
- var err error
- buf, err = CreateShmBuffer(int(e.Width), int(e.Height), int(e.Stride))
- if err != nil {
- log.Error("failed to create buffer", "err", err)
- return
- }
- buf.Format = format
- })
-
- frame.SetFlagsHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FlagsEvent) {
- yInverted = (e.Flags & 1) != 0
- })
-
- frame.SetBufferDoneHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1BufferDoneEvent) {
- if buf == nil {
- return
- }
-
- var err error
- pool, err = s.shm.CreatePool(buf.Fd(), int32(buf.Size()))
- if err != nil {
- log.Error("failed to create pool", "err", err)
- return
- }
-
- wlBuf, err = pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format))
- if err != nil {
- pool.Destroy()
- pool = nil
- log.Error("failed to create wl_buffer", "err", err)
- return
- }
-
- if err := frame.Copy(wlBuf); err != nil {
- log.Error("failed to copy frame", "err", err)
- }
- })
-
- frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
- ready = true
- })
-
- frame.SetFailedHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1FailedEvent) {
- failed = true
- })
-
- for !ready && !failed {
- if err := s.ctx.Dispatch(); err != nil {
- frame.Destroy()
- return nil, fmt.Errorf("dispatch: %w", err)
- }
- }
-
- frame.Destroy()
- if wlBuf != nil {
- wlBuf.Destroy()
- }
- if pool != nil {
- pool.Destroy()
- }
-
- if failed {
- if buf != nil {
- buf.Close()
- }
- return nil, fmt.Errorf("frame capture failed")
- }
-
- if format.Is24Bit() {
- converted, newFormat, err := buf.ConvertTo32Bit(format)
- if err != nil {
- buf.Close()
- return nil, fmt.Errorf("convert 24-bit to 32-bit: %w", err)
- }
- if converted != buf {
- buf.Close()
- buf = converted
- }
- format = newFormat
- }
-
- return &CaptureResult{
- Buffer: buf,
- Region: region,
- YInverted: yInverted,
- Format: uint32(format),
- }, nil
-}
-
-func (s *Screenshoter) findOutputByName(name string) *WaylandOutput {
- s.outputsMu.Lock()
- defer s.outputsMu.Unlock()
- for _, o := range s.outputs {
- if o.name == name {
- return o
- }
- }
- return nil
-}
-
-func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
- s.outputsMu.Lock()
- defer s.outputsMu.Unlock()
-
- cx := region.X + region.Width/2
- cy := region.Y + region.Height/2
-
- for _, o := range s.outputs {
- x, y, w, h := o.x, o.y, o.width, o.height
- if DetectCompositor() == CompositorHyprland {
- if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
- x, y, w, h = hx, hy, hw, hh
- }
- }
- if cx >= x && cx < x+w && cy >= y && cy < y+h {
- return o
- }
- }
-
- for _, o := range s.outputs {
- x, y, w, h := o.x, o.y, o.width, o.height
- if DetectCompositor() == CompositorHyprland {
- if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
- x, y, w, h = hx, hy, hw, hh
- }
- }
- if region.X >= x && region.X < x+w &&
- region.Y >= y && region.Y < y+h {
- return o
- }
- }
-
- return nil
-}
-
-func (s *Screenshoter) findFocusedOutput() *WaylandOutput {
- if mon := GetFocusedMonitor(); mon != "" {
- s.outputsMu.Lock()
- defer s.outputsMu.Unlock()
- for _, o := range s.outputs {
- if o.name == mon {
- return o
- }
- }
- }
- s.outputsMu.Lock()
- defer s.outputsMu.Unlock()
- for _, o := range s.outputs {
- return o
- }
- return nil
-}
-
-func (s *Screenshoter) connect() error {
- display, err := client.Connect("")
- if err != nil {
- return err
- }
- s.display = display
- s.ctx = display.Context()
- return nil
-}
-
-func (s *Screenshoter) roundtrip() error {
- return wlhelpers.Roundtrip(s.display, s.ctx)
-}
-
-func (s *Screenshoter) setupRegistry() error {
- registry, err := s.display.GetRegistry()
- if err != nil {
- return err
- }
- s.registry = registry
-
- registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
- s.handleGlobal(e)
- })
-
- registry.SetGlobalRemoveHandler(func(e client.RegistryGlobalRemoveEvent) {
- s.outputsMu.Lock()
- delete(s.outputs, e.Name)
- s.outputsMu.Unlock()
- })
-
- return nil
-}
-
-func (s *Screenshoter) handleGlobal(e client.RegistryGlobalEvent) {
- switch e.Interface {
- case client.CompositorInterfaceName:
- comp := client.NewCompositor(s.ctx)
- if err := s.registry.Bind(e.Name, e.Interface, e.Version, comp); err == nil {
- s.compositor = comp
- }
-
- case client.ShmInterfaceName:
- shm := client.NewShm(s.ctx)
- if err := s.registry.Bind(e.Name, e.Interface, e.Version, shm); err == nil {
- s.shm = shm
- }
-
- case client.OutputInterfaceName:
- output := client.NewOutput(s.ctx)
- version := e.Version
- if version > 4 {
- version = 4
- }
- if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil {
- s.outputsMu.Lock()
- s.outputs[e.Name] = &WaylandOutput{
- wlOutput: output,
- globalName: e.Name,
- scale: 1,
- fractionalScale: 1.0,
- }
- s.outputsMu.Unlock()
- s.setupOutputHandlers(e.Name, output)
- }
-
- case wlr_screencopy.ZwlrScreencopyManagerV1InterfaceName:
- sc := wlr_screencopy.NewZwlrScreencopyManagerV1(s.ctx)
- version := e.Version
- if version > 3 {
- version = 3
- }
- if err := s.registry.Bind(e.Name, e.Interface, version, sc); err == nil {
- s.screencopy = sc
- }
- }
-}
-
-func (s *Screenshoter) setupOutputHandlers(name uint32, output *client.Output) {
- output.SetGeometryHandler(func(e client.OutputGeometryEvent) {
- s.outputsMu.Lock()
- if o, ok := s.outputs[name]; ok {
- o.x, o.y = e.X, e.Y
- o.transform = int32(e.Transform)
- }
- s.outputsMu.Unlock()
- })
-
- output.SetModeHandler(func(e client.OutputModeEvent) {
- if e.Flags&uint32(client.OutputModeCurrent) == 0 {
- return
- }
- s.outputsMu.Lock()
- if o, ok := s.outputs[name]; ok {
- o.width, o.height = e.Width, e.Height
- }
- s.outputsMu.Unlock()
- })
-
- output.SetScaleHandler(func(e client.OutputScaleEvent) {
- s.outputsMu.Lock()
- if o, ok := s.outputs[name]; ok {
- o.scale = e.Factor
- o.fractionalScale = float64(e.Factor)
- }
- s.outputsMu.Unlock()
- })
-
- output.SetNameHandler(func(e client.OutputNameEvent) {
- s.outputsMu.Lock()
- if o, ok := s.outputs[name]; ok {
- o.name = e.Name
- }
- s.outputsMu.Unlock()
- })
-}
-
-func (s *Screenshoter) cleanup() {
- if s.screencopy != nil {
- s.screencopy.Destroy()
- }
- if s.display != nil {
- s.ctx.Close()
- }
-}
-
-func (s *Screenshoter) GetOutputs() []*WaylandOutput {
- s.outputsMu.Lock()
- defer s.outputsMu.Unlock()
- out := make([]*WaylandOutput, 0, len(s.outputs))
- for _, o := range s.outputs {
- out = append(out, o)
- }
- return out
-}
-
-func ListOutputs() ([]Output, error) {
- sc := New(DefaultConfig())
- if err := sc.connect(); err != nil {
- return nil, err
- }
- defer sc.cleanup()
-
- if err := sc.setupRegistry(); err != nil {
- return nil, err
- }
- if err := sc.roundtrip(); err != nil {
- return nil, err
- }
- if err := sc.roundtrip(); err != nil {
- return nil, err
- }
-
- sc.outputsMu.Lock()
- defer sc.outputsMu.Unlock()
-
- compositor := DetectCompositor()
- result := make([]Output, 0, len(sc.outputs))
- for _, o := range sc.outputs {
- out := Output{
- Name: o.name,
- X: o.x,
- Y: o.y,
- Width: o.width,
- Height: o.height,
- Scale: o.scale,
- FractionalScale: o.fractionalScale,
- Transform: o.transform,
- }
-
- switch compositor {
- case CompositorHyprland:
- if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
- out.X, out.Y = hx, hy
- out.Width, out.Height = hw, hh
- }
- if s := GetHyprlandMonitorScale(o.name); s > 0 {
- out.FractionalScale = s
- }
- }
-
- result = append(result, out)
- }
- return result, nil
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go
deleted file mode 100644
index a178cf0..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/shm.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package screenshot
-
-import "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/shm"
-
-type PixelFormat = shm.PixelFormat
-
-const (
- FormatARGB8888 = shm.FormatARGB8888
- FormatXRGB8888 = shm.FormatXRGB8888
- FormatABGR8888 = shm.FormatABGR8888
- FormatXBGR8888 = shm.FormatXBGR8888
- FormatRGB888 = shm.FormatRGB888
- FormatBGR888 = shm.FormatBGR888
-)
-
-const (
- TransformNormal = shm.TransformNormal
- Transform90 = shm.Transform90
- Transform180 = shm.Transform180
- Transform270 = shm.Transform270
- TransformFlipped = shm.TransformFlipped
- TransformFlipped90 = shm.TransformFlipped90
- TransformFlipped180 = shm.TransformFlipped180
- TransformFlipped270 = shm.TransformFlipped270
-)
-
-type ShmBuffer = shm.Buffer
-
-func CreateShmBuffer(width, height, stride int) (*ShmBuffer, error) {
- return shm.CreateBuffer(width, height, stride)
-}
-
-func InverseTransform(transform int32) int32 {
- return shm.InverseTransform(transform)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go
deleted file mode 100644
index 0ba6c63..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/state.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package screenshot
-
-import (
- "encoding/json"
- "os"
- "path"
- "path/filepath"
-)
-
-type PersistentState struct {
- LastRegion Region `json:"last_region"`
-}
-
-func getStateFilePath() string {
- cacheDir, err := os.UserCacheDir()
- if err != nil {
- cacheDir = path.Join(os.Getenv("HOME"), ".cache")
- }
- return filepath.Join(cacheDir, "dms", "screenshot-state.json")
-}
-
-func LoadState() (*PersistentState, error) {
- path := getStateFilePath()
- data, err := os.ReadFile(path)
- if err != nil {
- if os.IsNotExist(err) {
- return &PersistentState{}, nil
- }
- return nil, err
- }
-
- var state PersistentState
- if err := json.Unmarshal(data, &state); err != nil {
- return &PersistentState{}, nil
- }
- return &state, nil
-}
-
-func SaveState(state *PersistentState) error {
- path := getStateFilePath()
- dir := filepath.Dir(path)
- if err := os.MkdirAll(dir, 0o755); err != nil {
- return err
- }
-
- data, err := json.MarshalIndent(state, "", " ")
- if err != nil {
- return err
- }
- return os.WriteFile(path, data, 0o644)
-}
-
-func GetLastRegion() Region {
- state, err := LoadState()
- if err != nil {
- return Region{}
- }
- return state.LastRegion
-}
-
-func SaveLastRegion(r Region) error {
- state, _ := LoadState()
- state.LastRegion = r
- return SaveState(state)
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go
deleted file mode 100644
index a2bd5f6..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/theme.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package screenshot
-
-import (
- "encoding/json"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
-)
-
-type ThemeColors struct {
- Background string `json:"surface"`
- OnSurface string `json:"on_surface"`
- Primary string `json:"primary"`
-}
-
-type ColorScheme struct {
- Dark ThemeColors `json:"dark"`
- Light ThemeColors `json:"light"`
-}
-
-type ColorsFile struct {
- Colors ColorScheme `json:"colors"`
-}
-
-var cachedStyle *OverlayStyle
-
-func LoadOverlayStyle() OverlayStyle {
- if cachedStyle != nil {
- return *cachedStyle
- }
-
- style := DefaultOverlayStyle
- colors := loadColorsFile()
- if colors == nil {
- cachedStyle = &style
- return style
- }
-
- theme := &colors.Dark
- if isLightMode() {
- theme = &colors.Light
- }
-
- if bg, ok := parseHexColor(theme.Background); ok {
- style.BackgroundR, style.BackgroundG, style.BackgroundB = bg[0], bg[1], bg[2]
- }
- if text, ok := parseHexColor(theme.OnSurface); ok {
- style.TextR, style.TextG, style.TextB = text[0], text[1], text[2]
- }
- if accent, ok := parseHexColor(theme.Primary); ok {
- style.AccentR, style.AccentG, style.AccentB = accent[0], accent[1], accent[2]
- }
-
- cachedStyle = &style
- return style
-}
-
-func loadColorsFile() *ColorScheme {
- path := getColorsFilePath()
- data, err := os.ReadFile(path)
- if err != nil {
- return nil
- }
-
- var file ColorsFile
- if err := json.Unmarshal(data, &file); err != nil {
- return nil
- }
-
- return &file.Colors
-}
-
-func getColorsFilePath() string {
- cacheDir, err := os.UserCacheDir()
- if err != nil {
- log.Error("Failed to get user cache dir", "err", err)
- return ""
- }
- return filepath.Join(cacheDir, "DankMaterialShell", "dms-colors.json")
-}
-
-func isLightMode() bool {
- scheme, err := utils.GsettingsGet("org.gnome.desktop.interface", "color-scheme")
- if err != nil {
- return false
- }
-
- switch scheme {
- case "'prefer-light'", "'default'":
- return true
- }
- return false
-}
-
-func parseHexColor(hex string) ([3]uint8, bool) {
- hex = strings.TrimPrefix(hex, "#")
- if len(hex) != 6 {
- return [3]uint8{}, false
- }
-
- var r, g, b uint8
- for i, ptr := range []*uint8{&r, &g, &b} {
- val := 0
- for j := 0; j < 2; j++ {
- c := hex[i*2+j]
- val *= 16
- switch {
- case c >= '0' && c <= '9':
- val += int(c - '0')
- case c >= 'a' && c <= 'f':
- val += int(c - 'a' + 10)
- case c >= 'A' && c <= 'F':
- val += int(c - 'A' + 10)
- default:
- return [3]uint8{}, false
- }
- }
- *ptr = uint8(val)
- }
-
- return [3]uint8{r, g, b}, true
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go
deleted file mode 100644
index 00cbe1c..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/screenshot/types.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package screenshot
-
-type Mode int
-
-const (
- ModeRegion Mode = iota
- ModeWindow
- ModeFullScreen
- ModeAllScreens
- ModeOutput
- ModeLastRegion
-)
-
-type Format int
-
-const (
- FormatPNG Format = iota
- FormatJPEG
- FormatPPM
-)
-
-type CursorMode int
-
-const (
- CursorOff CursorMode = iota
- CursorOn
-)
-
-type Region struct {
- X int32 `json:"x"`
- Y int32 `json:"y"`
- Width int32 `json:"width"`
- Height int32 `json:"height"`
- Output string `json:"output,omitempty"`
-}
-
-func (r Region) IsEmpty() bool {
- return r.Width <= 0 || r.Height <= 0
-}
-
-type Output struct {
- Name string
- X, Y int32
- Width int32
- Height int32
- Scale int32
- FractionalScale float64
- Transform int32
-}
-
-type Config struct {
- Mode Mode
- OutputName string
- Cursor CursorMode
- NoConfirm bool
- Reset bool
- Format Format
- Quality int
- OutputDir string
- Filename string
- Clipboard bool
- SaveFile bool
- Notify bool
- Stdout bool
-}
-
-func DefaultConfig() Config {
- return Config{
- Mode: ModeRegion,
- Cursor: CursorOff,
- NoConfirm: false,
- Reset: false,
- Format: FormatPNG,
- Quality: 90,
- OutputDir: "",
- Filename: "",
- Clipboard: true,
- SaveFile: true,
- Notify: true,
- }
-}