Module: Echoes::ObjC

Defined in:
lib/echoes/objc.rb

Constant Summary collapse

LIBOBJC =
Fiddle.dlopen('/usr/lib/libobjc.A.dylib')
APPKIT =
Fiddle.dlopen('/System/Library/Frameworks/AppKit.framework/AppKit')
FOUNDATION =
Fiddle.dlopen('/System/Library/Frameworks/Foundation.framework/Foundation')
P =

Type aliases

Fiddle::TYPE_VOIDP
D =
Fiddle::TYPE_DOUBLE
L =
Fiddle::TYPE_LONG
I =
Fiddle::TYPE_INT
V =
Fiddle::TYPE_VOID
GetClass =

Core runtime functions

Fiddle::Function.new(LIBOBJC['objc_getClass'], [P], P)
RegisterName =
Fiddle::Function.new(LIBOBJC['sel_registerName'], [P], P)
AllocateClassPair =
Fiddle::Function.new(LIBOBJC['objc_allocateClassPair'], [P, P, I], P)
AddMethod =
Fiddle::Function.new(LIBOBJC['class_addMethod'], [P, P, P, P], I)
RegisterClassPair =
Fiddle::Function.new(LIBOBJC['objc_registerClassPair'], [P], V)
GetMethodImpl =
Fiddle::Function.new(LIBOBJC['class_getMethodImplementation'], [P, P], P)
AddProtocol =
Fiddle::Function.new(LIBOBJC['class_addProtocol'], [P, P], I)
GetProtocol =
Fiddle::Function.new(LIBOBJC['objc_getProtocol'], [P], P)
MSG_PTR =

id = msg(id, SEL)

new_msg([P, P], P)
MSG_PTR_1 =

id = msg(id, SEL, id)

new_msg([P, P, P], P)
MSG_PTR_2 =

id = msg(id, SEL, id, id)

new_msg([P, P, P, P], P)
MSG_PTR_L =

id = msg(id, SEL, long)

new_msg([P, P, L], P)
MSG_PTR_1L =

id = msg(id, SEL, id, long)

new_msg([P, P, P, L], P)
MSG_PTR_L_1 =

id = msg(id, SEL, long, id)

new_msg([P, P, L, P], P)
MSG_VOID =

void = msg(id, SEL)

new_msg([P, P], V)
MSG_VOID_1 =

void = msg(id, SEL, id)

new_msg([P, P, P], V)
MSG_VOID_2 =

void = msg(id, SEL, id, id)

new_msg([P, P, P, P], V)
MSG_VOID_4 =

void = msg(id, SEL, id, id, id, id)

new_msg([P, P, P, P, P, P], V)
MSG_PTR_3 =

id = msg(id, SEL, id, id, id)

new_msg([P, P, P, P, P], P)
MSG_VOID_I =

void = msg(id, SEL, int)

new_msg([P, P, I], V)
MSG_VOID_L =

void = msg(id, SEL, long)

new_msg([P, P, L], V)
MSG_VOID_2D =

void = msg(id, SEL, double, double)

new_msg([P, P, D, D], V)
MSG_RET_D =

double = msg(id, SEL)

new_msg([P, P], D)
MSG_PTR_D =

id = msg(id, SEL, double)

new_msg([P, P, D], P)
MSG_RET_D_1 =

double = msg(id, SEL, id)

new_msg([P, P, P], D)
MSG_RET_L =

long = msg(id, SEL)

new_msg([P, P], L)
MSG_PTR_RECT =

CGRect as 4 doubles

new_msg([P, P, D, D, D, D], P)
MSG_VOID_RECT =

NSRectFill equivalent

new_msg([P, P, D, D, D, D], V)
MSG_VOID_RECT_1 =

addCursorRect:cursor:

new_msg([P, P, D, D, D, D, P], V)
MSG_VOID_RECT_L_1 =

drawWithRect:options:attributes: (NSRect + NSStringDrawingOptions + NSDictionary)

