Skip to content

Commit

Permalink
Merge pull request #27 from kbinani/wayland
Browse files Browse the repository at this point in the history
Add a fallback capture method for Wayland
  • Loading branch information
kbinani authored Aug 19, 2024
2 parents e520a41 + f16dc0e commit 30c18db
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 18 deletions.
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@ require (
github.com/lxn/win v0.0.0-20210218163916-a377121e959e
)

require golang.org/x/sys v0.11.0 // indirect
require (
github.com/godbus/dbus/v5 v5.1.0 // indirect
golang.org/x/sys v0.11.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/gen2brain/shm v0.0.0-20230802011745-f2460f5984f7 h1:VLEKvjGJYAMCXw0/32r9io61tEXnMWDRxMk+peyRVFc=
github.com/gen2brain/shm v0.0.0-20230802011745-f2460f5984f7/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk=
github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
Expand Down
17 changes: 17 additions & 0 deletions internal/dbus_available.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//go:build !s390x && !ppc64le && !darwin && !windows && (linux || openbsd || netbsd)

package internal

import (
"image"
"os"
)

func Capture(x, y, width, height int) (img *image.RGBA, e error) {
sessionType := os.Getenv("XDG_SESSION_TYPE")
if sessionType == "wayland" {
return captureDbus(x, y, width, height)
} else {
return captureXinerama(x, y, width, height)
}
}
11 changes: 11 additions & 0 deletions internal/dbus_unavailable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build freebsd

package internal

import (
"image"
)

func Capture(x, y, width, height int) (img *image.RGBA, e error) {
return captureXinerama(x, y, width, height)
}
2 changes: 1 addition & 1 deletion internal/util/util.go → internal/util.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package internal

import (
"errors"
Expand Down
97 changes: 97 additions & 0 deletions internal/wayland.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//go:build !s390x && !ppc64le && !darwin && !windows && !freebsd && (linux || openbsd || netbsd)

package internal

import (
"fmt"
"github.com/godbus/dbus/v5"
"image"
"image/draw"
"image/png"
"net/url"
"os"
"sync/atomic"
)

var gCounter uint64 = 0

func captureDbus(x, y, width, height int) (img *image.RGBA, e error) {
c, err := dbus.ConnectSessionBus()
if err != nil {
return nil, fmt.Errorf("dbus.SessionBus() failed: %v", err)
}
defer func(c *dbus.Conn) {
err := c.Close()
if err != nil {
e = err
}
}(c)
token := atomic.AddUint64(&gCounter, 1)
options := map[string]dbus.Variant{
"modal": dbus.MakeVariant(false),
"interactive": dbus.MakeVariant(false),
"handle_token": dbus.MakeVariant(token),
}
obj := c.Object("org.freedesktop.portal.Desktop", dbus.ObjectPath("/org/freedesktop/portal/desktop"))
call := obj.Call("org.freedesktop.portal.Screenshot.Screenshot", 0, "", options)
var path dbus.ObjectPath
err = call.Store(&path)
if err != nil {
return nil, fmt.Errorf("dbus.Store() failed: %v", err)
}
ch := make(chan *dbus.Message)
c.Eavesdrop(ch)
for msg := range ch {
o, ok := msg.Headers[dbus.FieldPath]
if !ok {
continue
}
s, ok := o.Value().(dbus.ObjectPath)
if !ok {
return nil, fmt.Errorf("dbus.FieldPath value does't have ObjectPath type")
}
if s != path {
continue
}
for _, body := range msg.Body {
v, ok := body.(map[string]dbus.Variant)
if !ok {
continue
}
uri, ok := v["uri"]
if !ok {
continue
}
path, ok := uri.Value().(string)
if !ok {
return nil, fmt.Errorf("uri is not a string")
}
fpath, err := url.Parse(path)
if err != nil {
return nil, fmt.Errorf("url.Parse(%v) failed: %v", path, err)
}
if fpath.Scheme != "file" {
return nil, fmt.Errorf("uri is not a file path")
}
file, err := os.Open(fpath.Path)
if err != nil {
return nil, fmt.Errorf("os.Open(%s) failed: %v", path, err)
}
defer func(file *os.File) {
_ = file.Close()
_ = os.Remove(fpath.Path)
}(file)
img, err := png.Decode(file)
if err != nil {
return nil, fmt.Errorf("png.Decode(%s) failed: %v", path, err)
}
canvas, err := CreateImage(image.Rect(0, 0, width, height))
if err != nil {
return nil, fmt.Errorf("util.CreateImage(%v) failed: %v", path, err)
}
draw.Draw(canvas, image.Rect(0, 0, width, height), img, image.Point{x, y}, draw.Src)
return canvas, e
}
}
return nil, fmt.Errorf("dbus.Message doesn't contain uri")
}
14 changes: 7 additions & 7 deletions internal/xwindow/xwindow.go → internal/xwindow.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package xwindow
//go:build !s390x && !ppc64le && !darwin && !windows && (linux || freebsd || openbsd || netbsd)

package internal

import (
"fmt"
"image"
"image/color"

"github.com/kbinani/screenshot/internal/util"
"github.com/gen2brain/shm"
"github.com/jezek/xgb"
mshm "github.com/jezek/xgb/shm"
"github.com/jezek/xgb/xinerama"
"github.com/jezek/xgb/xproto"
"image"
"image/color"
)

func Capture(x, y, width, height int) (img *image.RGBA, e error) {
func captureXinerama(x, y, width, height int) (img *image.RGBA, e error) {
defer func() {
err := recover()
if err != nil {
Expand Down Expand Up @@ -53,7 +53,7 @@ func Capture(x, y, width, height int) (img *image.RGBA, e error) {
intersect := wholeScreenBounds.Intersect(targetBounds)

rect := image.Rect(0, 0, width, height)
img, err = util.CreateImage(rect)
img, err = CreateImage(rect)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions screenshot_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import (
"image"
"unsafe"

"github.com/kbinani/screenshot/internal/util"
"github.com/kbinani/screenshot/internal"
)

func Capture(x, y, width, height int) (*image.RGBA, error) {
Expand All @@ -81,7 +81,7 @@ func Capture(x, y, width, height int) (*image.RGBA, error) {
}

rect := image.Rect(0, 0, width, height)
img, err := util.CreateImage(rect)
img, err := internal.CreateImage(rect)
if err != nil {
return nil, err
}
Expand Down
9 changes: 4 additions & 5 deletions screenshot_supported.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@
package screenshot

import (
"github.com/kbinani/screenshot/internal"
"image"

"github.com/kbinani/screenshot/internal/xwindow"
)

// Capture returns screen capture of specified desktop region.
// x and y represent distance from the upper-left corner of primary display.
// Y-axis is downward direction. This means coordinates system is similar to Windows OS.
func Capture(x, y, width, height int) (*image.RGBA, error) {
return xwindow.Capture(x, y, width, height)
return internal.Capture(x, y, width, height)
}

// NumActiveDisplays returns the number of active displays.
func NumActiveDisplays() int {
return xwindow.NumActiveDisplays()
return internal.NumActiveDisplays()
}

// GetDisplayBounds returns the bounds of displayIndex'th display.
// The main display is displayIndex = 0.
func GetDisplayBounds(displayIndex int) image.Rectangle {
return xwindow.GetDisplayBounds(displayIndex)
return internal.GetDisplayBounds(displayIndex)
}
4 changes: 2 additions & 2 deletions screenshot_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package screenshot

import (
"errors"
"github.com/kbinani/screenshot/internal/util"
"github.com/kbinani/screenshot/internal"
win "github.com/lxn/win"
"image"
"syscall"
Expand All @@ -19,7 +19,7 @@ var (

func Capture(x, y, width, height int) (*image.RGBA, error) {
rect := image.Rect(0, 0, width, height)
img, err := util.CreateImage(rect)
img, err := internal.CreateImage(rect)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 30c18db

Please sign in to comment.