dt_automate/vendor/github.com/kbinani/screenshot/nix_xwindow.go
2025-02-19 18:30:19 +08:00

135 lines
2.8 KiB
Go

//go:build !s390x && !ppc64le && !darwin && !windows && (linux || freebsd || openbsd || netbsd)
package screenshot
import (
"fmt"
"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 captureXinerama(x, y, width, height int) (img *image.RGBA, e error) {
defer func() {
err := recover()
if err != nil {
img = nil
e = fmt.Errorf("%v", err)
}
}()
c, err := xgb.NewConn()
if err != nil {
return nil, err
}
defer c.Close()
err = xinerama.Init(c)
if err != nil {
return nil, err
}
reply, err := xinerama.QueryScreens(c).Reply()
if err != nil {
return nil, err
}
primary := reply.ScreenInfo[0]
x0 := int(primary.XOrg)
y0 := int(primary.YOrg)
useShm := true
err = mshm.Init(c)
if err != nil {
useShm = false
}
screen := xproto.Setup(c).DefaultScreen(c)
wholeScreenBounds := image.Rect(0, 0, int(screen.WidthInPixels), int(screen.HeightInPixels))
targetBounds := image.Rect(x+x0, y+y0, x+x0+width, y+y0+height)
intersect := wholeScreenBounds.Intersect(targetBounds)
rect := image.Rect(0, 0, width, height)
img, err = createImage(rect)
if err != nil {
return nil, err
}
// Paint with opaque black
index := 0
for iy := 0; iy < height; iy++ {
j := index
for ix := 0; ix < width; ix++ {
img.Pix[j+3] = 255
j += 4
}
index += img.Stride
}
if !intersect.Empty() {
var data []byte
if useShm {
shmSize := intersect.Dx() * intersect.Dy() * 4
shmId, err := shm.Get(shm.IPC_PRIVATE, shmSize, shm.IPC_CREAT|0777)
if err != nil {
return nil, err
}
seg, err := mshm.NewSegId(c)
if err != nil {
return nil, err
}
data, err = shm.At(shmId, 0, 0)
if err != nil {
return nil, err
}
mshm.Attach(c, seg, uint32(shmId), false)
defer mshm.Detach(c, seg)
defer func() {
_ = shm.Rm(shmId)
}()
defer func() {
_ = shm.Dt(data)
}()
_, err = mshm.GetImage(c, xproto.Drawable(screen.Root),
int16(intersect.Min.X), int16(intersect.Min.Y),
uint16(intersect.Dx()), uint16(intersect.Dy()), 0xffffffff,
byte(xproto.ImageFormatZPixmap), seg, 0).Reply()
if err != nil {
return nil, err
}
} else {
xImg, err := xproto.GetImage(c, xproto.ImageFormatZPixmap, xproto.Drawable(screen.Root),
int16(intersect.Min.X), int16(intersect.Min.Y),
uint16(intersect.Dx()), uint16(intersect.Dy()), 0xffffffff).Reply()
if err != nil {
return nil, err
}
data = xImg.Data
}
// BitBlt by hand
offset := 0
for iy := intersect.Min.Y; iy < intersect.Max.Y; iy++ {
for ix := intersect.Min.X; ix < intersect.Max.X; ix++ {
r := data[offset+2]
g := data[offset+1]
b := data[offset]
img.SetRGBA(ix-(x+x0), iy-(y+y0), color.RGBA{r, g, b, 255})
offset += 4
}
}
}
return img, e
}