new_msg([P, P, D, D, D, D, L, P], V)
MSG_VOID_RECT_D =

NSGradient drawInRect:angle: (4 doubles for rect + 1 double for angle)

new_msg([P, P, D, D, D, D, D], V)
MSG_PTR_RECT_L_L_I =

initWithContentRect:styleMask:backing:defer:

new_msg([P, P, D, D, D, D, L, L, I], P)
MSG_VOID_PT_1 =

drawAtPoint:withAttributes: (NSPoint = 2 doubles + id)

new_msg([P, P, D, D, P], V)
MSG_VOID_1_PT_1 =

popUpMenuPositioningItem:atLocation:inView: (id + NSPoint + id)

new_msg([P, P, P, D, D, P], V)
MSG_VOID_D_1 =

setDouble:forKey: (double + id) -> void

new_msg([P, P, D, P], V)
MSG_PTR_4D =

colorWithRed:green:blue:alpha: (4 doubles)

new_msg([P, P, D, D, D, D], P)
MSG_PTR_1D =

fontWithName:size: (id, double)

new_msg([P, P, P, D], P)
MSG_PTR_2D =

monospacedSystemFontOfSize:weight: (2 doubles)

new_msg([P, P, D, D], P)
MSG_PTR_D_P_P_P_I =

scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:

new_msg([P, P, D, P, P, P, I], P)
NSRectFill =

NSRectFill C function

Fiddle::Function.new(APPKIT['NSRectFill'], [D, D, D, D], V)
NSWindowStyleMaskTitled =

Cocoa constants

1 << 0
NSWindowStyleMaskClosable =
1 << 1
NSWindowStyleMaskMiniaturizable =
1 << 2
NSWindowStyleMaskResizable =
1 << 3
NSWindowStyleMaskDefault =
NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
NSBackingStoreBuffered =
2
NSEventModifierFlagShift =
1 << 17
NSEventModifierFlagControl =
1 << 18
NSEventModifierFlagOption =
1 << 19
NSEventModifierFlagCommand =
1 << 20
NSEventModifierFlagNumericPad =
1 << 21
SEL_CACHE =

Selector cache

{}
NSFontAttributeName =
appkit_const('NSFontAttributeName')
NSForegroundColorAttributeName =
appkit_const('NSForegroundColorAttributeName')
NSUnderlineStyleAttributeName =
appkit_const('NSUnderlineStyleAttributeName')
NSStrikethroughStyleAttributeName =
appkit_const('NSStrikethroughStyleAttributeName')
NSLigatureAttributeName =
appkit_const('NSLigatureAttributeName')
NSParagraphStyleAttributeName =
appkit_const('NSParagraphStyleAttributeName')
NSPasteboardTypeString =
appkit_const('NSPasteboardTypeString')
NSPasteboardTypeFileURL =
appkit_const('NSPasteboardTypeFileURL')
COREGRAPHICS =

CoreGraphics framework

Fiddle.dlopen('/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics')
CGColorSpaceCreateDeviceRGB =
Fiddle::Function.new(COREGRAPHICS['CGColorSpaceCreateDeviceRGB'], [], P)
CGColorSpaceRelease =
Fiddle::Function.new(COREGRAPHICS['CGColorSpaceRelease'], [P], V)
CGBitmapContextCreate =
Fiddle::Function.new(COREGRAPHICS['CGBitmapContextCreate'], [P, L, L, L, L, P, I], P)
CGBitmapContextCreateImage =
Fiddle::Function.new(COREGRAPHICS['CGBitmapContextCreateImage'], [P], P)
CGContextDrawImage =
Fiddle::Function.new(COREGRAPHICS['CGContextDrawImage'], [P, D, D, D, D, P], V)
CGContextSaveGState =
Fiddle::Function.new(COREGRAPHICS['CGContextSaveGState'], [P], V)
CGContextRestoreGState =
Fiddle::Function.new(COREGRAPHICS['CGContextRestoreGState'], [P], V)
CGContextTranslateCTM =
Fiddle::Function.new(COREGRAPHICS['CGContextTranslateCTM'], [P, D, D], V)
CGContextScaleCTM =
Fiddle::Function.new(COREGRAPHICS['CGContextScaleCTM'], [P, D, D], V)
CGContextRotateCTM =
Fiddle::Function.new(COREGRAPHICS['CGContextRotateCTM'], [P, D], V)
CGContextConcatCTM =

