forked from kbinani/screenshot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
screenshot_windows.go
135 lines (115 loc) · 3.6 KB
/
screenshot_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package screenshot
import (
"errors"
"github.com/brendanporter/screenshot-util"
win "github.com/lxn/win"
"image"
"syscall"
"unsafe"
)
var (
libUser32, _ = syscall.LoadLibrary("user32.dll")
funcGetDesktopWindow, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "GetDesktopWindow")
funcEnumDisplayMonitors, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "EnumDisplayMonitors")
)
func Capture(x, y, width, height int) (*image.RGBA, error) {
rect := image.Rect(0, 0, width, height)
img, err := util.CreateImage(rect)
if err != nil {
return nil, err
}
hwnd := getDesktopWindow()
hdc := win.GetDC(hwnd)
if hdc == 0 {
return nil, errors.New("GetDC failed")
}
defer win.ReleaseDC(hwnd, hdc)
memory_device := win.CreateCompatibleDC(hdc)
if memory_device == 0 {
return nil, errors.New("CreateCompatibleDC failed")
}
defer win.DeleteDC(memory_device)
bitmap := win.CreateCompatibleBitmap(hdc, int32(width), int32(height))
if bitmap == 0 {
return nil, errors.New("CreateCompatibleBitmap failed")
}
defer win.DeleteObject(win.HGDIOBJ(bitmap))
var header win.BITMAPINFOHEADER
header.BiSize = uint32(unsafe.Sizeof(header))
header.BiPlanes = 1
header.BiBitCount = 32
header.BiWidth = int32(width)
header.BiHeight = int32(-height)
header.BiCompression = win.BI_RGB
header.BiSizeImage = 0
old := win.SelectObject(memory_device, win.HGDIOBJ(bitmap))
if old == 0 {
return nil, errors.New("SelectObject failed")
}
defer win.SelectObject(memory_device, old)
if !win.BitBlt(memory_device, 0, 0, int32(width), int32(height), hdc, int32(x), int32(y), win.SRCCOPY) {
return nil, errors.New("BitBlt failed")
}
if win.GetDIBits(hdc, bitmap, 0, uint32(height), (*byte)(unsafe.Pointer(&img.Pix[0])), (*win.BITMAPINFO)(unsafe.Pointer(&header)), win.DIB_RGB_COLORS) == 0 {
return nil, errors.New("GetDIBits failed")
}
i := 0
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
// BGRA => RGBA, and set A to 255
img.Pix[i], img.Pix[i+2], img.Pix[i+3] = img.Pix[i+2], img.Pix[i], 255
i += 4
}
}
return img, nil
}
func NumActiveDisplays() int {
var count int = 0
enumDisplayMonitors(win.HDC(0), nil, syscall.NewCallback(countupMonitorCallback), uintptr(unsafe.Pointer(&count)))
return count
}
func GetDisplayBounds(displayIndex int) image.Rectangle {
var ctx getMonitorBoundsContext
ctx.Index = displayIndex
ctx.Count = 0
enumDisplayMonitors(win.HDC(0), nil, syscall.NewCallback(getMonitorBoundsCallback), uintptr(unsafe.Pointer(&ctx)))
return image.Rect(
int(ctx.Rect.Left), int(ctx.Rect.Top),
int(ctx.Rect.Right), int(ctx.Rect.Bottom))
}
func getDesktopWindow() win.HWND {
ret, _, _ := syscall.Syscall(funcGetDesktopWindow, 0, 0, 0, 0)
return win.HWND(ret)
}
func enumDisplayMonitors(hdc win.HDC, lprcClip *win.RECT, lpfnEnum uintptr, dwData uintptr) bool {
ret, _, _ := syscall.Syscall6(funcEnumDisplayMonitors, 4,
uintptr(hdc),
uintptr(unsafe.Pointer(lprcClip)),
lpfnEnum,
dwData,
0,
0)
return int(ret) != 0
}
func countupMonitorCallback(hMonitor win.HMONITOR, hdcMonitor win.HDC, lprcMonitor *win.RECT, dwData uintptr) uintptr {
var count *int
count = (*int)(unsafe.Pointer(dwData))
*count = *count + 1
return uintptr(1)
}
type getMonitorBoundsContext struct {
Index int
Rect win.RECT
Count int
}
func getMonitorBoundsCallback(hMonitor win.HMONITOR, hdcMonitor win.HDC, lprcMonitor *win.RECT, dwData uintptr) uintptr {
var ctx *getMonitorBoundsContext
ctx = (*getMonitorBoundsContext)(unsafe.Pointer(dwData))
if ctx.Count == ctx.Index {
ctx.Rect = *lprcMonitor
return uintptr(0)
} else {
ctx.Count = ctx.Count + 1
return uintptr(1)
}
}