tam
This commit is contained in:
105
client/internal/screen/screen_darwin.go
Normal file
105
client/internal/screen/screen_darwin.go
Normal file
@@ -0,0 +1,105 @@
|
||||
//go:build darwin
|
||||
|
||||
package screen
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c -Wno-deprecated-declarations -Wno-unguarded-availability-new
|
||||
#cgo LDFLAGS: -framework CoreGraphics -framework Foundation -framework AppKit
|
||||
|
||||
// Disable availability checks - we handle this at runtime
|
||||
#define __API_UNAVAILABLE(...)
|
||||
#define API_UNAVAILABLE(...)
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
// Use dlsym to call CGWindowListCreateImage dynamically to bypass macOS 15 availability check
|
||||
typedef CGImageRef (*CGWindowListCreateImageFunc)(CGRect, CGWindowListOption, CGWindowID, CGWindowImageOption);
|
||||
|
||||
static unsigned char* CaptureFullDesktop(int* outLen, int quality) {
|
||||
// Dynamically load CGWindowListCreateImage to bypass compile-time availability check
|
||||
static CGWindowListCreateImageFunc createImageFunc = NULL;
|
||||
if (!createImageFunc) {
|
||||
void *handle = dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", RTLD_LAZY);
|
||||
if (handle) {
|
||||
createImageFunc = (CGWindowListCreateImageFunc)dlsym(handle, "CGWindowListCreateImage");
|
||||
}
|
||||
}
|
||||
|
||||
if (!createImageFunc) {
|
||||
*outLen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CGImageRef image = createImageFunc(
|
||||
CGRectInfinite,
|
||||
kCGWindowListOptionOnScreenOnly,
|
||||
kCGNullWindowID,
|
||||
kCGWindowImageDefault
|
||||
);
|
||||
|
||||
if (!image) {
|
||||
*outLen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@autoreleasepool {
|
||||
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:image];
|
||||
CGImageRelease(image);
|
||||
|
||||
if (!rep) {
|
||||
*outLen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float q = (float)quality / 100.0f;
|
||||
if (q < 0.1f) q = 0.1f;
|
||||
if (q > 1.0f) q = 1.0f;
|
||||
|
||||
NSDictionary *props = @{NSImageCompressionFactor: @(q)};
|
||||
NSData *jpegData = [rep representationUsingType:NSBitmapImageFileTypeJPEG properties:props];
|
||||
|
||||
if (!jpegData || jpegData.length == 0) {
|
||||
*outLen = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*outLen = (int)jpegData.length;
|
||||
unsigned char *buf = (unsigned char*)malloc(jpegData.length);
|
||||
memcpy(buf, jpegData.bytes, jpegData.length);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// CaptureScreen captures the full desktop on macOS using CGWindowListCreateImage (via dlsym)
|
||||
// Returns a data:image/jpeg;base64,... string
|
||||
func CaptureScreen() (string, error) {
|
||||
var outLen C.int
|
||||
quality := C.int(50) // JPEG quality 50%
|
||||
|
||||
buf := C.CaptureFullDesktop(&outLen, quality)
|
||||
if buf == nil || int(outLen) == 0 {
|
||||
return "", fmt.Errorf("failed to capture screen: CGWindowListCreateImage returned nil")
|
||||
}
|
||||
defer C.free(unsafe.Pointer(buf))
|
||||
|
||||
jpegBytes := C.GoBytes(unsafe.Pointer(buf), outLen)
|
||||
|
||||
if len(jpegBytes) < 100 {
|
||||
log.Printf("[SCREEN] Warning: captured image is very small (%d bytes), may indicate permission issue", len(jpegBytes))
|
||||
}
|
||||
|
||||
encoded := base64.StdEncoding.EncodeToString(jpegBytes)
|
||||
return "data:image/jpeg;base64," + encoded, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !darwin
|
||||
|
||||
package screen
|
||||
|
||||
import (
|
||||
@@ -10,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// CaptureScreen chụp màn hình chính và trả về chuỗi Base64 dạng "data:image/jpeg;base64,..."
|
||||
// Non-darwin: dùng kbinani/screenshot
|
||||
func CaptureScreen() (string, error) {
|
||||
n := screenshot.NumActiveDisplays()
|
||||
if n <= 0 {
|
||||
Reference in New Issue
Block a user