Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
e9787a81b4 There are dylib issues and I don't want to invest more time in this right now 2022-04-22 18:09:32 -07:00
3 changed files with 670 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
import { it } from "bun:test";
it("read", async () => {
// This doesn't run on Linux but it shouldn't throw at least
const text = await Bun.Clipboard.readText();
expect(text).toBe("hello");
});

499
src/deps/objc.zig Normal file
View File

@@ -0,0 +1,499 @@
// Objective-C runtime headers without the extern
// intended for dlopen
const std = @import("std");
pub const ObjC = struct {
// copypasta because the autocomplete for it is broken
pub const DlDynlib = struct {
const os = std.os;
const system = std.os.system;
pub const Error = error{FileNotFound};
handle: *anyopaque,
pub fn open(path: []const u8) !DlDynlib {
const path_c = try os.toPosixPath(path);
return openZ(&path_c);
}
pub fn openZ(path_c: [*:0]const u8) !DlDynlib {
return DlDynlib{
.handle = system.dlopen(path_c, system.RTLD.LAZY) orelse {
return error.FileNotFound;
},
};
}
pub fn close(self: *DlDynlib) void {
_ = system.dlclose(self.handle);
self.* = undefined;
}
pub fn lookup(self: *DlDynlib, comptime T: type, name: [:0]const u8) ?T {
// dlsym (and other dl-functions) secretly take shadow parameter - return address on stack
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66826
if (@call(.{ .modifier = .never_tail }, system.dlsym, .{ self.handle, name.ptr })) |symbol| {
@setRuntimeSafety(false);
return @ptrCast(T, @alignCast(@alignOf(T), symbol));
} else {
return null;
}
}
};
pub const C = struct {
objc_lookUpClass: Types.objc_lookUpClass,
sel_getUid: Types.sel_getUid,
objc_msgSend: fn (...) callconv(.C) Types.id,
objc_dylib: DlDynlib,
appkit_dylib: DlDynlib,
CFStringCreateWithBytesNoCopy: Types.CFStringCreateWithBytesNoCopy,
NSPasteboardTypeString: Types.CFStringRef,
NSPasteboardTypePNG: Types.CFStringRef,
// NSPasteboardTypeURL: *Types.id,
CFArrayCreate: Types.CFArrayCreate,
kCFAllocatorNull: Types.kCFAllocatorNull,
CFArrayContainsValue: Types.CFArrayContainsValue,
CFRetain: Types.CFRetain,
CFRelease: Types.CFRelease,
CFDataGetLength: Types.CFDataGetLength,
CFDataGetBytePtr: Types.CFDataGetBytePtr,
CFDataCreateWithBytesNoCopy: Types.CFDataCreateWithBytesNoCopy,
CFArrayGetCount: Types.CFArrayGetCount,
CFArrayGetValueAtIndex: Types.CFArrayGetValueAtIndex,
const id = Types.id;
pub fn class(c: *C, s: [*c]const u8) Types.id {
return @ptrCast(C.id, @alignCast(@alignOf(C.id), c.objc_lookUpClass(s)));
}
pub fn call(c: *C, obj: Types.id, sel_name: [*c]const u8) Types.id {
var f = @ptrCast(
fn (C.id, Types.SEL) callconv(.C) C.id,
c.objc_msgSend,
);
return f(obj, c.sel_getUid(sel_name));
}
pub fn call_(c: *C, obj: Types.id, sel_name: [*c]const u8, arg: anytype) C.id {
// objc_msgSend has the prototype "void objc_msgSend(void)",
// so we have to cast it based on the types of our arguments
// (https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.html)
var f = @ptrCast(
fn (Types.id, Types.SEL, @TypeOf(arg)) callconv(.C) Types.id,
c.objc_msgSend,
);
return f(obj, c.sel_getUid(sel_name), arg);
}
pub fn call2(c: *C, obj: Types.id, sel_name: [*c]const u8, arg: anytype, arg2: anytype) C.id {
// objc_msgSend has the prototype "void objc_msgSend(void)",
// so we have to cast it based on the types of our arguments
// (https://www.mikeash.com/pyblog/objc_msgsends-new-prototype.html)
var f = @ptrCast(
fn (Types.id, Types.SEL, @TypeOf(arg), @TypeOf(arg2)) callconv(.C) C.id,
c.objc_msgSend,
);
return f(obj, c.sel_getUid(sel_name), arg, arg2);
}
};
pub const Clipboard = struct {
pub const Data = enum {
png,
string,
// url,
};
pub fn get(allocator: std.mem.Allocator, tag: Data) ![]u8 {
if (!objc_loaded) {
try load();
}
const pasteboard_class = objc.class("NSPasteboard");
const pb = objc.call(pasteboard_class, "generalPasteboard");
var kind = switch (tag) {
.png => objc.NSPasteboardTypePNG,
.string => objc.NSPasteboardTypeString,
// .url => objc.NSPasteboardTypeURL,
};
var array = [_]?*const anyopaque{kind};
var supported_types = objc.CFArrayCreate(null, &array, 1, null);
defer objc.CFRelease(supported_types);
const item = objc.call_(pb, "availableTypeFromArray:", supported_types.toNSArray());
const data = objc.call_(
item,
"dataForType:",
@ptrCast(Types.id, @alignCast(@alignOf(Types.id), kind)),
);
const size: usize = @ptrToInt(objc.call(data, "length"));
if (size == 0)
return &[_]u8{};
var bytes = try allocator.alloc(u8, size);
_ = objc.call2(data, "getBytes:length:", bytes.ptr, size);
return bytes;
}
pub fn set(tag: Data, blob: []const u8) !void {
if (!objc_loaded) {
try load();
}
const pasteboard_class = objc.class("NSPasteboard");
const pb = objc.call(pasteboard_class, "generalPasteboard");
const NSData = objc.class("NSData");
var data = objc.call2(NSData, "dataWithBytes:length", blob.ptr, blob.len);
var kind = switch (tag) {
.png => objc.NSPasteboardTypePNG,
.string => objc.NSPasteboardTypeString,
// .url => objc.NSPasteboardTypeURL,
};
_ = objc.call2(pb, "declareTypes:owner", kind, @as(Types.id, null));
_ = objc.call2(
pb,
"setData:forType",
data,
@ptrCast(Types.id, @alignCast(@alignOf(Types.id), kind)),
);
}
};
pub var objc: C = undefined;
pub var objc_loaded: bool = false;
pub fn load() !void {
var dylib = try DlDynlib.openZ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
var appkit = try DlDynlib.openZ("/System/Library/Frameworks/AppKit.framework/AppKit");
var CFStringCreateWithBytesNoCopy = dylib.lookup(Types.CFStringCreateWithBytesNoCopy, "CFStringCreateWithBytesNoCopy").?;
const kCFAllocatorNull = dylib.lookup(Types.kCFAllocatorNull, "kCFAllocatorNull").?;
var NSPasteboardTypeString = CFStringCreateWithBytesNoCopy(
null,
"public.utf8-plain-text",
"public.utf8-plain-text".len,
Types.CFStringEncoding.ASCII,
1,
null,
);
var NSPasteboardTypePNG = CFStringCreateWithBytesNoCopy(
null,
"public.png",
"public.png".len,
Types.CFStringEncoding.ASCII,
1,
null,
);
objc = C{
.kCFAllocatorNull = kCFAllocatorNull,
.objc_lookUpClass = dylib.lookup(Types.objc_lookUpClass, "objc_lookUpClass").?,
.sel_getUid = dylib.lookup(Types.sel_getUid, "sel_getUid").?,
.objc_msgSend = dylib.lookup(fn (...) callconv(.C) C.id, "objc_msgSend").?,
.appkit_dylib = appkit,
.objc_dylib = dylib,
.CFStringCreateWithBytesNoCopy = CFStringCreateWithBytesNoCopy,
.NSPasteboardTypeString = NSPasteboardTypeString,
.NSPasteboardTypePNG = NSPasteboardTypePNG,
.CFArrayCreate = dylib.lookup(@TypeOf(objc.CFArrayCreate), "CFArrayCreate").?,
.CFArrayContainsValue = dylib.lookup(@TypeOf(objc.CFArrayContainsValue), "CFArrayContainsValue").?,
.CFRetain = dylib.lookup(@TypeOf(objc.CFRetain), "CFRetain").?,
.CFRelease = dylib.lookup(@TypeOf(objc.CFRelease), "CFRelease").?,
.CFDataGetLength = dylib.lookup(@TypeOf(objc.CFDataGetLength), "CFDataGetLength").?,
.CFDataGetBytePtr = dylib.lookup(@TypeOf(objc.CFDataGetBytePtr), "CFDataGetBytePtr").?,
.CFDataCreateWithBytesNoCopy = dylib.lookup(@TypeOf(objc.CFDataCreateWithBytesNoCopy), "CFDataCreateWithBytesNoCopy").?,
.CFArrayGetCount = dylib.lookup(@TypeOf(objc.CFArrayGetCount), "CFArrayGetCount").?,
.CFArrayGetValueAtIndex = dylib.lookup(@TypeOf(objc.CFArrayGetValueAtIndex), "CFArrayGetValueAtIndex").?,
};
objc_loaded = true;
}
pub const Types = struct {
pub const CFRange = extern struct {
location: CFIndex,
length: CFIndex,
};
const UInt8 = u8;
pub const CFData = opaque {
pub fn init(objc_id: id) *CFData {
return @ptrCast(*CFData, objc_id.isa.?);
}
};
const UInt32 = u32;
pub const CFStringEncoding = enum(UInt32) {
MacRoman = 0,
WindowsLatin1 = 1280,
ISOLatin1 = 513,
NextStepLatin = 2817,
ASCII = 1536,
UTF8 = 134217984,
NonLossyASCII = 3071,
UTF16 = 256,
UTF16BE = 268435712,
UTF16LE = 335544576,
UTF32 = 201326848,
UTF32BE = 402653440,
UTF32LE = 469762304,
_,
};
pub const CFStringBuiltInEncodings = CFStringEncoding;
pub const CFStringCreateWithBytesNoCopy = fn (alloc: CFAllocatorRef, bytes: [*]const UInt8, numBytes: CFIndex, encoding: CFStringEncoding, isExternalRepresentation: Boolean, contentsDeallocator: CFAllocatorRef) callconv(.C) CFStringRef;
pub const CFDataRef = ?*const CFData;
pub const CFArrayCreate = fn (allocator: CFAllocatorRef, values: [*]?*const anyopaque, numValues: CFIndex, callBacks: ?*const CFArrayCallBacks) callconv(.C) CFArrayRef;
pub const CFArrayContainsValue = fn (theArray: CFArrayRef, range: CFRange, value: ?*const anyopaque) callconv(.C) Boolean;
pub const CFRetain = fn (cf: CFTypeRef) callconv(.C) CFTypeRef;
pub const CFRelease = fn (cf: CFTypeRef) callconv(.C) void;
pub const CFDataGetLength = fn (theData: CFDataRef) callconv(.C) CFIndex;
pub const CFDataGetBytePtr = fn (theData: CFDataRef) callconv(.C) [*c]const UInt8;
pub const CFDataCreateWithBytesNoCopy = fn (allocator: CFAllocatorRef, bytes: [*c]const UInt8, length: CFIndex, bytesDeallocator: CFAllocatorRef) callconv(.C) CFDataRef;
pub const Boolean = u8;
pub const CFTypeRef = ?*anyopaque;
pub const struct___CFAllocator = opaque {};
pub const CFAllocatorRef = ?*const struct___CFAllocator;
pub const CFString = opaque {};
pub const CFStringRef = *CFString;
pub const kCFAllocatorNull = CFAllocatorRef;
pub const CFArrayRetainCallBack = ?fn (CFAllocatorRef, ?*const anyopaque) callconv(.C) ?*const anyopaque;
pub const CFArrayReleaseCallBack = ?fn (CFAllocatorRef, ?*const anyopaque) callconv(.C) void;
pub const CFArrayCopyDescriptionCallBack = ?fn (?*const anyopaque) callconv(.C) CFStringRef;
pub const CFArrayEqualCallBack = ?fn (?*const anyopaque, ?*const anyopaque) callconv(.C) Boolean;
pub const CFArrayCallBacks = extern struct {
version: CFIndex,
retain: CFArrayRetainCallBack,
release: CFArrayReleaseCallBack,
copyDescription: CFArrayCopyDescriptionCallBack,
equal: CFArrayEqualCallBack,
};
pub extern const kCFTypeArrayCallBacks: CFArrayCallBacks;
pub const CFArrayApplierFunction = ?fn (?*const anyopaque, ?*anyopaque) callconv(.C) void;
pub const CFArray = opaque {
pub fn toNSArray(this: *CFArray) id {
return @ptrCast(id, @alignCast(@alignOf(id), this));
}
};
pub const CFArrayRef = *CFArray;
pub const CFMutableArrayRef = ?*CFArray;
pub const CFArrayGetCount = fn (theArray: CFArrayRef) callconv(.C) CFIndex;
pub const CFArrayGetValueAtIndex = fn (theArray: CFArrayRef, idx: CFIndex) callconv(.C) ?*const anyopaque;
pub const CFIndex = c_long;
pub const struct_objc_class = opaque {};
pub const Class = ?*struct_objc_class;
pub const struct_objc_object = extern struct {
isa: Class,
};
pub const id = ?*struct_objc_object;
pub const struct_objc_selector = opaque {};
pub const SEL = ?*struct_objc_selector;
pub const IMP = ?fn () callconv(.C) void;
pub const BOOL = bool;
pub const objc_objectptr_t = ?*const anyopaque;
pub const arith_t = c_long;
pub const uarith_t = c_ulong;
pub const STR = [*c]u8;
pub const ptrdiff_t = c_long;
pub const wchar_t = c_int;
pub const max_align_t = c_longdouble;
pub const struct_objc_method = opaque {};
pub const Method = ?*struct_objc_method;
pub const struct_objc_ivar = opaque {};
pub const Ivar = ?*struct_objc_ivar;
pub const struct_objc_category = opaque {};
pub const Category = ?*struct_objc_category;
pub const struct_objc_property = opaque {};
pub const objc_property_t = ?*struct_objc_property;
pub const Protocol = struct_objc_object;
pub const struct_objc_method_description = extern struct {
name: SEL,
types: [*c]u8,
};
pub const objc_property_attribute_t = extern struct {
name: [*c]const u8,
value: [*c]const u8,
};
pub const sel_getName = fn (sel: SEL) callconv(.C) [*c]const u8;
pub const sel_registerName = fn (str: [*c]const u8) callconv(.C) SEL;
pub const object_getClassName = fn (obj: id) callconv(.C) [*c]const u8;
pub const object_getIndexedIvars = fn (obj: id) callconv(.C) ?*anyopaque;
pub const sel_isMapped = fn (sel: SEL) callconv(.C) BOOL;
pub const sel_getUid = fn (str: [*c]const u8) callconv(.C) SEL;
pub const objc_retainedObject = fn (obj: objc_objectptr_t) callconv(.C) id;
pub const objc_unretainedObject = fn (obj: objc_objectptr_t) callconv(.C) id;
pub const objc_unretainedPointer = fn (obj: id) callconv(.C) objc_objectptr_t;
pub const object_copy = fn (obj: id, size: usize) callconv(.C) id;
pub const object_dispose = fn (obj: id) callconv(.C) id;
pub const object_getClass = fn (obj: id) callconv(.C) Class;
pub const object_setClass = fn (obj: id, cls: Class) callconv(.C) Class;
pub const object_isClass = fn (obj: id) callconv(.C) BOOL;
pub const object_getIvar = fn (obj: id, ivar: Ivar) callconv(.C) id;
pub const object_setIvar = fn (obj: id, ivar: Ivar, value: id) callconv(.C) void;
pub const object_setIvarWithStrongDefault = fn (obj: id, ivar: Ivar, value: id) callconv(.C) void;
pub const object_setInstanceVariable = fn (obj: id, name: [*c]const u8, value: ?*anyopaque) callconv(.C) Ivar;
pub const object_setInstanceVariableWithStrongDefault = fn (obj: id, name: [*c]const u8, value: ?*anyopaque) callconv(.C) Ivar;
pub const object_getInstanceVariable = fn (obj: id, name: [*c]const u8, outValue: [*c]?*anyopaque) callconv(.C) Ivar;
pub const objc_getClass = fn (name: [*c]const u8) callconv(.C) Class;
pub const objc_getMetaClass = fn (name: [*c]const u8) callconv(.C) Class;
pub const objc_lookUpClass = fn (name: [*c]const u8) callconv(.C) Class;
pub const objc_getRequiredClass = fn (name: [*c]const u8) callconv(.C) Class;
pub const objc_getClassList = fn (buffer: [*c]Class, bufferCount: c_int) callconv(.C) c_int;
pub const objc_copyClassList = fn (outCount: [*c]c_uint) callconv(.C) [*c]Class;
pub const class_getName = fn (cls: Class) callconv(.C) [*c]const u8;
pub const class_isMetaClass = fn (cls: Class) callconv(.C) BOOL;
pub const class_getSuperclass = fn (cls: Class) callconv(.C) Class;
pub const class_setSuperclass = fn (cls: Class, newSuper: Class) callconv(.C) Class;
pub const class_getVersion = fn (cls: Class) callconv(.C) c_int;
pub const class_setVersion = fn (cls: Class, version: c_int) callconv(.C) void;
pub const class_getInstanceSize = fn (cls: Class) callconv(.C) usize;
pub const class_getInstanceVariable = fn (cls: Class, name: [*c]const u8) callconv(.C) Ivar;
pub const class_getClassVariable = fn (cls: Class, name: [*c]const u8) callconv(.C) Ivar;
pub const class_copyIvarList = fn (cls: Class, outCount: [*c]c_uint) callconv(.C) [*c]Ivar;
pub const class_getInstanceMethod = fn (cls: Class, name: SEL) callconv(.C) Method;
pub const class_getClassMethod = fn (cls: Class, name: SEL) callconv(.C) Method;
pub const class_getMethodImplementation = fn (cls: Class, name: SEL) callconv(.C) IMP;
pub const class_getMethodImplementation_stret = fn (cls: Class, name: SEL) callconv(.C) IMP;
pub const class_respondsToSelector = fn (cls: Class, sel: SEL) callconv(.C) BOOL;
pub const class_copyMethodList = fn (cls: Class, outCount: [*c]c_uint) callconv(.C) [*c]Method;
pub const class_conformsToProtocol = fn (cls: Class, protocol: [*c]Protocol) callconv(.C) BOOL;
pub const class_copyProtocolList = fn (cls: Class, outCount: [*c]c_uint) callconv(.C) [*c][*c]Protocol;
pub const class_getProperty = fn (cls: Class, name: [*c]const u8) callconv(.C) objc_property_t;
pub const class_copyPropertyList = fn (cls: Class, outCount: [*c]c_uint) callconv(.C) [*c]objc_property_t;
pub const class_getIvarLayout = fn (cls: Class) callconv(.C) [*c]const u8;
pub const class_getWeakIvarLayout = fn (cls: Class) callconv(.C) [*c]const u8;
pub const class_addMethod = fn (cls: Class, name: SEL, imp: IMP, types: [*c]const u8) callconv(.C) BOOL;
pub const class_replaceMethod = fn (cls: Class, name: SEL, imp: IMP, types: [*c]const u8) callconv(.C) IMP;
pub const class_addIvar = fn (cls: Class, name: [*c]const u8, size: usize, alignment: u8, types: [*c]const u8) callconv(.C) BOOL;
pub const class_addProtocol = fn (cls: Class, protocol: [*c]Protocol) callconv(.C) BOOL;
pub const class_addProperty = fn (cls: Class, name: [*c]const u8, attributes: [*c]const objc_property_attribute_t, attributeCount: c_uint) callconv(.C) BOOL;
pub const class_replaceProperty = fn (cls: Class, name: [*c]const u8, attributes: [*c]const objc_property_attribute_t, attributeCount: c_uint) callconv(.C) void;
pub const class_setIvarLayout = fn (cls: Class, layout: [*c]const u8) callconv(.C) void;
pub const class_setWeakIvarLayout = fn (cls: Class, layout: [*c]const u8) callconv(.C) void;
pub const objc_getFutureClass = fn (name: [*c]const u8) callconv(.C) Class;
pub const class_createInstance = fn (cls: Class, extraBytes: usize) callconv(.C) id;
pub const objc_constructInstance = fn (cls: Class, bytes: ?*anyopaque) callconv(.C) id;
pub const objc_destructInstance = fn (obj: id) callconv(.C) ?*anyopaque;
pub const objc_allocateClassPair = fn (superclass: Class, name: [*c]const u8, extraBytes: usize) callconv(.C) Class;
pub const objc_registerClassPair = fn (cls: Class) callconv(.C) void;
pub const objc_duplicateClass = fn (original: Class, name: [*c]const u8, extraBytes: usize) callconv(.C) Class;
pub const objc_disposeClassPair = fn (cls: Class) callconv(.C) void;
pub const method_getName = fn (m: Method) callconv(.C) SEL;
pub const method_getImplementation = fn (m: Method) callconv(.C) IMP;
pub const method_getTypeEncoding = fn (m: Method) callconv(.C) [*c]const u8;
pub const method_getNumberOfArguments = fn (m: Method) callconv(.C) c_uint;
pub const method_copyReturnType = fn (m: Method) callconv(.C) [*c]u8;
pub const method_copyArgumentType = fn (m: Method, index: c_uint) callconv(.C) [*c]u8;
pub const method_getReturnType = fn (m: Method, dst: [*c]u8, dst_len: usize) callconv(.C) void;
pub const method_getArgumentType = fn (m: Method, index: c_uint, dst: [*c]u8, dst_len: usize) callconv(.C) void;
pub const method_getDescription = fn (m: Method) callconv(.C) [*c]struct_objc_method_description;
pub const method_setImplementation = fn (m: Method, imp: IMP) callconv(.C) IMP;
pub const method_exchangeImplementations = fn (m1: Method, m2: Method) callconv(.C) void;
pub const ivar_getName = fn (v: Ivar) callconv(.C) [*c]const u8;
pub const ivar_getTypeEncoding = fn (v: Ivar) callconv(.C) [*c]const u8;
pub const ivar_getOffset = fn (v: Ivar) callconv(.C) ptrdiff_t;
pub const property_getName = fn (property: objc_property_t) callconv(.C) [*c]const u8;
pub const property_getAttributes = fn (property: objc_property_t) callconv(.C) [*c]const u8;
pub const property_copyAttributeList = fn (property: objc_property_t, outCount: [*c]c_uint) callconv(.C) [*c]objc_property_attribute_t;
pub const property_copyAttributeValue = fn (property: objc_property_t, attributeName: [*c]const u8) callconv(.C) [*c]u8;
pub const objc_getProtocol = fn (name: [*c]const u8) callconv(.C) [*c]Protocol;
pub const objc_copyProtocolList = fn (outCount: [*c]c_uint) callconv(.C) [*c][*c]Protocol;
pub const protocol_conformsToProtocol = fn (proto: [*c]Protocol, other: [*c]Protocol) callconv(.C) BOOL;
pub const protocol_isEqual = fn (proto: [*c]Protocol, other: [*c]Protocol) callconv(.C) BOOL;
pub const protocol_getName = fn (proto: [*c]Protocol) callconv(.C) [*c]const u8;
pub const protocol_getMethodDescription = fn (proto: [*c]Protocol, aSel: SEL, isRequiredMethod: BOOL, isInstanceMethod: BOOL) callconv(.C) struct_objc_method_description;
pub const protocol_copyMethodDescriptionList = fn (proto: [*c]Protocol, isRequiredMethod: BOOL, isInstanceMethod: BOOL, outCount: [*c]c_uint) callconv(.C) [*c]struct_objc_method_description;
pub const protocol_getProperty = fn (proto: [*c]Protocol, name: [*c]const u8, isRequiredProperty: BOOL, isInstanceProperty: BOOL) callconv(.C) objc_property_t;
pub const protocol_copyPropertyList = fn (proto: [*c]Protocol, outCount: [*c]c_uint) callconv(.C) [*c]objc_property_t;
pub const protocol_copyPropertyList2 = fn (proto: [*c]Protocol, outCount: [*c]c_uint, isRequiredProperty: BOOL, isInstanceProperty: BOOL) callconv(.C) [*c]objc_property_t;
pub const protocol_copyProtocolList = fn (proto: [*c]Protocol, outCount: [*c]c_uint) callconv(.C) [*c][*c]Protocol;
pub const objc_allocateProtocol = fn (name: [*c]const u8) callconv(.C) [*c]Protocol;
pub const objc_registerProtocol = fn (proto: [*c]Protocol) callconv(.C) void;
pub const protocol_addMethodDescription = fn (proto: [*c]Protocol, name: SEL, types: [*c]const u8, isRequiredMethod: BOOL, isInstanceMethod: BOOL) callconv(.C) void;
pub const protocol_addProtocol = fn (proto: [*c]Protocol, addition: [*c]Protocol) callconv(.C) void;
pub const protocol_addProperty = fn (proto: [*c]Protocol, name: [*c]const u8, attributes: [*c]const objc_property_attribute_t, attributeCount: c_uint, isRequiredProperty: BOOL, isInstanceProperty: BOOL) callconv(.C) void;
pub const objc_copyImageNames = fn (outCount: [*c]c_uint) callconv(.C) [*c][*c]const u8;
pub const class_getImageName = fn (cls: Class) callconv(.C) [*c]const u8;
pub const objc_copyClassNamesForImage = fn (image: [*c]const u8, outCount: [*c]c_uint) callconv(.C) [*c][*c]const u8;
pub const sel_isEqual = fn (lhs: SEL, rhs: SEL) callconv(.C) BOOL;
pub const objc_enumerationMutation = fn (obj: id) callconv(.C) void;
pub const objc_setEnumerationMutationHandler = fn (handler: ?fn (id) callconv(.C) void) callconv(.C) void;
pub const objc_setForwardHandler = fn (fwd: ?*anyopaque, fwd_stret: ?*anyopaque) callconv(.C) void;
pub const imp_implementationWithBlock = fn (block: id) callconv(.C) IMP;
pub const imp_getBlock = fn (anImp: IMP) callconv(.C) id;
pub const imp_removeBlock = fn (anImp: IMP) callconv(.C) BOOL;
pub const objc_loadWeak = fn (location: [*c]id) callconv(.C) id;
pub const objc_storeWeak = fn (location: [*c]id, obj: id) callconv(.C) id;
pub const objc_AssociationPolicy = usize;
pub const OBJC_ASSOCIATION_ASSIGN: c_int = 0;
pub const OBJC_ASSOCIATION_RETAIN_NONATOMIC: c_int = 1;
pub const OBJC_ASSOCIATION_COPY_NONATOMIC: c_int = 3;
pub const OBJC_ASSOCIATION_RETAIN: c_int = 769;
pub const OBJC_ASSOCIATION_COPY: c_int = 771;
const enum_unnamed_1 = c_uint;
pub const objc_setAssociatedObject = fn (object: id, key: ?*const anyopaque, value: id, policy: objc_AssociationPolicy) callconv(.C) void;
pub const objc_getAssociatedObject = fn (object: id, key: ?*const anyopaque) callconv(.C) id;
pub const objc_removeAssociatedObjects = fn (object: id) callconv(.C) void;
pub const objc_hook_getImageName = ?fn (Class, [*c][*c]const u8) callconv(.C) BOOL;
pub const objc_setHook_getImageName = fn (newValue: objc_hook_getImageName, outOldValue: [*c]objc_hook_getImageName) callconv(.C) void;
pub const objc_hook_getClass = ?fn ([*c]const u8, [*c]Class) callconv(.C) BOOL;
pub const objc_setHook_getClass = fn (newValue: objc_hook_getClass, outOldValue: [*c]objc_hook_getClass) callconv(.C) void;
pub const struct_mach_header = opaque {};
pub const objc_func_loadImage = ?fn (?*const struct_mach_header) callconv(.C) void;
pub const objc_addLoadImageFunc = fn (func: objc_func_loadImage) callconv(.C) void;
pub const objc_hook_lazyClassNamer = ?fn (Class) callconv(.C) [*c]const u8;
pub const objc_setHook_lazyClassNamer = fn (newValue: objc_hook_lazyClassNamer, oldOutValue: [*c]objc_hook_lazyClassNamer) callconv(.C) void;
pub const _objc_swiftMetadataInitializer = ?fn (Class, ?*anyopaque) callconv(.C) Class;
pub const _objc_realizeClassFromSwift = fn (cls: Class, previously: ?*anyopaque) callconv(.C) Class;
pub const struct_objc_method_list = opaque {};
pub const class_lookupMethod = fn (cls: Class, sel: SEL) callconv(.C) IMP;
pub const class_respondsToMethod = fn (cls: Class, sel: SEL) callconv(.C) BOOL;
pub const _objc_flush_caches = fn (cls: Class) callconv(.C) void;
pub const object_copyFromZone = fn (anObject: id, nBytes: usize, z: ?*anyopaque) callconv(.C) id;
pub const class_createInstanceFromZone = fn (Class, idxIvars: usize, z: ?*anyopaque) callconv(.C) id;
pub inline fn __P(protos: anytype) @TypeOf(protos) {
return protos;
}
pub const @"bool" = bool;
pub const @"true" = @as(c_int, 1);
pub const @"false" = @as(c_int, 0);
pub const __bool_true_false_are_defined = @as(c_int, 1);
pub const OBJC_BOOL_IS_BOOL = @as(c_int, 1);
pub const OBJC_BOOL_DEFINED = "";
pub const Nil = @as(usize, 0);
pub const nil = @as(usize, 0);
pub const __autoreleasing = "";
pub const ARITH_SHIFT = @as(c_int, 32);
pub inline fn ISSELECTOR(sel: anytype) @TypeOf(sel_isMapped(sel)) {
return sel_isMapped(sel);
}
pub inline fn SELNAME(sel: anytype) @TypeOf(sel_getName(sel)) {
return sel_getName(sel);
}
pub inline fn SELUID(str: anytype) @TypeOf(sel_getUid(str)) {
return sel_getUid(str);
}
pub inline fn NAMEOF(obj: anytype) @TypeOf(object_getClassName(obj)) {
return object_getClassName(obj);
}
pub inline fn IV(obj: anytype) @TypeOf(object_getIndexedIvars(obj)) {
return object_getIndexedIvars(obj);
}
pub const NULL = @import("std").zig.c_translation.cast(?*anyopaque, @as(c_int, 0));
pub const objc_class = struct_objc_class;
pub const objc_object = struct_objc_object;
pub const objc_selector = struct_objc_selector;
pub const objc_method = struct_objc_method;
pub const objc_ivar = struct_objc_ivar;
pub const objc_category = struct_objc_category;
pub const objc_property = struct_objc_property;
pub const objc_method_description = struct_objc_method_description;
pub const mach_header = struct_mach_header;
pub const objc_method_list = struct_objc_method_list;
};
};