CGAffineTransform inlined as 6 doubles (a, b, c, d, tx, ty).

Fiddle::Function.new(COREGRAPHICS['CGContextConcatCTM'], [P, D, D, D, D, D, D], V)
CGContextClearRect =
Fiddle::Function.new(COREGRAPHICS['CGContextClearRect'], [P, D, D, D, D], V)
CGImageRelease =
Fiddle::Function.new(COREGRAPHICS['CGImageRelease'], [P], V)
CGContextRelease =
Fiddle::Function.new(COREGRAPHICS['CGContextRelease'], [P], V)
CGContextBeginPath =

Path construction

Fiddle::Function.new(COREGRAPHICS['CGContextBeginPath'], [P], V)
CGContextMoveToPoint =
Fiddle::Function.new(COREGRAPHICS['CGContextMoveToPoint'], [P, D, D], V)
CGContextAddLineToPoint =
Fiddle::Function.new(COREGRAPHICS['CGContextAddLineToPoint'], [P, D, D], V)
CGContextAddCurveToPoint =
Fiddle::Function.new(COREGRAPHICS['CGContextAddCurveToPoint'], [P, D, D, D, D, D, D], V)
CGContextAddQuadCurveToPoint =
Fiddle::Function.new(COREGRAPHICS['CGContextAddQuadCurveToPoint'], [P, D, D, D, D], V)
CGContextAddArc =

CGContextAddArc(ctx, x, y, radius, startAngle, endAngle, clockwise)

Fiddle::Function.new(COREGRAPHICS['CGContextAddArc'], [P, D, D, D, D, D, I], V)
CGContextClosePath =
Fiddle::Function.new(COREGRAPHICS['CGContextClosePath'], [P], V)
CGContextSetRGBFillColor =

Paint state

Fiddle::Function.new(COREGRAPHICS['CGContextSetRGBFillColor'], [P, D, D, D, D], V)
CGContextSetRGBStrokeColor =
Fiddle::Function.new(COREGRAPHICS['CGContextSetRGBStrokeColor'], [P, D, D, D, D], V)
CGContextSetAlpha =
Fiddle::Function.new(COREGRAPHICS['CGContextSetAlpha'], [P, D], V)
CGContextSetLineWidth =
Fiddle::Function.new(COREGRAPHICS['CGContextSetLineWidth'], [P, D], V)
CGContextSetLineCap =
Fiddle::Function.new(COREGRAPHICS['CGContextSetLineCap'], [P, I], V)
CGContextSetLineJoin =
Fiddle::Function.new(COREGRAPHICS['CGContextSetLineJoin'], [P, I], V)
CGContextSetMiterLimit =
Fiddle::Function.new(COREGRAPHICS['CGContextSetMiterLimit'], [P, D], V)
CGContextFillPath =

Path rasterization

Fiddle::Function.new(COREGRAPHICS['CGContextFillPath'], [P], V)
CGContextEOFillPath =
Fiddle::Function.new(COREGRAPHICS['CGContextEOFillPath'], [P], V)
CGContextStrokePath =
Fiddle::Function.new(COREGRAPHICS['CGContextStrokePath'], [P], V)
CGContextDrawPath =

CGContextDrawPath(ctx, mode) — mode picks fill / EO-fill / stroke / fill+stroke.

Fiddle::Function.new(COREGRAPHICS['CGContextDrawPath'], [P, I], V)
KCGImageAlphaPremultipliedLast =

kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault

1
KCG_PATH_FILL =

CGPathDrawingMode

