mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-30 22:13:54 +00:00
move bitmap to bitmap.go and move ocr to img.go
This commit is contained in:
parent
4782da7e77
commit
cae09b8577
@ -1,6 +1,6 @@
|
||||
#include "file_io.h"
|
||||
// #include "os.h"
|
||||
// #include "bmp_io_c.h"
|
||||
#include "bmp_io_c.h"
|
||||
#include "png_io_c.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <string.h> /* For strcmp() */
|
||||
|
555
bitmap.go
Normal file
555
bitmap.go
Normal file
@ -0,0 +1,555 @@
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
package robotgo
|
||||
|
||||
/*
|
||||
#cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/cdeps/mac/amd -lpng -lz
|
||||
#cgo darwin,arm64 LDFLAGS:-L${SRCDIR}/cdeps/mac/m1 -lpng -lz
|
||||
//
|
||||
#cgo linux LDFLAGS: -L/usr/src -lpng -lz
|
||||
//
|
||||
#cgo windows,amd64 LDFLAGS: -L${SRCDIR}/cdeps/win/amd/win64 -lpng -lz
|
||||
#cgo windows,386 LDFLAGS: -L${SRCDIR}/cdeps/win/amd/win32 -lpng -lz
|
||||
#cgo windows,arm64 LDFLAGS:-L${SRCDIR}/cdeps/win/arm -lpng -lz
|
||||
//
|
||||
//#include "screen/goScreen.h"
|
||||
#include "bitmap/goBitmap.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"image"
|
||||
"unsafe"
|
||||
|
||||
"github.com/vcaesar/tt"
|
||||
)
|
||||
|
||||
/*
|
||||
.______ __ .___________..___ ___. ___ .______
|
||||
| _ \ | | | || \/ | / \ | _ \
|
||||
| |_) | | | `---| |----`| \ / | / ^ \ | |_) |
|
||||
| _ < | | | | | |\/| | / /_\ \ | ___/
|
||||
| |_) | | | | | | | | | / _____ \ | |
|
||||
|______/ |__| |__| |__| |__| /__/ \__\ | _|
|
||||
*/
|
||||
|
||||
// ToBitmap trans C.MMBitmapRef to Bitmap
|
||||
func ToBitmap(bit C.MMBitmapRef) Bitmap {
|
||||
bitmap := Bitmap{
|
||||
ImgBuf: (*uint8)(bit.imageBuffer),
|
||||
Width: int(bit.width),
|
||||
Height: int(bit.height),
|
||||
Bytewidth: int(bit.bytewidth),
|
||||
BitsPixel: uint8(bit.bitsPerPixel),
|
||||
BytesPerPixel: uint8(bit.bytesPerPixel),
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
// ToCBitmap trans Bitmap to C.MMBitmapRef
|
||||
func ToCBitmap(bit Bitmap) C.MMBitmapRef {
|
||||
cbitmap := C.createMMBitmap(
|
||||
(*C.uint8_t)(bit.ImgBuf),
|
||||
C.size_t(bit.Width),
|
||||
C.size_t(bit.Height),
|
||||
C.size_t(bit.Bytewidth),
|
||||
C.uint8_t(bit.BitsPixel),
|
||||
C.uint8_t(bit.BytesPerPixel),
|
||||
)
|
||||
|
||||
return cbitmap
|
||||
}
|
||||
|
||||
// ToBitmapBytes saves Bitmap to bitmap format in bytes
|
||||
func ToBitmapBytes(bit C.MMBitmapRef) []byte {
|
||||
var len C.size_t
|
||||
ptr := C.saveMMBitmapAsBytes(bit, &len)
|
||||
if int(len) < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bs := C.GoBytes(unsafe.Pointer(ptr), C.int(len))
|
||||
C.free(unsafe.Pointer(ptr))
|
||||
return bs
|
||||
}
|
||||
|
||||
// ToMMBitmapRef trans CBitmap to C.MMBitmapRef
|
||||
func ToMMBitmapRef(bit CBitmap) C.MMBitmapRef {
|
||||
return C.MMBitmapRef(bit)
|
||||
}
|
||||
|
||||
// TostringBitmap tostring bitmap to string
|
||||
func TostringBitmap(bit C.MMBitmapRef) string {
|
||||
strBit := C.tostring_bitmap(bit)
|
||||
return C.GoString(strBit)
|
||||
}
|
||||
|
||||
// TocharBitmap tostring bitmap to C.char
|
||||
func TocharBitmap(bit C.MMBitmapRef) *C.char {
|
||||
strBit := C.tostring_bitmap(bit)
|
||||
return strBit
|
||||
}
|
||||
|
||||
// ToImage convert C.MMBitmapRef to standard image.Image
|
||||
func ToImage(bit C.MMBitmapRef) image.Image {
|
||||
return ToRGBA(bit)
|
||||
}
|
||||
|
||||
// ToRGBA convert C.MMBitmapRef to standard image.RGBA
|
||||
func ToRGBA(bit C.MMBitmapRef) *image.RGBA {
|
||||
bmp1 := ToBitmap(bit)
|
||||
return ToRGBAGo(bmp1)
|
||||
}
|
||||
|
||||
func internalFindBitmap(bit, sbit C.MMBitmapRef, tolerance float64) (int, int) {
|
||||
pos := C.find_bitmap(bit, sbit, C.float(tolerance))
|
||||
// fmt.Println("pos----", pos)
|
||||
return int(pos.x), int(pos.y)
|
||||
}
|
||||
|
||||
// FindCBitmap find bitmap's pos by CBitmap
|
||||
func FindCBitmap(bmp CBitmap, args ...interface{}) (int, int) {
|
||||
return FindBitmap(ToMMBitmapRef(bmp), args...)
|
||||
}
|
||||
|
||||
// FindBitmap find the bitmap's pos
|
||||
//
|
||||
// robotgo.FindBitmap(bitmap, source_bitamp C.MMBitmapRef, tolerance float64)
|
||||
//
|
||||
// |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
// colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
//
|
||||
// This method only automatically free the internal bitmap,
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
func FindBitmap(bit C.MMBitmapRef, args ...interface{}) (int, int) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance = 0.01
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = args[1].(float64)
|
||||
}
|
||||
|
||||
fx, fy := internalFindBitmap(bit, sbit, tolerance)
|
||||
// FreeBitmap(bit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
|
||||
return fx, fy
|
||||
}
|
||||
|
||||
// FindPic finding the image by path
|
||||
//
|
||||
// robotgo.FindPic(path string, source_bitamp C.MMBitmapRef, tolerance float64)
|
||||
//
|
||||
// This method only automatically free the internal bitmap,
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
func FindPic(path string, args ...interface{}) (int, int) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance = 0.01
|
||||
)
|
||||
|
||||
openbit := OpenBitmap(path)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = args[1].(float64)
|
||||
}
|
||||
|
||||
fx, fy := internalFindBitmap(openbit, sbit, tolerance)
|
||||
FreeBitmap(openbit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
|
||||
return fx, fy
|
||||
}
|
||||
|
||||
// FreeMMPointArr free MMPoint array
|
||||
func FreeMMPointArr(pointArray C.MMPointArrayRef) {
|
||||
C.destroyMMPointArray(pointArray)
|
||||
}
|
||||
|
||||
// FindEveryBitmap find the every bitmap
|
||||
func FindEveryBitmap(bit C.MMBitmapRef, args ...interface{}) (posArr []Point) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance C.float = 0.01
|
||||
lpos C.MMPoint
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = 0
|
||||
} else {
|
||||
lpos.x = 0
|
||||
lpos.y = 0
|
||||
}
|
||||
|
||||
if len(args) > 3 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = C.size_t(args[3].(int))
|
||||
}
|
||||
|
||||
pos := C.find_every_bitmap(bit, sbit, tolerance, &lpos)
|
||||
// FreeBitmap(bit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
if pos == nil {
|
||||
return
|
||||
}
|
||||
defer FreeMMPointArr(pos)
|
||||
|
||||
cSize := pos.count
|
||||
cArray := pos.array
|
||||
gSlice := (*[(1 << 28) - 1]C.MMPoint)(unsafe.Pointer(cArray))[:cSize:cSize]
|
||||
for i := 0; i < len(gSlice); i++ {
|
||||
posArr = append(posArr, Point{
|
||||
X: int(gSlice[i].x),
|
||||
Y: int(gSlice[i].y),
|
||||
})
|
||||
}
|
||||
|
||||
// fmt.Println("pos----", pos)
|
||||
return
|
||||
}
|
||||
|
||||
// CountBitmap count of the bitmap
|
||||
func CountBitmap(bitmap, sbit C.MMBitmapRef, args ...float32) int {
|
||||
var tolerance C.float = 0.01
|
||||
if len(args) > 0 {
|
||||
tolerance = C.float(args[0])
|
||||
}
|
||||
|
||||
count := C.count_of_bitmap(bitmap, sbit, tolerance)
|
||||
return int(count)
|
||||
}
|
||||
|
||||
// BitmapClick find the bitmap and click
|
||||
func BitmapClick(bitmap C.MMBitmapRef, args ...interface{}) {
|
||||
x, y := FindBitmap(bitmap)
|
||||
MovesClick(x, y, args...)
|
||||
}
|
||||
|
||||
// PointInBounds bitmap point in bounds
|
||||
func PointInBounds(bitmap C.MMBitmapRef, x, y int) bool {
|
||||
var point C.MMPoint
|
||||
point.x = C.size_t(x)
|
||||
point.y = C.size_t(y)
|
||||
cbool := C.point_in_bounds(bitmap, point)
|
||||
|
||||
return bool(cbool)
|
||||
}
|
||||
|
||||
// OpenBitmap open the bitmap return C.MMBitmapRef
|
||||
//
|
||||
// robotgo.OpenBitmap(path string, type int)
|
||||
func OpenBitmap(gpath string, args ...int) C.MMBitmapRef {
|
||||
path := C.CString(gpath)
|
||||
var mtype C.uint16_t = 1
|
||||
|
||||
if len(args) > 0 {
|
||||
mtype = C.uint16_t(args[0])
|
||||
}
|
||||
|
||||
bit := C.bitmap_open(path, mtype)
|
||||
C.free(unsafe.Pointer(path))
|
||||
|
||||
return bit
|
||||
}
|
||||
|
||||
// BitmapStr bitmap from string
|
||||
func BitmapStr(str string) C.MMBitmapRef {
|
||||
return BitmapFromStr(str)
|
||||
}
|
||||
|
||||
// BitmapFromStr bitmap from string
|
||||
func BitmapFromStr(str string) C.MMBitmapRef {
|
||||
cs := C.CString(str)
|
||||
bit := C.bitmap_from_string(cs)
|
||||
C.free(unsafe.Pointer(cs))
|
||||
|
||||
return bit
|
||||
}
|
||||
|
||||
// SaveBitmap save the bitmap to image
|
||||
//
|
||||
// robotgo.SaveBimap(bitmap C.MMBitmapRef, path string, type int)
|
||||
func SaveBitmap(bitmap C.MMBitmapRef, gpath string, args ...int) string {
|
||||
var mtype C.uint16_t = 1
|
||||
if len(args) > 0 {
|
||||
mtype = C.uint16_t(args[0])
|
||||
}
|
||||
|
||||
path := C.CString(gpath)
|
||||
saveBit := C.bitmap_save(bitmap, path, mtype)
|
||||
C.free(unsafe.Pointer(path))
|
||||
|
||||
return C.GoString(saveBit)
|
||||
}
|
||||
|
||||
// GetPortion get bitmap portion
|
||||
func GetPortion(bit C.MMBitmapRef, x, y, w, h int) C.MMBitmapRef {
|
||||
var rect C.MMRect
|
||||
rect.origin.x = C.size_t(x)
|
||||
rect.origin.y = C.size_t(y)
|
||||
rect.size.width = C.size_t(w)
|
||||
rect.size.height = C.size_t(h)
|
||||
|
||||
pos := C.get_portion(bit, rect)
|
||||
return pos
|
||||
}
|
||||
|
||||
// Convert convert the bitmap
|
||||
//
|
||||
// robotgo.Convert(opath, spath string, type int)
|
||||
func Convert(opath, spath string, args ...int) {
|
||||
var mtype = 1
|
||||
if len(args) > 0 {
|
||||
mtype = args[0]
|
||||
}
|
||||
|
||||
// C.CString()
|
||||
bitmap := OpenBitmap(opath)
|
||||
// fmt.Println("a----", bit_map)
|
||||
SaveBitmap(bitmap, spath, mtype)
|
||||
}
|
||||
|
||||
// FreeBitmap free and dealloc the C bitmap
|
||||
func FreeBitmap(bitmap C.MMBitmapRef) {
|
||||
// C.destroyMMBitmap(bitmap)
|
||||
C.bitmap_dealloc(bitmap)
|
||||
}
|
||||
|
||||
// FreeBitmapArr free and dealloc the C bitmap array
|
||||
func FreeBitmapArr(bit ...C.MMBitmapRef) {
|
||||
for i := 0; i < len(bit); i++ {
|
||||
FreeBitmap(bit[i])
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBitmap returns false and sets error if |bitmap| is NULL
|
||||
func ReadBitmap(bitmap C.MMBitmapRef) bool {
|
||||
abool := C.bitmap_ready(bitmap)
|
||||
gbool := bool(abool)
|
||||
return gbool
|
||||
}
|
||||
|
||||
// CopyBitPB copy bitmap to pasteboard
|
||||
func CopyBitPB(bitmap C.MMBitmapRef) bool {
|
||||
abool := C.bitmap_copy_to_pboard(bitmap)
|
||||
gbool := bool(abool)
|
||||
|
||||
return gbool
|
||||
}
|
||||
|
||||
// Deprecated: CopyBitpb copy bitmap to pasteboard, Wno-deprecated
|
||||
func CopyBitpb(bitmap C.MMBitmapRef) bool {
|
||||
tt.Drop("CopyBitpb", "CopyBitPB")
|
||||
return CopyBitPB(bitmap)
|
||||
}
|
||||
|
||||
// DeepCopyBit deep copy bitmap
|
||||
func DeepCopyBit(bitmap C.MMBitmapRef) C.MMBitmapRef {
|
||||
bit := C.bitmap_deepcopy(bitmap)
|
||||
return bit
|
||||
}
|
||||
|
||||
// GetColor get bitmap color
|
||||
func GetColor(bitmap C.MMBitmapRef, x, y int) C.MMRGBHex {
|
||||
color := C.bitmap_get_color(bitmap, C.size_t(x), C.size_t(y))
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
// GetColors get bitmap color retrun string
|
||||
func GetColors(bitmap C.MMBitmapRef, x, y int) string {
|
||||
clo := GetColor(bitmap, x, y)
|
||||
|
||||
return PadHex(clo)
|
||||
}
|
||||
|
||||
// FindColor find bitmap color
|
||||
//
|
||||
// robotgo.FindColor(color CHex, bitmap C.MMBitmapRef, tolerance float)
|
||||
func FindColor(color CHex, args ...interface{}) (int, int) {
|
||||
var (
|
||||
tolerance C.float = 0.01
|
||||
bitmap C.MMBitmapRef
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
pos := C.bitmap_find_color(bitmap, C.MMRGBHex(color), tolerance)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
x := int(pos.x)
|
||||
y := int(pos.y)
|
||||
|
||||
return x, y
|
||||
}
|
||||
|
||||
// FindColorCS findcolor by CaptureScreen
|
||||
func FindColorCS(color CHex, x, y, w, h int, args ...float64) (int, int) {
|
||||
var tolerance = 0.01
|
||||
|
||||
if len(args) > 0 {
|
||||
tolerance = args[0]
|
||||
}
|
||||
|
||||
bitmap := CaptureScreen(x, y, w, h)
|
||||
rx, ry := FindColor(color, bitmap, tolerance)
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return rx, ry
|
||||
}
|
||||
|
||||
// FindEveryColor find every color
|
||||
func FindEveryColor(color CHex, args ...interface{}) (posArr []Point) {
|
||||
var (
|
||||
bitmap C.MMBitmapRef
|
||||
tolerance C.float = 0.01
|
||||
lpos C.MMPoint
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = 0
|
||||
} else {
|
||||
lpos.x = 0
|
||||
lpos.y = 0
|
||||
}
|
||||
|
||||
if len(args) > 3 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = C.size_t(args[3].(int))
|
||||
}
|
||||
|
||||
pos := C.bitmap_find_every_color(bitmap, C.MMRGBHex(color), tolerance, &lpos)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
if pos == nil {
|
||||
return
|
||||
}
|
||||
defer FreeMMPointArr(pos)
|
||||
|
||||
cSize := pos.count
|
||||
cArray := pos.array
|
||||
gSlice := (*[(1 << 28) - 1]C.MMPoint)(unsafe.Pointer(cArray))[:cSize:cSize]
|
||||
for i := 0; i < len(gSlice); i++ {
|
||||
posArr = append(posArr, Point{
|
||||
X: int(gSlice[i].x),
|
||||
Y: int(gSlice[i].y),
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CountColor count bitmap color
|
||||
func CountColor(color CHex, args ...interface{}) int {
|
||||
var (
|
||||
tolerance C.float = 0.01
|
||||
bitmap C.MMBitmapRef
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
count := C.bitmap_count_of_color(bitmap, C.MMRGBHex(color), tolerance)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
// CountColorCS count bitmap color by CaptureScreen
|
||||
func CountColorCS(color CHex, x, y, w, h int, args ...float64) int {
|
||||
var tolerance = 0.01
|
||||
|
||||
if len(args) > 0 {
|
||||
tolerance = args[0]
|
||||
}
|
||||
|
||||
bitmap := CaptureScreen(x, y, w, h)
|
||||
rx := CountColor(color, bitmap, tolerance)
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return rx
|
||||
}
|
||||
|
||||
// GetImgSize get the image size
|
||||
func GetImgSize(imgPath string) (int, int) {
|
||||
bitmap := OpenBitmap(imgPath)
|
||||
gbit := ToBitmap(bitmap)
|
||||
|
||||
w := gbit.Width / 2
|
||||
h := gbit.Height / 2
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return w, h
|
||||
}
|
22
img.go
22
img.go
@ -12,6 +12,7 @@ package robotgo
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os/exec"
|
||||
"unsafe"
|
||||
|
||||
"github.com/vcaesar/imgo"
|
||||
@ -157,3 +158,24 @@ func copyToVUint8A(dst []uint8, src *uint8) {
|
||||
dst[i+3] = val(src, i+3)
|
||||
}
|
||||
}
|
||||
|
||||
// GetText get the image text by tesseract ocr
|
||||
//
|
||||
// robotgo.GetText(imgPath, lang string)
|
||||
func GetText(imgPath string, args ...string) (string, error) {
|
||||
var lang = "eng"
|
||||
|
||||
if len(args) > 0 {
|
||||
lang = args[0]
|
||||
if lang == "zh" {
|
||||
lang = "chi_sim"
|
||||
}
|
||||
}
|
||||
|
||||
body, err := exec.Command("tesseract", imgPath,
|
||||
"stdout", "-l", lang).Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
553
robotgo.go
553
robotgo.go
@ -27,24 +27,17 @@ package robotgo
|
||||
#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations
|
||||
#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit
|
||||
#cgo darwin LDFLAGS: -framework Carbon -framework CoreFoundation
|
||||
#cgo darwin,amd64 LDFLAGS:-L${SRCDIR}/cdeps/mac/amd -lpng -lz
|
||||
#cgo darwin,arm64 LDFLAGS:-L${SRCDIR}/cdeps/mac/m1 -lpng -lz
|
||||
//#elif defined(USE_X11)
|
||||
// Drop -std=c11
|
||||
#cgo linux CFLAGS: -I/usr/src
|
||||
#cgo linux LDFLAGS: -L/usr/src -lpng -lz -lX11 -lXtst -lm
|
||||
#cgo linux LDFLAGS: -L/usr/src -lX11 -lXtst -lm
|
||||
// #cgo linux LDFLAGS: -lX11-xcb -lxcb -lxcb-xkb -lxkbcommon -lxkbcommon-x11
|
||||
//#endif
|
||||
// #cgo windows LDFLAGS: -lgdi32 -luser32 -lpng -lz
|
||||
#cgo windows LDFLAGS: -lgdi32 -luser32
|
||||
#cgo windows,amd64 LDFLAGS: -L${SRCDIR}/cdeps/win/amd/win64 -lpng -lz
|
||||
#cgo windows,386 LDFLAGS: -L${SRCDIR}/cdeps/win/amd/win32 -lpng -lz
|
||||
#cgo windows,arm64 LDFLAGS:-L${SRCDIR}/cdeps/win/arm -lpng -lz
|
||||
// #include <AppKit/NSEvent.h>
|
||||
#include "screen/goScreen.h"
|
||||
#include "mouse/goMouse.h"
|
||||
#include "key/goKey.h"
|
||||
#include "bitmap/goBitmap.h"
|
||||
//#include "event/goEvent.h"
|
||||
#include "window/goWindow.h"
|
||||
*/
|
||||
@ -64,7 +57,6 @@ import (
|
||||
|
||||
// "syscall"
|
||||
"math/rand"
|
||||
"os/exec"
|
||||
|
||||
"github.com/go-vgo/robotgo/clipboard"
|
||||
"github.com/vcaesar/tt"
|
||||
@ -946,549 +938,6 @@ func SetDelay(d ...int) {
|
||||
SetKeyDelay(v)
|
||||
}
|
||||
|
||||
/*
|
||||
.______ __ .___________..___ ___. ___ .______
|
||||
| _ \ | | | || \/ | / \ | _ \
|
||||
| |_) | | | `---| |----`| \ / | / ^ \ | |_) |
|
||||
| _ < | | | | | |\/| | / /_\ \ | ___/
|
||||
| |_) | | | | | | | | | / _____ \ | |
|
||||
|______/ |__| |__| |__| |__| /__/ \__\ | _|
|
||||
*/
|
||||
|
||||
// GetText get the image text by tesseract ocr
|
||||
//
|
||||
// robotgo.GetText(imgPath, lang string)
|
||||
func GetText(imgPath string, args ...string) (string, error) {
|
||||
var lang = "eng"
|
||||
|
||||
if len(args) > 0 {
|
||||
lang = args[0]
|
||||
if lang == "zh" {
|
||||
lang = "chi_sim"
|
||||
}
|
||||
}
|
||||
|
||||
body, err := exec.Command("tesseract", imgPath,
|
||||
"stdout", "-l", lang).Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
// ToBitmap trans C.MMBitmapRef to Bitmap
|
||||
func ToBitmap(bit C.MMBitmapRef) Bitmap {
|
||||
bitmap := Bitmap{
|
||||
ImgBuf: (*uint8)(bit.imageBuffer),
|
||||
Width: int(bit.width),
|
||||
Height: int(bit.height),
|
||||
Bytewidth: int(bit.bytewidth),
|
||||
BitsPixel: uint8(bit.bitsPerPixel),
|
||||
BytesPerPixel: uint8(bit.bytesPerPixel),
|
||||
}
|
||||
|
||||
return bitmap
|
||||
}
|
||||
|
||||
// ToCBitmap trans Bitmap to C.MMBitmapRef
|
||||
func ToCBitmap(bit Bitmap) C.MMBitmapRef {
|
||||
cbitmap := C.createMMBitmap(
|
||||
(*C.uint8_t)(bit.ImgBuf),
|
||||
C.size_t(bit.Width),
|
||||
C.size_t(bit.Height),
|
||||
C.size_t(bit.Bytewidth),
|
||||
C.uint8_t(bit.BitsPixel),
|
||||
C.uint8_t(bit.BytesPerPixel),
|
||||
)
|
||||
|
||||
return cbitmap
|
||||
}
|
||||
|
||||
// ToBitmapBytes saves Bitmap to bitmap format in bytes
|
||||
func ToBitmapBytes(bit C.MMBitmapRef) []byte {
|
||||
var len C.size_t
|
||||
ptr := C.saveMMBitmapAsBytes(bit, &len)
|
||||
if int(len) < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bs := C.GoBytes(unsafe.Pointer(ptr), C.int(len))
|
||||
C.free(unsafe.Pointer(ptr))
|
||||
return bs
|
||||
}
|
||||
|
||||
// ToMMBitmapRef trans CBitmap to C.MMBitmapRef
|
||||
func ToMMBitmapRef(bit CBitmap) C.MMBitmapRef {
|
||||
return C.MMBitmapRef(bit)
|
||||
}
|
||||
|
||||
// TostringBitmap tostring bitmap to string
|
||||
func TostringBitmap(bit C.MMBitmapRef) string {
|
||||
strBit := C.tostring_bitmap(bit)
|
||||
return C.GoString(strBit)
|
||||
}
|
||||
|
||||
// TocharBitmap tostring bitmap to C.char
|
||||
func TocharBitmap(bit C.MMBitmapRef) *C.char {
|
||||
strBit := C.tostring_bitmap(bit)
|
||||
return strBit
|
||||
}
|
||||
|
||||
// ToImage convert C.MMBitmapRef to standard image.Image
|
||||
func ToImage(bit C.MMBitmapRef) image.Image {
|
||||
return ToRGBA(bit)
|
||||
}
|
||||
|
||||
// ToRGBA convert C.MMBitmapRef to standard image.RGBA
|
||||
func ToRGBA(bit C.MMBitmapRef) *image.RGBA {
|
||||
bmp1 := ToBitmap(bit)
|
||||
return ToRGBAGo(bmp1)
|
||||
}
|
||||
|
||||
func internalFindBitmap(bit, sbit C.MMBitmapRef, tolerance float64) (int, int) {
|
||||
pos := C.find_bitmap(bit, sbit, C.float(tolerance))
|
||||
// fmt.Println("pos----", pos)
|
||||
return int(pos.x), int(pos.y)
|
||||
}
|
||||
|
||||
// FindCBitmap find bitmap's pos by CBitmap
|
||||
func FindCBitmap(bmp CBitmap, args ...interface{}) (int, int) {
|
||||
return FindBitmap(ToMMBitmapRef(bmp), args...)
|
||||
}
|
||||
|
||||
// FindBitmap find the bitmap's pos
|
||||
//
|
||||
// robotgo.FindBitmap(bitmap, source_bitamp C.MMBitmapRef, tolerance float64)
|
||||
//
|
||||
// |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
// colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
//
|
||||
// This method only automatically free the internal bitmap,
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
func FindBitmap(bit C.MMBitmapRef, args ...interface{}) (int, int) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance = 0.01
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = args[1].(float64)
|
||||
}
|
||||
|
||||
fx, fy := internalFindBitmap(bit, sbit, tolerance)
|
||||
// FreeBitmap(bit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
|
||||
return fx, fy
|
||||
}
|
||||
|
||||
// FindPic finding the image by path
|
||||
//
|
||||
// robotgo.FindPic(path string, source_bitamp C.MMBitmapRef, tolerance float64)
|
||||
//
|
||||
// This method only automatically free the internal bitmap,
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
func FindPic(path string, args ...interface{}) (int, int) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance = 0.01
|
||||
)
|
||||
|
||||
openbit := OpenBitmap(path)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = args[1].(float64)
|
||||
}
|
||||
|
||||
fx, fy := internalFindBitmap(openbit, sbit, tolerance)
|
||||
FreeBitmap(openbit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
|
||||
return fx, fy
|
||||
}
|
||||
|
||||
// FreeMMPointArr free MMPoint array
|
||||
func FreeMMPointArr(pointArray C.MMPointArrayRef) {
|
||||
C.destroyMMPointArray(pointArray)
|
||||
}
|
||||
|
||||
// FindEveryBitmap find the every bitmap
|
||||
func FindEveryBitmap(bit C.MMBitmapRef, args ...interface{}) (posArr []Point) {
|
||||
var (
|
||||
sbit C.MMBitmapRef
|
||||
tolerance C.float = 0.01
|
||||
lpos C.MMPoint
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
sbit = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
sbit = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = 0
|
||||
} else {
|
||||
lpos.x = 0
|
||||
lpos.y = 0
|
||||
}
|
||||
|
||||
if len(args) > 3 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = C.size_t(args[3].(int))
|
||||
}
|
||||
|
||||
pos := C.find_every_bitmap(bit, sbit, tolerance, &lpos)
|
||||
// FreeBitmap(bit)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(sbit)
|
||||
}
|
||||
if pos == nil {
|
||||
return
|
||||
}
|
||||
defer FreeMMPointArr(pos)
|
||||
|
||||
cSize := pos.count
|
||||
cArray := pos.array
|
||||
gSlice := (*[(1 << 28) - 1]C.MMPoint)(unsafe.Pointer(cArray))[:cSize:cSize]
|
||||
for i := 0; i < len(gSlice); i++ {
|
||||
posArr = append(posArr, Point{
|
||||
X: int(gSlice[i].x),
|
||||
Y: int(gSlice[i].y),
|
||||
})
|
||||
}
|
||||
|
||||
// fmt.Println("pos----", pos)
|
||||
return
|
||||
}
|
||||
|
||||
// CountBitmap count of the bitmap
|
||||
func CountBitmap(bitmap, sbit C.MMBitmapRef, args ...float32) int {
|
||||
var tolerance C.float = 0.01
|
||||
if len(args) > 0 {
|
||||
tolerance = C.float(args[0])
|
||||
}
|
||||
|
||||
count := C.count_of_bitmap(bitmap, sbit, tolerance)
|
||||
return int(count)
|
||||
}
|
||||
|
||||
// BitmapClick find the bitmap and click
|
||||
func BitmapClick(bitmap C.MMBitmapRef, args ...interface{}) {
|
||||
x, y := FindBitmap(bitmap)
|
||||
MovesClick(x, y, args...)
|
||||
}
|
||||
|
||||
// PointInBounds bitmap point in bounds
|
||||
func PointInBounds(bitmap C.MMBitmapRef, x, y int) bool {
|
||||
var point C.MMPoint
|
||||
point.x = C.size_t(x)
|
||||
point.y = C.size_t(y)
|
||||
cbool := C.point_in_bounds(bitmap, point)
|
||||
|
||||
return bool(cbool)
|
||||
}
|
||||
|
||||
// OpenBitmap open the bitmap return C.MMBitmapRef
|
||||
//
|
||||
// robotgo.OpenBitmap(path string, type int)
|
||||
func OpenBitmap(gpath string, args ...int) C.MMBitmapRef {
|
||||
path := C.CString(gpath)
|
||||
var mtype C.uint16_t = 1
|
||||
|
||||
if len(args) > 0 {
|
||||
mtype = C.uint16_t(args[0])
|
||||
}
|
||||
|
||||
bit := C.bitmap_open(path, mtype)
|
||||
C.free(unsafe.Pointer(path))
|
||||
|
||||
return bit
|
||||
}
|
||||
|
||||
// BitmapStr bitmap from string
|
||||
func BitmapStr(str string) C.MMBitmapRef {
|
||||
return BitmapFromStr(str)
|
||||
}
|
||||
|
||||
// BitmapFromStr bitmap from string
|
||||
func BitmapFromStr(str string) C.MMBitmapRef {
|
||||
cs := C.CString(str)
|
||||
bit := C.bitmap_from_string(cs)
|
||||
C.free(unsafe.Pointer(cs))
|
||||
|
||||
return bit
|
||||
}
|
||||
|
||||
// SaveBitmap save the bitmap to image
|
||||
//
|
||||
// robotgo.SaveBimap(bitmap C.MMBitmapRef, path string, type int)
|
||||
func SaveBitmap(bitmap C.MMBitmapRef, gpath string, args ...int) string {
|
||||
var mtype C.uint16_t = 1
|
||||
if len(args) > 0 {
|
||||
mtype = C.uint16_t(args[0])
|
||||
}
|
||||
|
||||
path := C.CString(gpath)
|
||||
saveBit := C.bitmap_save(bitmap, path, mtype)
|
||||
C.free(unsafe.Pointer(path))
|
||||
|
||||
return C.GoString(saveBit)
|
||||
}
|
||||
|
||||
// GetPortion get bitmap portion
|
||||
func GetPortion(bit C.MMBitmapRef, x, y, w, h int) C.MMBitmapRef {
|
||||
var rect C.MMRect
|
||||
rect.origin.x = C.size_t(x)
|
||||
rect.origin.y = C.size_t(y)
|
||||
rect.size.width = C.size_t(w)
|
||||
rect.size.height = C.size_t(h)
|
||||
|
||||
pos := C.get_portion(bit, rect)
|
||||
return pos
|
||||
}
|
||||
|
||||
// Convert convert the bitmap
|
||||
//
|
||||
// robotgo.Convert(opath, spath string, type int)
|
||||
func Convert(opath, spath string, args ...int) {
|
||||
var mtype = 1
|
||||
if len(args) > 0 {
|
||||
mtype = args[0]
|
||||
}
|
||||
|
||||
// C.CString()
|
||||
bitmap := OpenBitmap(opath)
|
||||
// fmt.Println("a----", bit_map)
|
||||
SaveBitmap(bitmap, spath, mtype)
|
||||
}
|
||||
|
||||
// FreeBitmap free and dealloc the C bitmap
|
||||
func FreeBitmap(bitmap C.MMBitmapRef) {
|
||||
// C.destroyMMBitmap(bitmap)
|
||||
C.bitmap_dealloc(bitmap)
|
||||
}
|
||||
|
||||
// FreeBitmapArr free and dealloc the C bitmap array
|
||||
func FreeBitmapArr(bit ...C.MMBitmapRef) {
|
||||
for i := 0; i < len(bit); i++ {
|
||||
FreeBitmap(bit[i])
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBitmap returns false and sets error if |bitmap| is NULL
|
||||
func ReadBitmap(bitmap C.MMBitmapRef) bool {
|
||||
abool := C.bitmap_ready(bitmap)
|
||||
gbool := bool(abool)
|
||||
return gbool
|
||||
}
|
||||
|
||||
// CopyBitPB copy bitmap to pasteboard
|
||||
func CopyBitPB(bitmap C.MMBitmapRef) bool {
|
||||
abool := C.bitmap_copy_to_pboard(bitmap)
|
||||
gbool := bool(abool)
|
||||
|
||||
return gbool
|
||||
}
|
||||
|
||||
// Deprecated: CopyBitpb copy bitmap to pasteboard, Wno-deprecated
|
||||
func CopyBitpb(bitmap C.MMBitmapRef) bool {
|
||||
tt.Drop("CopyBitpb", "CopyBitPB")
|
||||
return CopyBitPB(bitmap)
|
||||
}
|
||||
|
||||
// DeepCopyBit deep copy bitmap
|
||||
func DeepCopyBit(bitmap C.MMBitmapRef) C.MMBitmapRef {
|
||||
bit := C.bitmap_deepcopy(bitmap)
|
||||
return bit
|
||||
}
|
||||
|
||||
// GetColor get bitmap color
|
||||
func GetColor(bitmap C.MMBitmapRef, x, y int) C.MMRGBHex {
|
||||
color := C.bitmap_get_color(bitmap, C.size_t(x), C.size_t(y))
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
// GetColors get bitmap color retrun string
|
||||
func GetColors(bitmap C.MMBitmapRef, x, y int) string {
|
||||
clo := GetColor(bitmap, x, y)
|
||||
|
||||
return PadHex(clo)
|
||||
}
|
||||
|
||||
// FindColor find bitmap color
|
||||
//
|
||||
// robotgo.FindColor(color CHex, bitmap C.MMBitmapRef, tolerance float)
|
||||
func FindColor(color CHex, args ...interface{}) (int, int) {
|
||||
var (
|
||||
tolerance C.float = 0.01
|
||||
bitmap C.MMBitmapRef
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
pos := C.bitmap_find_color(bitmap, C.MMRGBHex(color), tolerance)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
x := int(pos.x)
|
||||
y := int(pos.y)
|
||||
|
||||
return x, y
|
||||
}
|
||||
|
||||
// FindColorCS findcolor by CaptureScreen
|
||||
func FindColorCS(color CHex, x, y, w, h int, args ...float64) (int, int) {
|
||||
var tolerance = 0.01
|
||||
|
||||
if len(args) > 0 {
|
||||
tolerance = args[0]
|
||||
}
|
||||
|
||||
bitmap := CaptureScreen(x, y, w, h)
|
||||
rx, ry := FindColor(color, bitmap, tolerance)
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return rx, ry
|
||||
}
|
||||
|
||||
// FindEveryColor find every color
|
||||
func FindEveryColor(color CHex, args ...interface{}) (posArr []Point) {
|
||||
var (
|
||||
bitmap C.MMBitmapRef
|
||||
tolerance C.float = 0.01
|
||||
lpos C.MMPoint
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
if len(args) > 2 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = 0
|
||||
} else {
|
||||
lpos.x = 0
|
||||
lpos.y = 0
|
||||
}
|
||||
|
||||
if len(args) > 3 {
|
||||
lpos.x = C.size_t(args[2].(int))
|
||||
lpos.y = C.size_t(args[3].(int))
|
||||
}
|
||||
|
||||
pos := C.bitmap_find_every_color(bitmap, C.MMRGBHex(color), tolerance, &lpos)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
if pos == nil {
|
||||
return
|
||||
}
|
||||
defer FreeMMPointArr(pos)
|
||||
|
||||
cSize := pos.count
|
||||
cArray := pos.array
|
||||
gSlice := (*[(1 << 28) - 1]C.MMPoint)(unsafe.Pointer(cArray))[:cSize:cSize]
|
||||
for i := 0; i < len(gSlice); i++ {
|
||||
posArr = append(posArr, Point{
|
||||
X: int(gSlice[i].x),
|
||||
Y: int(gSlice[i].y),
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CountColor count bitmap color
|
||||
func CountColor(color CHex, args ...interface{}) int {
|
||||
var (
|
||||
tolerance C.float = 0.01
|
||||
bitmap C.MMBitmapRef
|
||||
)
|
||||
|
||||
if len(args) > 0 && args[0] != nil {
|
||||
bitmap = args[0].(C.MMBitmapRef)
|
||||
} else {
|
||||
bitmap = CaptureScreen()
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
tolerance = C.float(args[1].(float64))
|
||||
}
|
||||
|
||||
count := C.bitmap_count_of_color(bitmap, C.MMRGBHex(color), tolerance)
|
||||
if len(args) <= 0 || (len(args) > 0 && args[0] == nil) {
|
||||
FreeBitmap(bitmap)
|
||||
}
|
||||
|
||||
return int(count)
|
||||
}
|
||||
|
||||
// CountColorCS count bitmap color by CaptureScreen
|
||||
func CountColorCS(color CHex, x, y, w, h int, args ...float64) int {
|
||||
var tolerance = 0.01
|
||||
|
||||
if len(args) > 0 {
|
||||
tolerance = args[0]
|
||||
}
|
||||
|
||||
bitmap := CaptureScreen(x, y, w, h)
|
||||
rx := CountColor(color, bitmap, tolerance)
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return rx
|
||||
}
|
||||
|
||||
// GetImgSize get the image size
|
||||
func GetImgSize(imgPath string) (int, int) {
|
||||
bitmap := OpenBitmap(imgPath)
|
||||
gbit := ToBitmap(bitmap)
|
||||
|
||||
w := gbit.Width / 2
|
||||
h := gbit.Height / 2
|
||||
FreeBitmap(bitmap)
|
||||
|
||||
return w, h
|
||||
}
|
||||
|
||||
/*
|
||||
____ __ ____ __ .__ __. _______ ______ ____ __ ____
|
||||
\ \ / \ / / | | | \ | | | \ / __ \ \ \ / \ / /
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "screengrab.h"
|
||||
#include "../base/bmp_io_c.h"
|
||||
// #include "../base/bmp_io_c.h"
|
||||
#include "../base/endian.h"
|
||||
#include <stdlib.h> /* malloc() */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user