View File

@@ -1202,9 +1202,173 @@ pub const Class = NewClass(
.SHA512_256 = .{
.get = Crypto.SHA512_256.getter,
},
.Clipboard = .{
.get = Clipboard.getter,
},
},
);
pub const Clipboard = struct {
const macOS = @import("../../../deps/objc.zig").ObjC;
pub const Class = NewClass(
void,
.{ .name = "Clipboard" },
.{
.readText = .{
.rfn = JSC.wrapWithHasContainer(Clipboard, "readText", false, false),
},
.readPNG = .{
.rfn = JSC.wrapWithHasContainer(Clipboard, "readPNG", false, false),
},
.writeText = .{
.rfn = JSC.wrapWithHasContainer(Clipboard, "writeText", false, false),
},
.writePNG = .{
.rfn = JSC.wrapWithHasContainer(Clipboard, "writePNG", false, false),
},
},
.{},
);
pub fn getter(
_: void,
ctx: js.JSContextRef,
_: js.JSValueRef,
_: js.JSStringRef,
_: js.ExceptionRef,
) js.JSValueRef {
var existing = ctx.ptr().getCachedObject(&ZigString.init("BunClipboard"));
if (existing.isEmpty()) {
return ctx.ptr().putCachedObject(
&ZigString.init("BunClipboard"),
JSC.JSValue.c(JSC.C.JSObjectMake(ctx, Clipboard.Class.get().*, null)),
).asObjectRef();
}
return existing.asObjectRef();
}
pub fn readText(
global: *JSC.JSGlobalObject,
exception: JSC.C.ExceptionRef,
) JSC.JSValue {
if (comptime !Environment.isMac) {
return JSC.JSValue.jsUndefined();
}
var data = macOS.Clipboard.get(bun.default_allocator, .string) catch |err| {
JSC.JSError(bun.default_allocator, "Error reading clipboard {s}", .{@errorName(err)}, global.ref(), exception);
return JSC.JSValue.jsUndefined();
};
var text = ZigString.init(data).withEncoding();
if (text.isUTF8()) {
var ret = text.toValueGC(global);
bun.default_allocator.free(data);
return JSC.JSPromise.resolvedPromiseValue(global, ret);
}
text.mark();
return JSC.JSPromise.resolvedPromiseValue(global, text.toExternalValue(global));
}
pub fn readPNG(
global: *JSC.JSGlobalObject,
exception: JSC.C.ExceptionRef,
) JSC.JSValue {
if (comptime !Environment.isMac) {
return JSC.JSValue.jsUndefined();
}
var data = macOS.Clipboard.get(bun.default_allocator, .png) catch |err| {
JSC.JSError(bun.default_allocator, "Error reading clipboard {s}", .{@errorName(err)}, global.ref(), exception);
return JSC.JSValue.jsUndefined();
};
if (data.len == 0) {
return JSC.JSPromise.resolvedPromiseValue(global, JSC.JSValue.jsUndefined());
}
var blob = JSC.WebCore.Blob.initWithStore(JSC.WebCore.Blob.Store.init(data, bun.default_allocator) catch unreachable, global);
var ptr = bun.default_allocator.create(JSC.WebCore.Blob) catch unreachable;
ptr.* = blob;
ptr.allocator = bun.default_allocator;
return JSC.JSPromise.resolvedPromiseValue(global, JSC.JSValue.c(JSC.WebCore.Blob.Class.make(global.ref(), ptr)));
}
pub fn writeText(
global: *JSGlobalObject,
text: ZigString,
) JSC.JSValue {
if (comptime !Environment.isMac) {
return JSC.JSValue.jsUndefined();
}
var slice = text.toSlice(bun.default_allocator);
defer slice.deinit();
var data = slice.slice();
macOS.Clipboard.set(.string, data) catch {
return ZigString.init("Error writing to clipboard").toErrorInstance(global);
};
return JSC.JSPromise.resolvedPromiseValue(global, JSC.JSValue.jsUndefined());
}
pub fn writePNG(
global: *JSGlobalObject,
blob_value: JSValue,
) JSC.JSValue {
if (comptime !Environment.isMac) {
return JSC.JSValue.jsUndefined();
}
var blob = blob_value.as(JSC.WebCore.Blob) orelse {
var err = ZigString.init("Expected Blob").toErrorInstance(global);
return JSC.JSPromise.rejectedPromiseValue(global, err);
};
if (blob.needsToReadFile()) {
var sink = bun.default_allocator.create(ClipboardPromise) catch unreachable;
sink.* = .{
.promise = JSC.JSPromise.create(global),
.global = global,
};
blob.doReadFileInternal(*ClipboardPromise, sink, ClipboardPromise.onFinishedLoading, global);
return sink.promise.asValue(global);
}
doWritePNG(blob.sharedView()) catch {
return JSC.JSPromise.rejectedPromiseValue(global, ZigString.init("Error writing to clipboard").toErrorInstance(global));
};
return JSC.JSPromise.resolvedPromiseValue(global, JSC.JSValue.jsUndefined());
}
pub fn doWritePNG(blob: []const u8) !void {
if (comptime !Environment.isMac) {
return;
}
try macOS.Clipboard.set(.png, blob);
}
const ClipboardPromise = struct {
promise: *JSC.JSPromise,
global: *JSGlobalObject,
pub fn onFinishedLoading(sink: *ClipboardPromise, bytes: JSC.WebCore.Blob.Store.ReadFile.ResultType) void {
switch (bytes) {
.err => |err| {
sink.promise.reject(sink.global, err.toErrorInstance(sink.global));
bun.default_allocator.destroy(sink);
return;
},
.result => |data| {
doWritePNG(data.buf) catch {
sink.promise.reject(sink.global, ZigString.init("Error writing to clipboard").toErrorInstance(sink.global));
bun.default_allocator.destroy(sink);
return;
};
sink.promise.resolve(sink.global, JSC.JSValue.jsUndefined());
bun.default_allocator.destroy(sink);
},
}
}
};
};
pub const Crypto = struct {
const Hashers = @import("../../../sha.zig");