0
KCG_PATH_EO_FILL =
1
KCG_PATH_STROKE =
2
KCG_PATH_FILL_STROKE =
3
KCG_PATH_EO_FILL_STROKE =
4
KCG_LINE_CAP_BUTT =

CGLineCap

0
KCG_LINE_CAP_ROUND =
1
KCG_LINE_CAP_SQUARE =
2
KCG_LINE_JOIN_MITER =

CGLineJoin

0
KCG_LINE_JOIN_ROUND =
1
KCG_LINE_JOIN_BEVEL =
2
CORETEXT =

CoreText framework

Fiddle.dlopen('/System/Library/Frameworks/CoreText.framework/CoreText')
CTFontCreateForString =

CTFontCreateForString(CTFontRef currentFont, CFStringRef string, CFRange range) -> CTFontRef CFRange is CFIndex = long, decomposed into 2 GPR args on arm64

Fiddle::Function.new(CORETEXT['CTFontCreateForString'], [P, P, L, L], P)

Class Method Summary collapse

Class Method Details

.appkit_const(name) ⇒ Object

AppKit string constant accessors



154
155
156
157
# File 'lib/echoes/objc.rb', line 154

def self.appkit_const(name)
  ptr = Fiddle::Pointer.new(APPKIT[name])
  Fiddle::Pointer.new(ptr[0, Fiddle::SIZEOF_VOIDP].unpack1('J'))
end

.cls(name) ⇒ Object



106
107
108
# File 'lib/echoes/objc.rb', line 106

def self.cls(name)
  GetClass.call(name)
end

.define_class(name, superclass_name, methods) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/echoes/objc.rb', line 143

def self.define_class(name, superclass_name, methods)
  super_cls = cls(superclass_name)
  new_cls = AllocateClassPair.call(super_cls, name, 0)
  methods.each do |sel_name, (type_encoding, closure)|
    AddMethod.call(new_cls, sel(sel_name), closure, type_encoding)
  end
  RegisterClassPair.call(new_cls)
  new_cls
end

.new_msg(args, ret) ⇒ Object

objc_msgSend variants for different signatures



29
30
31
# File 'lib/echoes/objc.rb', line 29

def self.new_msg(args, ret)
  Fiddle::Function.new(LIBOBJC['objc_msgSend'], args, ret)
end

.nsdict(hash) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/echoes/objc.rb', line 131

def self.nsdict(hash)
  dict = MSG_PTR.call(cls('NSMutableDictionary'), sel('dictionary'))
  hash.each do |key, value|
    MSG_VOID_2.call(dict, sel('setObject:forKey:'), value, key)
  end
  dict
end

.nsnumber_int(val) ⇒ Object



139
140
141
# File 'lib/echoes/objc.rb', line 139

def self.nsnumber_int(val)
  MSG_PTR_L.call(cls('NSNumber'), sel('numberWithInteger:'), val)
end

.nsstring(str) ⇒ Object



122
123
124
# File 'lib/echoes/objc.rb', line 122

def self.nsstring(str)
  MSG_PTR_1.call(cls('NSString'), sel('stringWithUTF8String:'), str)
end

.release(obj) ⇒ Object



118
119
120
# File 'lib/echoes/objc.rb', line 118

def self.release(obj)
  MSG_VOID.call(obj, sel('release'))
end

.retain(obj) ⇒ Object



114
115
116
# File 'lib/echoes/objc.rb', line 114

def self.retain(obj)
  MSG_PTR.call(obj, sel('retain'))
end

.sel(name) ⇒ Object



110
111
112
# File 'lib/echoes/objc.rb', line 110

def self.sel(name)
  SEL_CACHE[name] ||= RegisterName.call(name)
end

.to_ruby_string(nsstring_ptr) ⇒ Object



126
127
128
129
# File 'lib/echoes/objc.rb', line 126

def self.to_ruby_string(nsstring_ptr)
  cstr = MSG_PTR.call(nsstring_ptr, sel('UTF8String'))
  cstr.to_s.force_encoding(Encoding::UTF_8)
end