2014-03-01 03:17:32 -06:00
/ * 28 february 2014 * /
/ *
I wanted to avoid invoking Objective - C directly , preferring to do everything directly with the API . However , there are some things that simply cannot be done too well ; for those situations , there ' s this . It does use the Objective - C runtime , eschewing the actual Objective - C part of this being an Objective - C file .
The main culprits are :
- data types listed as being defined in nonexistent headers
- 32 - bit / 64 - bit type differences that are more than just a different typedef
2014-03-01 14:58:54 -06:00
- wrong documentation
2014-03-05 19:09:15 -06:00
though this is not always the case .
2014-03-01 03:17:32 -06:00
* /
2014-03-01 12:53:29 -06:00
# include "objc_darwin.h"
2014-03-01 03:17:32 -06:00
2014-03-02 22:11:29 -06:00
# include < stdlib . h >
2014-03-01 03:17:32 -06:00
# include < Foundation / NSGeometry . h >
2014-03-02 22:11:29 -06:00
# include < AppKit / NSKeyValueBinding . h >
2014-03-05 19:09:15 -06:00
# include < AppKit / NSEvent . h >
2014-03-30 13:25:01 -05:00
# include < AppKit / NSGraphics . h >
# include < AppKit / NSBitmapImageRep . h >
2014-04-04 13:52:38 -05:00
# include < AppKit / NSCell . h >
2014-04-06 22:33:27 -05:00
# include < AppKit / NSApplication . h >
2014-05-10 20:03:04 -05:00
# include < AppKit / NSTrackingArea . h >
2014-03-01 03:17:32 -06:00
2014-03-02 22:11:29 -06:00
/ * used by listbox_darwin . go ; requires NSString * /
id * _NSObservedObjectKey = ( id * ) ( & NSObservedObjectKey ) ;
2014-04-05 12:30:56 -05:00
/ *
These are all the selectors and class IDs used by the functions below .
* /
static SEL s_getIndexes ; / * NSIndexSetEntries ( ) * /
static id c_NSEvent ; / * makeDummyEvent ( ) * /
static SEL s_newEvent ;
static id c_NSBitmapImageRep ; / * drawImage ( ) * /
static SEL s_alloc ;
static SEL s_initWithBitmapDataPlanes ;
static SEL s_drawInRect ;
static SEL s_release ;
static SEL s_locationInWindow ; / * getTranslatedEventPoint ( ) * /
static SEL s_convertPointFromView ;
static id c_NSFont ;
static SEL s_setFont ; / * objc_setFont ( ) * /
static SEL s_systemFontOfSize ;
static SEL s_systemFontSizeForControlSize ;
2014-05-10 20:03:04 -05:00
static id c_NSTrackingArea ;
static SEL s_bounds ;
static SEL s_initTrackingArea ;
2014-04-05 12:30:56 -05:00
void initBleh ( )
{
s_getIndexes = sel_getUid ( "getIndexes:maxCount:inIndexRange:" ) ;
c_NSEvent = objc_getClass ( "NSEvent" ) ;
s_newEvent = sel_getUid ( "otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:" ) ;
c_NSBitmapImageRep = objc_getClass ( "NSBitmapImageRep" ) ;
s_alloc = sel_getUid ( "alloc" ) ;
s_initWithBitmapDataPlanes = sel_getUid ( "initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:" ) ;
s_drawInRect = sel_getUid ( "drawInRect:fromRect:operation:fraction:respectFlipped:hints:" ) ;
s_release = sel_getUid ( "release" ) ;
s_locationInWindow = sel_getUid ( "locationInWindow" ) ;
s_convertPointFromView = sel_getUid ( "convertPoint:fromView:" ) ;
c_NSFont = objc_getClass ( "NSFont" ) ;
s_setFont = sel_getUid ( "setFont:" ) ;
s_systemFontOfSize = sel_getUid ( "systemFontOfSize:" ) ;
s_systemFontSizeForControlSize = sel_getUid ( "systemFontSizeForControlSize:" ) ;
2014-05-10 20:03:04 -05:00
c_NSTrackingArea = objc_getClass ( "NSTrackingArea" ) ;
s_bounds = sel_getUid ( "bounds" ) ;
s_initTrackingArea = sel_getUid ( "initWithRect:options:owner:userInfo:" ) ;
2014-04-05 12:30:56 -05:00
}
2014-03-01 03:17:32 -06:00
/ *
2014-04-06 12:09:58 -05:00
NSUInteger is listed as being in < objc / NSObjCRuntime . h > . . . which doesn ' t exist . Rather than relying on undocumented header file locations or explicitly typedef - ing NSUInteger to the ( documented ) unsigned long , Even better : it appears to actually be < Foundation / NSObjCRuntime . h > , and requires Objective - C ( it declares NSString ) , so we don ' t have a choice but to just place things here . For maximum safety . I use uintptr_t as that should encompass every possible unsigned long .
2014-03-01 03:17:32 -06:00
* /
2014-03-02 22:28:43 -06:00
uintptr_t objc_msgSend _uintret _noargs ( id obj , SEL sel )
{
return ( uintptr_t ) ( ( NSUInteger ) objc_msgSend ( obj , sel ) ) ;
}
2014-04-04 17:56:37 -05:00
id objc_msgSend _uint ( id obj , SEL sel , uintptr_t a )
2014-03-01 03:17:32 -06:00
{
return objc_msgSend ( obj , sel , ( NSUInteger ) a ) ;
}
2014-03-02 22:28:43 -06:00
id objc_msgSend _id _uint ( id obj , SEL sel , id a , uintptr_t b )
{
return objc_msgSend ( obj , sel , a , ( NSUInteger ) b ) ;
}
2014-03-01 19:31:17 -06:00
/ *
same as above , but for NSInteger
* /
2014-03-02 17:38:45 -06:00
intptr_t objc_msgSend _intret _noargs ( id obj , SEL sel )
{
return ( intptr_t ) ( ( NSInteger ) objc_msgSend ( obj , sel ) ) ;
}
2014-03-01 19:31:17 -06:00
id objc_msgSend _int ( id obj , SEL sel , intptr_t a )
{
return objc_msgSend ( obj , sel , ( NSInteger ) a ) ;
}
2014-03-02 16:44:13 -06:00
id objc_msgSend _id _int ( id obj , SEL sel , id a , intptr_t b )
{
return objc_msgSend ( obj , sel , a , ( NSInteger ) b ) ;
}
2014-03-30 18:53:44 -05:00
/ *
same as above , but for unsigned short
* /
uintptr_t objc_msgSend _ushortret _noargs ( id obj , SEL sel )
{
return ( uintptr_t ) ( ( unsigned short ) objc_msgSend ( obj , sel ) ) ;
}
2014-03-01 03:17:32 -06:00
/ *
These are the objc_msgSend ( ) wrappers around NSRect . The problem is that while on 32 - bit systems , NSRect is a concrete structure , on 64 - bit systems it ' s just a typedef to CGRect . While in practice just using CGRect everywhere seems to work , better to be safe than sorry .
I use int64_t for maximum safety , as my coordinates are stored as Go ints and Go int -> C int ( which is what is documented as happening ) isn ' t reliable .
* /
2014-03-01 14:58:54 -06:00
/ *
2014-03-01 20:42:57 -06:00
This is not documented in the docs , but is in various places on apple . com . In fact , the docs are actually WRONG : they say you pass a pointer to the structure as the first argument to objc_msgSend _stret ( ) ! And there might be some cases where we can ' t use stret because the struct is small enough . . .
2014-03-01 14:58:54 -06:00
* /
static NSRect ( * objc_msgSend _stret _rect ) ( id , SEL , . . . ) =
( NSRect ( * ) ( id , SEL , . . . ) ) objc_msgSend _stret ;
2014-03-01 12:58:38 -06:00
struct xrect objc_msgSend _stret _rect _noargs ( id obj , SEL sel )
{
NSRect s ;
struct xrect t ;
2014-03-01 14:58:54 -06:00
s = objc_msgSend _stret _rect ( obj , sel ) ;
2014-03-01 12:58:38 -06:00
t . x = ( int64_t ) s . origin . x ;
t . y = ( int64_t ) s . origin . y ;
t . width = ( int64_t ) s . size . width ;
t . height = ( int64_t ) s . size . height ;
return t ;
}
2014-03-01 03:17:32 -06:00
# define OurRect ( ) ( NSMakeRect ( ( CGFloat ) x , ( CGFloat ) y , ( CGFloat ) w , ( CGFloat ) h ) )
2014-04-04 18:51:23 -05:00
id objc_msgSend _rect ( id obj , SEL sel , int64_t x , int64_t y , int64_t w , int64_t h )
2014-03-01 03:17:32 -06:00
{
return objc_msgSend ( obj , sel , OurRect ( ) ) ;
}
2014-04-04 18:08:25 -05:00
id objc_msgSend _rect _bool ( id obj , SEL sel , int64_t x , int64_t y , int64_t w , int64_t h , BOOL b )
2014-03-01 12:29:24 -06:00
{
return objc_msgSend ( obj , sel , OurRect ( ) , b ) ;
}
2014-04-04 18:14:03 -05:00
id objc_msgSend _rect _uint _uint _bool ( id obj , SEL sel , int64_t x , int64_t y , int64_t w , int64_t h , uintptr_t b , uintptr_t c , BOOL d )
2014-03-01 03:17:32 -06:00
{
return objc_msgSend ( obj , sel , OurRect ( ) , ( NSUInteger ) b , ( NSUInteger ) c , d ) ;
}
2014-03-01 03:21:47 -06:00
/ *
Same as NSRect above , but for NSSize now .
* /
2014-03-01 20:42:57 -06:00
/ *
2014-03-01 20:52:15 -06:00
. . . like this one . ( Note which function is being cast below . ) This is an Intel - specific optimization ; though this code won ' t run on PowerPC Macs ( Go , and thus package ui , requires 10.6 ) , if desktop ARM becomes a thing all bets are off . ( tl ; dr TODO )
2014-03-01 20:42:57 -06:00
* /
2014-03-01 14:58:54 -06:00
static NSSize ( * objc_msgSend _stret _size ) ( id , SEL , . . . ) =
2014-03-01 20:42:57 -06:00
( NSSize ( * ) ( id , SEL , . . . ) ) objc_msgSend ;
2014-03-01 14:58:54 -06:00
2014-03-01 03:21:47 -06:00
struct xsize objc_msgSend _stret _size _noargs ( id obj , SEL sel )
{
NSSize s ;
struct xsize t ;
2014-03-01 14:58:54 -06:00
s = objc_msgSend _stret _size ( obj , sel ) ;
2014-03-01 03:21:47 -06:00
t . width = ( int64_t ) s . width ;
t . height = ( int64_t ) s . height ;
return t ;
}
2014-03-02 22:11:29 -06:00
2014-04-13 19:16:05 -05:00
id objc_msgSend _size ( id obj , SEL sel , int64_t width , int64_t height )
{
return objc_msgSend ( obj , sel , NSMakeSize ( ( CGFloat ) width , ( CGFloat ) height ) ) ;
}
2014-03-30 13:25:01 -05:00
/ *
and again for NSPoint
* /
id objc_msgSend _point ( id obj , SEL sel , int64_t x , int64_t y )
{
return objc_msgSend ( obj , sel , NSMakePoint ( ( CGFloat ) x , ( CGFloat ) y ) ) ;
}
2014-03-02 22:11:29 -06:00
/ *
This is a doozy : it deals with a NSUInteger array needed for this one selector , and converts them all into a uintptr_t array so we can use it from Go . The two arrays are created at runtime with malloc ( ) ; only the NSUInteger one is freed here , while Go frees the returned one . It ' s not optimal .
* /
uintptr_t * NSIndexSetEntries ( id indexset , uintptr_t count )
{
NSUInteger * nsuints ;
uintptr_t * ret ;
uintptr_t i ;
size_t countsize ;
countsize = ( size_t ) count ;
nsuints = ( NSUInteger * ) malloc ( countsize * sizeof ( NSUInteger ) ) ;
/ * TODO check return value * /
2014-04-05 12:30:56 -05:00
objc_msgSend ( indexset , s_getIndexes ,
2014-03-02 22:11:29 -06:00
nsuints , ( NSUInteger ) count , nil ) ;
ret = ( uintptr_t * ) malloc ( countsize * sizeof ( uintptr_t ) ) ;
for ( i = 0 ; i < count ; i + + ) {
ret [ i ] = ( uintptr_t ) nsuints [ i ] ;
}
free ( nsuints ) ;
return ret ;
}
2014-03-05 19:09:15 -06:00
/ *
See uitask_darwin . go : we need to synthesize a NSEvent so - [ NSApplication stop : ] will work . We cannot simply init the default NSEvent though ( it throws an exception ) so we must do it "the right way" . This involves a very convoluted initializer ; we ' ll just do it here to keep things clean on the Go side ( this will only be run once anyway , on program exit ) .
* /
id makeDummyEvent ( )
{
return objc_msgSend ( c_NSEvent , s_newEvent ,
( NSUInteger ) NSApplicationDefined , / * otherEventWithType : * /
NSMakePoint ( 0 , 0 ) , / * location : * /
( NSUInteger ) 0 , / * modifierFlags : * /
( double ) 0 , / * timestamp : * /
( NSInteger ) 0 , / * windowNumber : * /
nil , / * context : * /
( short ) 0 , / * subtype : * /
( NSInteger ) 0 , / * data1 : * /
( NSInteger ) 0 ) ; / * data2 : * /
}
2014-03-29 22:57:49 -05:00
/ *
[ NSView drawRect : ] needs to be overridden in our Area subclass . This takes a NSRect , which I ' m not sure how to encode , so we ' re going to have to use @ encode ( ) and hope for the best for portability .
* /
extern void areaView_drawRect ( id , struct xrect ) ;
2014-04-04 21:21:53 -05:00
static void __areaView _drawRect ( id self , SEL sel , NSRect r )
2014-03-29 22:57:49 -05:00
{
struct xrect t ;
t . x = ( int64_t ) r . origin . x ;
t . y = ( int64_t ) r . origin . y ;
t . width = ( int64_t ) r . size . width ;
t . height = ( int64_t ) r . size . height ;
areaView_drawRect ( self , t ) ;
}
2014-04-04 21:21:53 -05:00
void * _areaView _drawRect = ( void * ) __areaView _drawRect ;
2014-03-29 22:57:49 -05:00
2014-04-06 22:33:27 -05:00
/ *
this and one below it are the only objective - c feature you ' ll see here
2014-03-29 22:57:49 -05:00
2014-04-04 21:21:53 -05:00
unfortunately NSRect both varies across architectures and is passed as just a structure , so its encoding has to be computed at compile time
because @ encode ( ) is NOT A LITERAL , we ' re going to just stick it all the way back in objc_darwin . go
see also : http : // stackoverflow . com / questions / 6812035 / adding - methods - dynamically
* /
char * encodedNSRect = @ encode ( NSRect ) ;
2014-03-30 13:25:01 -05:00
/ *
the NSBitmapImageRep constructor is complex ; put it here
the only way to draw a NSBitmapImageRep in a flipped NSView is to use the most complex drawing method ; put it here too
* /
2014-04-04 21:51:30 -05:00
/ *
hey guys you know what ' s fun ? 32 - bit ABI changes !
* /
static BOOL ( * objc_msgSend _drawInRect ) ( id , SEL , NSRect , NSRect , NSCompositingOperation , CGFloat , BOOL , id ) =
( BOOL ( * ) ( id , SEL , NSRect , NSRect , NSCompositingOperation , CGFloat , BOOL , id ) ) objc_msgSend ;
2014-03-30 16:52:27 -05:00
void drawImage ( void * pixels , int64_t width , int64_t height , int64_t stride , int64_t xdest , int64_t ydest )
2014-03-30 13:25:01 -05:00
{
unsigned char * planes [ 1 ] ; / * NSBitmapImageRep wants an array of planes ; we have one plane * /
id bitmap ;
bitmap = objc_msgSend ( c_NSBitmapImageRep , s_alloc ) ;
planes [ 0 ] = ( unsigned char * ) pixels ;
bitmap = objc_msgSend ( bitmap , s_initWithBitmapDataPlanes ,
2014-04-11 15:17:06 -05:00
planes , / * initWithBitmapDataPlanes : * /
( NSInteger ) width , / * pixelsWide : * /
( NSInteger ) height , / * pixelsHigh : * /
( NSInteger ) 8 , / * bitsPerSample : * /
( NSInteger ) 4 , / * samplesPerPixel : * /
( BOOL ) YES , / * hasAlpha : * /
( BOOL ) NO , / * isPlanar : * /
NSCalibratedRGBColorSpace , / * colorSpaceName : | TODO NSDeviceRGBColorSpace ? * /
( NSBitmapFormat ) 0 , / * bitmapFormat : | this is where the flag for placing alpha first would go if alpha came first ; the default is alpha last , which is how we ' re doing things ( otherwise the docs say "Color planes are arranged in the standard order—for example, red before green before blue for RGB color." ) ; this is also where the flag for non - premultiplied colors would go if we used it ( the default is alpha - premultiplied ) * /
( NSInteger ) stride , / * bytesPerRow : * /
( NSInteger ) 32 ) ; / * bitsPerPixel : * /
2014-04-04 21:51:30 -05:00
/ * TODO this CAN fail ; check error * /
objc_msgSend _drawInRect ( bitmap , s_drawInRect ,
2014-03-30 13:25:01 -05:00
NSMakeRect ( ( CGFloat ) xdest , ( CGFloat ) ydest ,
( CGFloat ) width , ( CGFloat ) height ) , / * drawInRect : * /
NSZeroRect , / * fromRect : | draw whole image * /
( NSCompositingOperation ) NSCompositeSourceOver , / * op : * /
( CGFloat ) 1.0 , / * fraction : * /
( BOOL ) YES , / * respectFlipped : * /
nil ) ; / * hints : * /
objc_msgSend ( bitmap , s_release ) ;
}
2014-03-30 16:52:27 -05:00
/ *
more NSPoint fumbling
* /
static NSPoint ( * objc_msgSend _stret _point ) ( id , SEL , . . . ) =
( NSPoint ( * ) ( id , SEL , . . . ) ) objc_msgSend ;
struct xpoint getTranslatedEventPoint ( id self , id event )
{
NSPoint p ;
struct xpoint ret ;
p = objc_msgSend _stret _point ( event , s_locationInWindow ) ;
p = objc_msgSend _stret _point ( self , s_convertPointFromView ,
p , / * convertPoint : * /
nil ) ; / * fromView : * /
ret . x = ( int64_t ) p . x ;
ret . y = ( int64_t ) p . y ;
return ret ;
}
2014-04-04 13:52:38 -05:00
/ *
we don ' t need this here technically — it can be done in Go just fine — but it ' s easier here
* /
static CGFloat ( * objc_msgSend _cgfloatret ) ( id , SEL , . . . ) =
( CGFloat ( * ) ( id , SEL , . . . ) ) objc_msgSend _fpret ;
void objc_setFont ( id what , unsigned int csize )
{
CGFloat size ;
size = objc_msgSend _cgfloatret ( c_NSFont , s_systemFontSizeForControlSize , ( NSControlSize ) csize ) ;
objc_msgSend ( what , s_setFont ,
objc_msgSend ( c_NSFont , s_systemFontOfSize , size ) ) ;
}
2014-04-06 22:33:27 -05:00
/ *
- [ NSApplicationDelegate applicationShouldTerminate ] used to return a BOOL , but now returns a NSApplicationTerminateReply , which is a NSUInteger ; hence , here .
* /
extern void appDelegate_applicationShouldTerminate ( ) ;
2014-04-07 11:46:09 -05:00
static NSApplicationTerminateReply __appDelegate _applicationShouldTerminate ( id self , SEL sel , id app )
2014-04-06 22:33:27 -05:00
{
appDelegate_applicationShouldTerminate ( ) ;
return NSTerminateCancel ; // don ' t quit
}
void * _appDelegate _applicationShouldTerminate = ( void * ) __appDelegate _applicationShouldTerminate ;
char * encodedTerminateReply = @ encode ( NSApplicationTerminateReply ) ;
2014-05-10 20:03:04 -05:00
/ *
tracking areas ; also here for convenience only
* /
/ * IDK if this is needed ; just to be safe * /
static id ( * objc_msgSend _initTrackingArea ) ( id , SEL , NSRect , NSTrackingAreaOptions , id , id ) =
( id ( * ) ( id , SEL , NSRect , NSTrackingAreaOptions , id , id ) ) objc_msgSend ;
id makeTrackingArea ( id area )
{
id trackingArea ;
trackingArea = objc_msgSend ( c_NSTrackingArea , s_alloc ) ;
trackingArea = ( * objc_msgSend _initTrackingArea ) ( trackingArea , s_initTrackingArea ,
( * objc_msgSend _stret _rect ) ( area , s_bounds ) , / * initWithRect : * /
2014-05-10 20:04:02 -05:00
/ * this bit mask ( except for NSTrackingInVisibleRect , which was added later to prevent events from being triggered outside the visible area of the Area ) comes from https : // github . com / andlabs / misctestprogs / blob / master / cocoaviewmousetest . m ( and I wrote this bit mask on 25 april 2014 ) and yes I know it includes enter / exit even though we don ' t watch those events ; it probably won ' t really matter anyway but if it does I can change it easily * /
2014-05-10 20:03:04 -05:00
( NSTrackingAreaOptions ) ( NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingEnabledDuringMouseDrag | NSTrackingInVisibleRect ) , / * options : * /
area , / * owner : * /
nil ) ; / * userData : * /
return trackingArea ;
}