Golang实现Wayland环境下的屏幕截图
Golang实现Wayland环境下的屏幕截图 我想用Go语言捕获屏幕截图。
在X11环境下一切正常。但在Wayland环境下,我得到的是黑屏。
我尝试通过DBUS捕获屏幕,成功了。
但我希望捕获屏幕并保存到同一文件夹中(不闪烁 + 不弹出提示)。
请帮我修复这个问题。
以下是我正在使用的代码:
package main
import (
"fmt"
"io"
"log"
"net/http"
"os"
"github.com/godbus/dbus"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
log.Fatalf("Failed to connect to session bus: %v", err)
}
defer conn.Close()
dest := "org.freedesktop.portal.Desktop"
path := "/org/freedesktop/portal/desktop"
iface := "org.freedesktop.portal.Screenshot"
options := map[string]dbus.Variant{
"Path": dbus.MakeVariant("/home/capture-wayland/screenshot.png"),
"Flash": dbus.MakeVariant(false),
}
obj := conn.Object(dest, dbus.ObjectPath(path))
caller := obj.Call(iface+".Screenshot", 0, "", options)
fmt.Println(caller)
fmt.Println(caller.Path)
fmt.Println(caller.Destination)
fmt.Println(caller.Method)
fmt.Println(caller.Args)
var response map[string]dbus.Variant
// caller.Store(&response)
// fmt.Println("response:", response)
// a, _ := json.Marshal(caller)
// fmt.Println("caller string:", string(a))
_ = obj.Call(iface+".Screenshot", 1, "", options).Store(&response)
fmt.Println(response)
if err != nil {
log.Fatalf("Failed to call Screenshot method: %v", err)
}
uri, ok := response["uri"]
if !ok {
log.Fatal("No URI found in the response")
}
// Download the image from the URI and save it to image.png
downloadAndSave(uri.Value().(string), "image.png")
}
func downloadAndSave(url, filename string) {
resp, err := http.Get(url)
if err != nil {
log.Fatalf("Failed to download the image: %v", err)
}
defer resp.Body.Close()
out, err := os.Create(filename)
if err != nil {
log.Fatalf("Failed to create the image file: %v", err)
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
log.Fatalf("Failed to save the image: %v", err)
}
fmt.Println("Saved screenshot to", filename)
}
更多关于Golang实现Wayland环境下的屏幕截图的实战教程也可以访问 https://www.itying.com/category-94-b0.html
duckduck:
除了通过
dbus的途径,另一种方法是使用这个库。我已在 Windows 和 Darwin 上成功使用,但不确定它是否能在 X 或 Wayland 上工作。GitHub - kbinani/screenshot: Go library to capture desktop to image
我已经使用过这个库了。它在 Wayland 上不工作,所以我转而使用 dbus。
更多关于Golang实现Wayland环境下的屏幕截图的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
除了通过 dbus 的途径外,另一种方法是使用这个库,我已在 Windows 和 Darwin 上成功使用,但不确定它是否适用于 X 或 Wayland。
GitHub - kbinani/screenshot: Go library to capture desktop to image
Go library to capture desktop to image. Contribute to kbinani/screenshot development by creating an account on GitHub.
在Wayland环境下通过DBus捕获屏幕截图时,需要正确处理portal API的异步响应。以下是修复后的代码:
package main
import (
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"path/filepath"
"github.com/godbus/dbus"
)
func main() {
conn, err := dbus.SessionBus()
if err != nil {
log.Fatalf("Failed to connect to session bus: %v", err)
}
defer conn.Close()
dest := "org.freedesktop.portal.Desktop"
path := "/org/freedesktop/portal/desktop"
iface := "org.freedesktop.portal.Screenshot"
// 获取当前工作目录
cwd, err := os.Getwd()
if err != nil {
log.Fatalf("Failed to get current directory: %v", err)
}
// 构建完整的文件路径
filePath := filepath.Join(cwd, "screenshot.png")
fileURI := "file://" + url.PathEscape(filePath)
options := map[string]dbus.Variant{
"handle_token": dbus.MakeVariant("go_screenshot"),
"modal": dbus.MakeVariant(false),
}
obj := conn.Object(dest, dbus.ObjectPath(path))
// 创建信号通道
signalChan := make(chan *dbus.Signal, 10)
conn.Signal(signalChan)
defer conn.RemoveSignal(signalChan)
// 设置信号匹配
matchRule := fmt.Sprintf(
"type='signal',interface='org.freedesktop.portal.Request',path='%s'",
"/org/freedesktop/portal/desktop/request/go_screenshot",
)
conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, matchRule)
// 调用Screenshot方法
call := obj.Call(iface+".Screenshot", 0, "", options)
if call.Err != nil {
log.Fatalf("Failed to call Screenshot method: %v", call.Err)
}
// 等待响应
var requestPath dbus.ObjectPath
if err := call.Store(&requestPath); err != nil {
log.Fatalf("Failed to store request path: %v", err)
}
// 监听响应信号
for signal := range signalChan {
if signal.Name == "org.freedesktop.portal.Request.Response" &&
len(signal.Body) >= 2 {
responseCode := signal.Body[0].(uint32)
results := signal.Body[1].(map[string]dbus.Variant)
if responseCode == 0 {
if uri, ok := results["uri"]; ok {
fileURL := uri.Value().(string)
// 如果是file:// URI,直接复制文件
if len(fileURL) > 7 && fileURL[:7] == "file://" {
srcPath, _ := url.PathUnescape(fileURL[7:])
copyFile(srcPath, filePath)
fmt.Printf("Screenshot saved to: %s\n", filePath)
return
}
// 否则下载文件
downloadAndSave(fileURL, filePath)
fmt.Printf("Screenshot saved to: %s\n", filePath)
return
}
}
log.Fatalf("Screenshot request failed with code: %d", responseCode)
}
}
}
func copyFile(src, dst string) {
srcFile, err := os.Open(src)
if err != nil {
log.Fatalf("Failed to open source file: %v", err)
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
log.Fatalf("Failed to create destination file: %v", err)
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
log.Fatalf("Failed to copy file: %v", err)
}
}
func downloadAndSave(urlStr, filename string) {
resp, err := http.Get(urlStr)
if err != nil {
log.Fatalf("Failed to download the image: %v", err)
}
defer resp.Body.Close()
out, err := os.Create(filename)
if err != nil {
log.Fatalf("Failed to create the image file: %v", err)
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
log.Fatalf("Failed to save the image: %v", err)
}
}
关键修复点:
- 添加handle_token:portal API需要唯一的handle_token来标识请求
- 正确处理异步响应:通过DBus信号监听响应,而不是同步等待
- 设置modal为false:避免弹出对话框
- 处理file:// URI:直接复制文件而不是重新下载
- 使用当前工作目录:确保文件保存到程序所在目录
这个实现会在Wayland环境下无闪烁、无提示地捕获屏幕截图,并保存到程序运行的同一文件夹中。

