diff --git a/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st b/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st index 4ceda95..3a883d7 100644 --- a/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st +++ b/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st @@ -9,5 +9,6 @@ BaselineOfObjCBridge >> baseline: spec [ spec for: #common do: [ - spec package: 'ObjectiveC' ]. + spec package: 'ObjectiveC'. + spec package: 'ObjectiveC-Cocoa']. ] diff --git a/src/ObjectiveC-Cocoa/CocoaApplication.class.st b/src/ObjectiveC-Cocoa/CocoaApplication.class.st index 459e6a3..54a5844 100644 --- a/src/ObjectiveC-Cocoa/CocoaApplication.class.st +++ b/src/ObjectiveC-Cocoa/CocoaApplication.class.st @@ -7,7 +7,7 @@ Class { #pools : [ 'CocoaConstants' ], - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #accessing } diff --git a/src/ObjectiveC-Cocoa/CocoaConstants.class.st b/src/ObjectiveC-Cocoa/CocoaConstants.class.st index d2307c6..b0ac4e9 100644 --- a/src/ObjectiveC-Cocoa/CocoaConstants.class.st +++ b/src/ObjectiveC-Cocoa/CocoaConstants.class.st @@ -8,7 +8,7 @@ Class { 'NSSquareStatusItemLength', 'NSVariableStatusItemLength' ], - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #'class initialization' } diff --git a/src/ObjectiveC-Cocoa/CocoaMenu.class.st b/src/ObjectiveC-Cocoa/CocoaMenu.class.st new file mode 100644 index 0000000..b2256c6 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenu.class.st @@ -0,0 +1,138 @@ +" +I represent an item of a Cocoa Menu. +I can hold items or submenus. +" +Class { + #name : #CocoaMenu, + #superclass : #Object, + #instVars : [ + 'nsMenu', + 'items', + 'title', + 'nsTitle', + 'menuItem' + ], + #classVars : [ + 'MainMenu' + ], + #category : #'ObjectiveC-Cocoa-Menus' +} + +{ #category : #'instance creation' } +CocoaMenu class >> with: aNSMenu [ + + ^ self new + setMenu: aNSMenu; + yourself +] + +{ #category : #adding } +CocoaMenu >> addItemWithTitle: aString action: aBlock [ + + ^ self addItemWithTitle: aString action: aBlock shortcut: '' +] + +{ #category : #adding } +CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutString [ + + items add: (CocoaMenuItem new + title: aTitle; + action: actionBlock; + shortcut: shortcutString; + yourself) +] + +{ #category : #adding } +CocoaMenu >> addSeparator [ + + items add: CocoaMenuSeparator new +] + +{ #category : #adding } +CocoaMenu >> addServicesMenu [ + + items add: CocoaServicesMenu new +] + +{ #category : #adding } +CocoaMenu >> addSubmenu: aTitle with: builderBlock [ + + | menu | + menu := self class new. + menu title: aTitle. + builderBlock value: menu. + items add: menu. + ^ menu +] + +{ #category : #private } +CocoaMenu >> addToMenu: aCocoaMenu [ + + + self buildNSMenu. + menuItem := aCocoaMenu nsMenu + addItemWithTitle: nsTitle + action: ObjCObject nil + keyEquivalent: '' asNSString. + + aCocoaMenu nsMenu setSubmenu: nsMenu forItem: menuItem. + +] + +{ #category : #configuring } +CocoaMenu >> beMainMenu [ + + self buildNSMenu. + #NSApplication inObjC sharedApplication setMainMenu: nsMenu. + MainMenu := self +] + +{ #category : #private } +CocoaMenu >> buildNSMenu [ + + + nsTitle := (title ifNil: [ '' ]) asNSString. + nsMenu := #NSMenu inObjC alloc initWithTitle: nsTitle. + items do: [ :item | item addToMenu: self ]. + ^ nsMenu +] + +{ #category : #initialization } +CocoaMenu >> initialize [ + + super initialize. + items := OrderedCollection new +] + +{ #category : #accessing } +CocoaMenu >> nsMenu [ + + ^ nsMenu +] + +{ #category : #printing } +CocoaMenu >> printOn: aStream [ + aStream << self class name << '(' << (title ifNil: [ '' ]) << ')' +] + +{ #category : #removing } +CocoaMenu >> removeAllItems [ + + nsMenu removeAllItems. + + self flag: 'TODO: release ObjC menu items'. + items := OrderedCollection new. + +] + +{ #category : #private } +CocoaMenu >> setMenu: aNSMenu [ + + nsMenu := aNSMenu +] + +{ #category : #accessing } +CocoaMenu >> title: aString [ + + title := aString +] diff --git a/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st new file mode 100644 index 0000000..13f1165 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st @@ -0,0 +1,57 @@ +" +I represent an item of a Cocoa menu. +I can hold menu items or submenus. +" +Class { + #name : #CocoaMenuItem, + #superclass : #Object, + #instVars : [ + 'action', + 'shortcut', + 'title', + 'target', + 'nsTitle', + 'nsShortcut', + 'menuItem' + ], + #category : #'ObjectiveC-Cocoa-Menus' +} + +{ #category : #accessing } +CocoaMenuItem >> action: aBlock [ + + action := aBlock +] + +{ #category : #adding } +CocoaMenuItem >> addToMenu: aCocoaMenu [ + + + nsTitle := title asNSString. + nsShortcut := shortcut asNSString. + + menuItem := aCocoaMenu nsMenu + addItemWithTitle: nsTitle + action: #execute asObjCSelector + keyEquivalent: nsShortcut. + + target := CocoaMenuTarget new + block: action; + yourself. + ObjCProxyClass newFor: target. + menuItem setTarget: target. + menuItem setEnabled: true + +] + +{ #category : #accessing } +CocoaMenuItem >> shortcut: aString [ + + shortcut := aString +] + +{ #category : #accessing } +CocoaMenuItem >> title: aString [ + + title := aString +] diff --git a/src/ObjectiveC-Cocoa/CocoaMenuSeparator.class.st b/src/ObjectiveC-Cocoa/CocoaMenuSeparator.class.st new file mode 100644 index 0000000..5d59bf7 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenuSeparator.class.st @@ -0,0 +1,17 @@ +" +A Cocoa menu separator. I'm also a menu item. +" +Class { + #name : #CocoaMenuSeparator, + #superclass : #Object, + #category : #'ObjectiveC-Cocoa-Menus' +} + +{ #category : #adding } +CocoaMenuSeparator >> addToMenu: aCocoaMenu [ + + + | separator | + separator := #NSMenuItem inObjC separatorItem. + aCocoaMenu nsMenu addItem: separator +] diff --git a/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st new file mode 100644 index 0000000..09408ca --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st @@ -0,0 +1,43 @@ +" +I represent a Cocoa menu target, i.e. the ObjectiveC object that will receive the callbacks. +" +Class { + #name : #CocoaMenuTarget, + #superclass : #Object, + #instVars : [ + 'proxy', + 'block' + ], + #category : #'ObjectiveC-Cocoa-Menus' +} + +{ #category : #accessing } +CocoaMenuTarget >> block [ + + ^ block +] + +{ #category : #accessing } +CocoaMenuTarget >> block: anObject [ + + block := anObject +] + +{ #category : #execution } +CocoaMenuTarget >> execute [ + + + block value +] + +{ #category : #accessing } +CocoaMenuTarget >> proxy [ + + ^ proxy +] + +{ #category : #accessing } +CocoaMenuTarget >> proxy: anObject [ + + proxy := anObject +] diff --git a/src/ObjectiveC-Cocoa/CocoaMorphView.class.st b/src/ObjectiveC-Cocoa/CocoaMorphView.class.st index 7defce4..f30688e 100644 --- a/src/ObjectiveC-Cocoa/CocoaMorphView.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMorphView.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'morph' ], - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #private } diff --git a/src/ObjectiveC-Cocoa/CocoaServicesMenu.class.st b/src/ObjectiveC-Cocoa/CocoaServicesMenu.class.st new file mode 100644 index 0000000..0057b1a --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaServicesMenu.class.st @@ -0,0 +1,42 @@ +" +I represent the Mac os ""Services"" menu. +You can use me to add a ""Services"" entry to your app menu. +" +Class { + #name : #CocoaServicesMenu, + #superclass : #CocoaMenu, + #category : #'ObjectiveC-Cocoa-Menus' +} + +{ #category : #configuring } +CocoaServicesMenu >> beServicesMenu [ + + + #NSApplication inObjC sharedApplication setServicesMenu: nsMenu +] + +{ #category : #private } +CocoaServicesMenu >> buildNSMenu [ + + nsMenu := self servicesMenu. + nsMenu isNull + ifTrue: [ + super buildNSMenu. + self beServicesMenu ] + ifFalse: [ nsTitle := title asNSString ]. + ^ nsMenu +] + +{ #category : #initialization } +CocoaServicesMenu >> initialize [ + + super initialize. + title := 'Services' +] + +{ #category : #accessing } +CocoaServicesMenu >> servicesMenu [ + "Get the Application services menu if set or null" + + ^ #NSApplication inObjC sharedApplication servicesMenu +] diff --git a/src/ObjectiveC-Cocoa/CocoaViewController.class.st b/src/ObjectiveC-Cocoa/CocoaViewController.class.st index bdcdfd1..76b5ea6 100644 --- a/src/ObjectiveC-Cocoa/CocoaViewController.class.st +++ b/src/ObjectiveC-Cocoa/CocoaViewController.class.st @@ -4,7 +4,7 @@ Class { #pools : [ 'CocoaConstants' ], - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #private } diff --git a/src/ObjectiveC-Cocoa/CocoaWindowController.class.st b/src/ObjectiveC-Cocoa/CocoaWindowController.class.st index 1f4d1d2..ba6d21b 100644 --- a/src/ObjectiveC-Cocoa/CocoaWindowController.class.st +++ b/src/ObjectiveC-Cocoa/CocoaWindowController.class.st @@ -4,7 +4,7 @@ Class { #pools : [ 'CocoaConstants' ], - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #private } diff --git a/src/ObjectiveC-Cocoa/CoreGraphicsLibrary.class.st b/src/ObjectiveC-Cocoa/CoreGraphicsLibrary.class.st index 3240777..2cf9a45 100644 --- a/src/ObjectiveC-Cocoa/CoreGraphicsLibrary.class.st +++ b/src/ObjectiveC-Cocoa/CoreGraphicsLibrary.class.st @@ -1,7 +1,7 @@ Class { #name : #CoreGraphicsLibrary, #superclass : #FFILibrary, - #category : #ObjectiveC-Cocoa + #category : #'ObjectiveC-Cocoa' } { #category : #'accessing platform' } diff --git a/src/ObjectiveC-Cocoa/MacOSXAlertDelegate.class.st b/src/ObjectiveC-Cocoa/MacOSXAlertDelegate.class.st index 104bf80..e1d217d 100644 --- a/src/ObjectiveC-Cocoa/MacOSXAlertDelegate.class.st +++ b/src/ObjectiveC-Cocoa/MacOSXAlertDelegate.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'proxy' ], - #category : #ObjectiveC-Cocoa-Examples + #category : #'ObjectiveC-Cocoa-Examples' } { #category : #example } diff --git a/src/ObjectiveC-Cocoa/MacOSXWindowDelegate.class.st b/src/ObjectiveC-Cocoa/MacOSXWindowDelegate.class.st index df910a9..04b7f7b 100644 --- a/src/ObjectiveC-Cocoa/MacOSXWindowDelegate.class.st +++ b/src/ObjectiveC-Cocoa/MacOSXWindowDelegate.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'proxy' ], - #category : #ObjectiveC-Cocoa-Examples + #category : #'ObjectiveC-Cocoa-Examples' } { #category : #example } diff --git a/src/ObjectiveC-Cocoa/NSPoint.class.st b/src/ObjectiveC-Cocoa/NSPoint.class.st index 619661f..841971d 100644 --- a/src/ObjectiveC-Cocoa/NSPoint.class.st +++ b/src/ObjectiveC-Cocoa/NSPoint.class.st @@ -5,7 +5,7 @@ Class { 'OFFSET_X', 'OFFSET_Y' ], - #category : #ObjectiveC-Cocoa-Structure + #category : #'ObjectiveC-Cocoa-Structure' } { #category : #'field definition' } diff --git a/src/ObjectiveC-Cocoa/NSRect.class.st b/src/ObjectiveC-Cocoa/NSRect.class.st index 2c6f96c..17e6ca5 100644 --- a/src/ObjectiveC-Cocoa/NSRect.class.st +++ b/src/ObjectiveC-Cocoa/NSRect.class.st @@ -7,7 +7,7 @@ Class { 'OFFSET_X', 'OFFSET_Y' ], - #category : #ObjectiveC-Cocoa-Structure + #category : #'ObjectiveC-Cocoa-Structure' } { #category : #'field definition' } diff --git a/src/ObjectiveC-Cocoa/String.extension.st b/src/ObjectiveC-Cocoa/String.extension.st index 6408c82..b6156b7 100644 --- a/src/ObjectiveC-Cocoa/String.extension.st +++ b/src/ObjectiveC-Cocoa/String.extension.st @@ -2,7 +2,15 @@ Extension { #name : #String } { #category : #'*ObjectiveC-Cocoa' } String >> asNSString [ - ^ self - ifNotEmpty: [ #NSString inObjC alloc initWithUTF8String: self ] - ifEmpty: [ #NSString inObjC string ] + + + | encoded param | + encoded := self utf8Encoded. + param := ByteArray new: encoded size + 1. + param pinInMemory. + + LibC memCopy: encoded to: param size: encoded size. + param at: encoded size + 1 put: 0. + + ^ #NSString inObjC alloc initWithUTF8String: param ] diff --git a/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st new file mode 100644 index 0000000..eefd7f7 --- /dev/null +++ b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st @@ -0,0 +1,142 @@ +Class { + #name : #UNNotificationCenter, + #superclass : #Object, + #instVars : [ + 'delegate' + ], + #classVars : [ + 'UniqueInstance' + ], + #category : #'ObjectiveC-Cocoa-Notifications' +} + +{ #category : #examples } +UNNotificationCenter class >> example [ + + + + "To send notifications the application should be signed, for development we can use ad hoc signing with the command: + + To Sign: codesign --force --deep -s - MyApp.app + To Check: codesign -dv -r- MyApp.app + + " + + UNNotificationCenter uniqueInstance + showNotificationTitle: 'I am a notification' + body: 'with a nice body' +] + +{ #category : #accessing } +UNNotificationCenter class >> isAvailable [ + + ^ #UNUserNotificationCenter inObjC isNull not +] + +{ #category : #accessing } +UNNotificationCenter class >> new [ + + ^ self error: 'Use uniqueInstance instead' +] + +{ #category : #accessing } +UNNotificationCenter class >> uniqueInstance [ + + UniqueInstance ifNil: [ UniqueInstance := self basicNew initialize; yourself ]. + ^ UniqueInstance + ensureDelegate; + yourself +] + +{ #category : #private } +UNNotificationCenter >> currentNotificationCenter [ + + ^ #UNUserNotificationCenter inObjC currentNotificationCenter +] + +{ #category : #private } +UNNotificationCenter >> doShowNotificationTitle: aTitle body: aBody [ + + | notificationContent identifier request sem completionHandler errorDescription | + notificationContent := #UNMutableNotificationContent inObjC alloc + init. + notificationContent setTitle: aTitle asNSString. + notificationContent setBody: aBody asNSString. + notificationContent setSound: + #UNNotificationSound inObjC defaultSound. + + identifier := UUID new asString asNSString. + + request := #UNNotificationRequest inObjC + requestWithIdentifier: identifier + content: notificationContent + trigger: ObjCObject nil. + + sem := Semaphore new. + + completionHandler := ObjCBlock + signature: #( void #( ObjCObject error ) ) + block: [ :error | + error isNull ifFalse: [ errorDescription := self extractErrorDescription: error]. + sem signal ]. + + self currentNotificationCenter + addNotificationRequest: request + withCompletionHandler: completionHandler. + + sem wait. + + errorDescription ifNotNil: [ self error: errorDescription ] +] + +{ #category : #private } +UNNotificationCenter >> ensureDelegate [ + + self currentNotificationCenter delegate isNull ifFalse: [ ^ self ]. + + delegate := ObjCProxyClass newFor: UNNotificationCenterDelegate new. + self currentNotificationCenter setDelegate: delegate +] + +{ #category : #private } +UNNotificationCenter >> extractErrorDescription: error [ + + | errorObject | + errorObject := ObjCObject fromHandle: error. + ^ errorObject localizedDescription UTF8String asByteArray utf8Decoded +] + +{ #category : #'as yet unclassified' } +UNNotificationCenter >> requestPermissions [ + + | sem hasPermissions blk center errorDescription permissions | + permissions := 1 << 1 + (1 << 2). "Sound" "Alert" + + sem := Semaphore new. + blk := ObjCBlock + signature: #( void #( int granted , ObjCObject error ) ) + block: [ :granted :error | + hasPermissions := granted = 1. + error isNull ifFalse: [ + errorDescription := self extractErrorDescription: error ]. + sem signal ]. + + center := self currentNotificationCenter. + center + requestAuthorizationWithOptions: permissions + completionHandler: blk. + + sem wait. + + hasPermissions ifFalse: [ self error: errorDescription ] +] + +{ #category : #'as yet unclassified' } +UNNotificationCenter >> showNotificationTitle: aTitle body: aBody [ + + self requestPermissions. + + CocoaApplication withAutoreleasePoolDo: [ + self doShowNotificationTitle: aTitle body: aBody + ] +] diff --git a/src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st b/src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st new file mode 100644 index 0000000..e497f58 --- /dev/null +++ b/src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st @@ -0,0 +1,61 @@ +Class { + #name : #UNNotificationCenterDelegate, + #superclass : #Object, + #instVars : [ + 'proxy' + ], + #category : #'ObjectiveC-Cocoa-Notifications' +} + +{ #category : #'as yet unclassified' } +UNNotificationCenterDelegate >> notificationPresentationOption [ + + ^ UNNotificationPresentationOptions UNNotificationPresentationOptionBadge value + | UNNotificationPresentationOptions UNNotificationPresentationOptionBanner value + | UNNotificationPresentationOptions UNNotificationPresentationOptionList value + | UNNotificationPresentationOptions UNNotificationPresentationOptionSound value +] + +{ #category : #accessing } +UNNotificationCenterDelegate >> proxy [ + + ^ proxy +] + +{ #category : #accessing } +UNNotificationCenterDelegate >> proxy: anObject [ + + proxy := anObject +] + +{ #category : #'as yet unclassified' } +UNNotificationCenterDelegate >> userNotificationCenter: center didReceiveNotificationResponse: response withCompletionHandler: completionHandler [ + + + + "Just Delegating to the default action" + + | block | + + block := ObjCExternalBlock fromHandle: completionHandler signature: #(void #()). + block valueWithArguments: {}. +] + +{ #category : #'as yet unclassified' } +UNNotificationCenterDelegate >> userNotificationCenter: center openSettingsForNotification: notification [ + + + + "Our App does not provide configuration in App for the settings" +] + +{ #category : #'as yet unclassified' } +UNNotificationCenterDelegate >> userNotificationCenter: center willPresentNotification: notification withCompletionHandler: completionHandler [ + + + + | block | + + block := ObjCExternalBlock fromHandle: completionHandler signature: #(void #(uint)). + block valueWithArguments: { self notificationPresentationOption }. +] diff --git a/src/ObjectiveC-Cocoa/UNNotificationPresentationOptions.class.st b/src/ObjectiveC-Cocoa/UNNotificationPresentationOptions.class.st new file mode 100644 index 0000000..e57c6fd --- /dev/null +++ b/src/ObjectiveC-Cocoa/UNNotificationPresentationOptions.class.st @@ -0,0 +1,49 @@ +Class { + #name : #UNNotificationPresentationOptions, + #superclass : #FFIEnumeration, + #classVars : [ + 'UNNotificationPresentationOptionBadge', + 'UNNotificationPresentationOptionBanner', + 'UNNotificationPresentationOptionList', + 'UNNotificationPresentationOptionSound' + ], + #category : #'ObjectiveC-Cocoa-Notifications' +} + +{ #category : #'accessing enum' } +UNNotificationPresentationOptions class >> UNNotificationPresentationOptionBadge [ + "This method was automatically generated" + ^ UNNotificationPresentationOptionBadge +] + +{ #category : #'accessing enum' } +UNNotificationPresentationOptions class >> UNNotificationPresentationOptionBanner [ + "This method was automatically generated" + ^ UNNotificationPresentationOptionBanner +] + +{ #category : #'accessing enum' } +UNNotificationPresentationOptions class >> UNNotificationPresentationOptionList [ + "This method was automatically generated" + ^ UNNotificationPresentationOptionList +] + +{ #category : #'accessing enum' } +UNNotificationPresentationOptions class >> UNNotificationPresentationOptionSound [ + "This method was automatically generated" + ^ UNNotificationPresentationOptionSound +] + +{ #category : #'enum declaration' } +UNNotificationPresentationOptions class >> enumDecl [ + " + self initializeEnumeration + " + + ^ #( + UNNotificationPresentationOptionBadge 1 + UNNotificationPresentationOptionBanner 16 + UNNotificationPresentationOptionList 8 + UNNotificationPresentationOptionSound 2 + ) +] diff --git a/src/ObjectiveC-Cocoa/package.st b/src/ObjectiveC-Cocoa/package.st index 56d1f80..01dd431 100644 --- a/src/ObjectiveC-Cocoa/package.st +++ b/src/ObjectiveC-Cocoa/package.st @@ -1 +1 @@ -Package { #name : #ObjectiveC-Cocoa } +Package { #name : #'ObjectiveC-Cocoa' } diff --git a/src/ObjectiveC/ObjCBlock.class.st b/src/ObjectiveC/ObjCBlock.class.st new file mode 100644 index 0000000..66c19bc --- /dev/null +++ b/src/ObjectiveC/ObjCBlock.class.st @@ -0,0 +1,41 @@ +Class { + #name : #ObjCBlock, + #superclass : #FFIExternalObject, + #instVars : [ + 'block', + 'callback', + 'blockStruct', + 'blockDescriptor' + ], + #category : #'ObjectiveC-Core' +} + +{ #category : #initialization } +ObjCBlock class >> signature: aSignature block: aBlock [ + + ^ self new + signature: aSignature block: aBlock; + yourself +] + +{ #category : #initialization } +ObjCBlock >> signature: aSignature block: aBlock [ + + block := aBlock. + + blockDescriptor := ObjCBlockDescriptorStructure externalNew autoRelease; yourself. + blockStruct := ObjCBlockStructure externalNew autoRelease; yourself. + + blockStruct isa: (ExternalAddress loadSymbol: '_NSConcreteStackBlock'). + blockStruct flags: (1<<29). + blockStruct descriptor: blockDescriptor. + blockDescriptor size: blockStruct class byteSize. + + self setHandle: blockStruct getHandle. + + callback := ObjCBlockFunction signature: aSignature block: aBlock. + callback ffiLibrary: ObjCBlockLibrary. + blockStruct invoke: callback. + + +] diff --git a/src/ObjectiveC/ObjCBlockDescriptorStructure.class.st b/src/ObjectiveC/ObjCBlockDescriptorStructure.class.st new file mode 100644 index 0000000..637b8a3 --- /dev/null +++ b/src/ObjectiveC/ObjCBlockDescriptorStructure.class.st @@ -0,0 +1,48 @@ +Class { + #name : #ObjCBlockDescriptorStructure, + #superclass : #ObjCStructure, + #classVars : [ + 'OFFSET_RESERVED', + 'OFFSET_SIZE' + ], + #category : #'ObjectiveC-Inner' +} + +{ #category : #'field definition' } +ObjCBlockDescriptorStructure class >> fieldsDesc [ + + " + self rebuildFieldAccessors + " + + + ^ #( + ulong reserved; + ulong size + ) + +] + +{ #category : #'accessing - structure variables' } +ObjCBlockDescriptorStructure >> reserved [ + "This method was automatically generated" + ^handle platformUnsignedLongAt: OFFSET_RESERVED +] + +{ #category : #'accessing - structure variables' } +ObjCBlockDescriptorStructure >> reserved: anObject [ + "This method was automatically generated" + ^handle platformUnsignedLongAt: OFFSET_RESERVED put: anObject +] + +{ #category : #'accessing - structure variables' } +ObjCBlockDescriptorStructure >> size [ + "This method was automatically generated" + ^handle platformUnsignedLongAt: OFFSET_SIZE +] + +{ #category : #'accessing - structure variables' } +ObjCBlockDescriptorStructure >> size: anObject [ + "This method was automatically generated" + ^handle platformUnsignedLongAt: OFFSET_SIZE put: anObject +] diff --git a/src/ObjectiveC/ObjCBlockFunction.class.st b/src/ObjectiveC/ObjCBlockFunction.class.st new file mode 100644 index 0000000..ec03a94 --- /dev/null +++ b/src/ObjectiveC/ObjCBlockFunction.class.st @@ -0,0 +1,24 @@ +Class { + #name : #ObjCBlockFunction, + #superclass : #FFICallback, + #category : #'ObjectiveC-Core' +} + +{ #category : #initialization } +ObjCBlockFunction >> signature: signature block: aBlock [ + | parser | + + parser := self newParser. + functionSpec := parser parseAnonymousFunction: signature. + functionSpec arguments addFirst: (FFIVariableArgument name: '_blk' typeName: 'ObjCBlockStructure' arity: 1). + + functionSpec resolveUsing: parser requestor. + + block := aBlock +] + +{ #category : #evaluating } +ObjCBlockFunction >> valueWithArguments: args [ + + ^ block valueWithArguments: args allButFirst +] diff --git a/src/ObjectiveC/ObjCBlockLibrary.class.st b/src/ObjectiveC/ObjCBlockLibrary.class.st new file mode 100644 index 0000000..e586e9c --- /dev/null +++ b/src/ObjectiveC/ObjCBlockLibrary.class.st @@ -0,0 +1,17 @@ +Class { + #name : #ObjCBlockLibrary, + #superclass : #FFILibrary, + #category : #'ObjectiveC-Core' +} + +{ #category : #converting } +ObjCBlockLibrary >> calloutAPIClass [ + + ^ TFCalloutAPI +] + +{ #category : #converting } +ObjCBlockLibrary >> runner [ + + ^ TFWorker default +] diff --git a/src/ObjectiveC/ObjCBlockStructure.class.st b/src/ObjectiveC/ObjCBlockStructure.class.st new file mode 100644 index 0000000..51c8310 --- /dev/null +++ b/src/ObjectiveC/ObjCBlockStructure.class.st @@ -0,0 +1,105 @@ +Class { + #name : #ObjCBlockStructure, + #superclass : #ObjCStructure, + #classVars : [ + 'OFFSET_DESCRIPTOR', + 'OFFSET_FLAGS', + 'OFFSET_INVOKE', + 'OFFSET_ISA', + 'OFFSET_RESERVED' + ], + #category : #'ObjectiveC-Inner' +} + +{ #category : #'field definition' } +ObjCBlockStructure class >> fieldsDesc [ + + " + self rebuildFieldAccessors + " + + + ^ #( + void* isa; + int flags; + int reserved; + FFICallback invoke; + ObjCBlockDescriptorStructure* descriptor + ) + +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> descriptor [ + "This method was automatically generated" + ^ObjCBlockDescriptorStructure fromHandle: (handle pointerAt: OFFSET_DESCRIPTOR) +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> descriptor: anObject [ + "This method was automatically generated" + handle pointerAt: OFFSET_DESCRIPTOR put: anObject getHandle. +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> flags [ + "This method was automatically generated" + ^handle signedLongAt: OFFSET_FLAGS +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> flags: anObject [ + "This method was automatically generated" + handle signedLongAt: OFFSET_FLAGS put: anObject +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> invoke [ + "This method was automatically generated" + ^FFICallback forAddress: ((handle pointerAt: OFFSET_INVOKE) asInteger) +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> invoke: anObject [ + "This method was automatically generated" + handle + pointerAt: OFFSET_INVOKE + put: (anObject + ifNotNil: [ anObject thunk asExternalAddress ] + ifNil: [ ExternalAddress null ]) +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> invokeAddress [ + ^ handle pointerAt: OFFSET_INVOKE +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> isa [ + "This method was automatically generated" + ^ExternalData fromHandle: (handle pointerAt: OFFSET_ISA) type: ExternalType void asPointerType +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> isa: anObject [ + "This method was automatically generated" + handle pointerAt: OFFSET_ISA put: anObject getHandle. +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> reserved [ + "This method was automatically generated" + ^handle signedLongAt: OFFSET_RESERVED +] + +{ #category : #'accessing - structure variables' } +ObjCBlockStructure >> reserved: anObject [ + "This method was automatically generated" + handle signedLongAt: OFFSET_RESERVED put: anObject +] + +{ #category : #accessing } +ObjCBlockStructure >> thunk [ + + ^ self getHandle tfPointerAddress +] diff --git a/src/ObjectiveC/ObjCCalloutMethodBuilder.class.st b/src/ObjectiveC/ObjCCalloutMethodBuilder.class.st index 0bdee10..c49bc77 100644 --- a/src/ObjectiveC/ObjCCalloutMethodBuilder.class.st +++ b/src/ObjectiveC/ObjCCalloutMethodBuilder.class.st @@ -120,14 +120,30 @@ ObjCCalloutMethodBuilder >> generateFFICallout: builder spec: functionSpec [ "save ffi call as literal" builder pushLiteral: (self createFFICalloutLiteralFromSpec: functionSpec). "iterate arguments in order (in the function) to create the function call" - functionSpec arguments do: [ :each | each emitArgument: builder context: sender inCallout: self requestor ]. + functionSpec arguments do: [ :each | + each emitArgument: builder context: sender inCallout: self requestor. + each resolvedType tfExternalTypeWithArity emitMarshallToPrimitive: builder ]. + "create the array" builder pushConsArray: functionSpec arguments size. + builder addTemp: #argumentsArray. + builder storeTemp: #argumentsArray. + "send call and store into result" builder send: #invokeFunction:withArguments:. - functionSpec arguments do: [ :each | each emitReturnArgument: builder context: sender ]. + functionSpec arguments withIndexDo: [ :each :index| + each emitReturnArgument: builder context: sender. + each resolvedType tfExternalTypeWithArity + emitFreeIfNeededOfIndex: index + argumentsArrayTempName: #argumentsArray + withBuilder: builder ]. + + "Additional marshall in the case of TFFI" + functionSpec returnType resolvedType tfExternalTypeWithArity emitMarshallFromPrimitive: builder. + "convert in case return type needs it. And return reseult" + ^ functionSpec returnType emitReturn: builder resultTempVar: #result diff --git a/src/ObjectiveC/ObjCExternalBlock.class.st b/src/ObjectiveC/ObjCExternalBlock.class.st new file mode 100644 index 0000000..1234846 --- /dev/null +++ b/src/ObjectiveC/ObjCExternalBlock.class.st @@ -0,0 +1,54 @@ +Class { + #name : #ObjCExternalBlock, + #superclass : #Object, + #traits : 'TObjCLibrary', + #classTraits : 'TObjCLibrary classTrait', + #instVars : [ + 'blockAddress', + 'functionSpec', + 'block' + ], + #category : #'ObjectiveC-Core' +} + +{ #category : #'instance creation' } +ObjCExternalBlock class >> fromHandle: anExternalAddress signature: aCollection [ + + ^ self new + fromHandle: anExternalAddress signature: aCollection; + yourself + +] + +{ #category : #'instance creation' } +ObjCExternalBlock >> fromHandle: anExternalAddress signature: aCollection [ + + | requestor | + + blockAddress := anExternalAddress. + block := ObjCBlockStructure fromHandle: blockAddress. + + requestor := FFICallout new + requestor: self; + yourself. + + functionSpec := (FFIFunctionParser new + requestor: requestor; + parseAnonymousFunction: aCollection). + + functionSpec resolveUsing: requestor +] + +{ #category : #'instance creation' } +ObjCExternalBlock >> valueWithArguments: arguments [ + + | fun | + fun := TFExternalFunction + fromAddress: block invokeAddress + definition: (TFFunctionDefinition + parameterTypes: {TFBasicType pointer} , (functionSpec arguments collect: [:e | e tfExternalTypeWithArity ]) + returnType: functionSpec returnType tfExternalTypeWithArity). + + TFSameThreadRunner uniqueInstance invokeFunction: fun withArguments: { blockAddress} , arguments. + +] diff --git a/src/ObjectiveC/ObjCFunction.class.st b/src/ObjectiveC/ObjCFunction.class.st index aa4b3f6..8d07d96 100644 --- a/src/ObjectiveC/ObjCFunction.class.st +++ b/src/ObjectiveC/ObjCFunction.class.st @@ -22,5 +22,5 @@ ObjCFunction class >> signature: signature block: aBlock [ { #category : #initialization } ObjCFunction >> initializeSignature: signature block: aBlock [ callback := FFICallback signature: signature block: aBlock. - self setHandle: (ExternalAddress fromAddress: callback thunk address) + self setHandle: callback thunk ] diff --git a/src/ObjectiveC/ObjCLibrary.class.st b/src/ObjectiveC/ObjCLibrary.class.st index d7dc732..b6307f4 100644 --- a/src/ObjectiveC/ObjCLibrary.class.st +++ b/src/ObjectiveC/ObjCLibrary.class.st @@ -61,16 +61,69 @@ ObjCLibrary >> calloutAPIClass [ ^ TFCalloutAPI ] +{ #category : #'objc - creating' } +ObjCLibrary >> class_addMethodClass: cls selector: name implementation: imp signature: types [ + + ^ self ffiCall: #(int class_addMethod(void* cls, void* name, void* imp, const char *types)) +] + { #category : #'accessing platform' } ObjCLibrary >> ffiBindingOf: aName [ ^ self class ffiBindingOf: aName ] +{ #category : #'objc - accessing' } +ObjCLibrary >> lookupClass: aString [ + + ^ self ffiCall: #(void* objc_lookUpClass(char *aString)) +] + +{ #category : #'objc - accessing' } +ObjCLibrary >> lookupSelector: aString [ + + ^ self ffiCall: #(void* sel_registerName(const char *aString)) +] + { #category : #'accessing platform' } ObjCLibrary >> macLibraryName [ ^ 'libobjc.dylib' ] +{ #category : #'objc - converting' } +ObjCLibrary >> nsStringOf: aString [ + + | class encoded param | + + encoded := aString utf8Encoded. + param := ByteArray new: encoded size + 1. + param pinInMemory. + + LibC memCopy: encoded to: param size: encoded size. + param at: encoded size + 1 put: 0. + + class := self lookupClass: 'NSString'. + ^ self sendMessageNamed: 'stringWithUTF8String:' to: class with: param +] + +{ #category : #'objc - creating' } +ObjCLibrary >> objc_allocateClassPairSuperclass: superclass name: name extraBytes: extraBytes [ + + ^ self ffiCall: #(void* objc_allocateClassPair(void* superclass, const char *name, size_t extraBytes)) +] + +{ #category : #'objc - creating' } +ObjCLibrary >> objc_registerClassPair: cls [ + "Registers a class that was allocated using objc_allocateClassPair" + + self ffiCall: #(void objc_registerClassPair(void* cls)) +] + +{ #category : #'objc - releasing' } +ObjCLibrary >> release: aObjCObject [ + + self sendMessageNamed: 'release' to: aObjCObject +] + { #category : #accessing } ObjCLibrary >> runner [ @@ -82,3 +135,85 @@ ObjCLibrary >> runner [ ^ runner ] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessage: sel to: cls [ + + ^ self ffiCall: #(void* objc_msgSend(void* cls, void* sel)) +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessage: sel to: rcv with: aParam [ + + ^ self ffiCall: #(void* objc_msgSend(void* rcv, void* sel, void* aParam)) +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessage: sel to: rcv with: aParam1 with: aParam2 [ + + ^ self ffiCall: #(void* objc_msgSend(void* rcv, void* sel, void* aParam1, void* aParam2)) +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessage: sel to: rcv with: aParam1 with: aParam2 with: aParam3 [ + + ^ self ffiCall: #(void* objc_msgSend(void* rcv, void* sel, void* aParam1, void* aParam2, void* aParam3)) +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessage: sel to: rcv withInteger: aParam [ + + ^ self ffiCall: #(void* objc_msgSend(void* rcv, void* sel, int aParam)) +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName to: cls [ + + ^ self sendMessage: (self lookupSelector: selectorName) to: cls +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName to: rcv with: aParam [ + | sel | + + sel := self lookupSelector: selectorName. + ^ self sendMessage: sel to: rcv with: aParam +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName to: rcv with: aParam1 with: aParam2 [ + | sel | + + sel := self lookupSelector: selectorName. + ^ self sendMessage: sel to: rcv with: aParam1 with: aParam2 +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName to: rcv with: aParam1 with: aParam2 with: aParam3 [ + | sel | + + sel := self lookupSelector: selectorName. + ^ self sendMessage: sel to: rcv with: aParam1 with: aParam2 with: aParam3 +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName to: rcv withInteger: aParam [ + | sel | + + sel := self lookupSelector: selectorName. + ^ self sendMessage: sel to: rcv withInteger: aParam +] + +{ #category : #'objc - message sending' } +ObjCLibrary >> sendMessageNamed: selectorName toClassNamed: className [ + + ^ self sendMessage: (self lookupSelector: selectorName) to: (self lookupClass: className) +] + +{ #category : #'objc - accessing' } +ObjCLibrary >> sharedApplication [ + + ^ self + sendMessageNamed: 'sharedApplication' + toClassNamed: 'NSApplication' +] diff --git a/src/ObjectiveC/ObjCObject.class.st b/src/ObjectiveC/ObjCObject.class.st index f4ebd89..5e97095 100644 --- a/src/ObjectiveC/ObjCObject.class.st +++ b/src/ObjectiveC/ObjCObject.class.st @@ -9,6 +9,9 @@ Class { #instVars : [ 'isa' ], + #classVars : [ + 'OBJ_PTR' + ], #category : #'ObjectiveC-Core' } @@ -40,6 +43,18 @@ ObjCObject >> descriptionString [ ^ self description UTF8String ] +{ #category : #private } +ObjCObject >> doGetInstanceVariable: aName in: returnValue [ + + self ffiCall: #(void* object_getInstanceVariable(self, char * aName, void* returnValue)) +] + +{ #category : #private } +ObjCObject >> doSetInstanceVariable: aName to: aValue [ + + self ffiCall: #(void* object_setInstanceVariable(self, char * aName, void* aValue)) +] + { #category : #'reflective operations' } ObjCObject >> doesNotUnderstand: aMessage [ | shadowMethod arguments | @@ -52,6 +67,18 @@ ObjCObject >> doesNotUnderstand: aMessage [ arguments: arguments ] +{ #category : #private } +ObjCObject >> getInstanceVariable: aName [ + + | returnValue | + OBJ_PTR := FFIExternalValueHolder ofType: 'void *'. + returnValue := OBJ_PTR new value: ExternalAddress null. + + self doGetInstanceVariable: aName in: returnValue. + + ^ ObjCObject fromHandle: returnValue value. +] + { #category : #private } ObjCObject >> innerStructure [ "The inner structure from this object" @@ -123,5 +150,14 @@ ObjCObject >> objCPerform: aSEL [ { #category : #accessing } ObjCObject >> release [ + self objCPerform: #release asObjCSelector ] + +{ #category : #private } +ObjCObject >> setInstanceVariable: aName to: aValue [ + + self doSetInstanceVariable: aName to: aValue. + + ^ self +] diff --git a/src/ObjectiveC/ObjCSpecParser.class.st b/src/ObjectiveC/ObjCSpecParser.class.st index 293d526..85a3677 100644 --- a/src/ObjectiveC/ObjCSpecParser.class.st +++ b/src/ObjectiveC/ObjCSpecParser.class.st @@ -53,7 +53,9 @@ ObjCSpecParser class >> initializeMethodTypeMap [ at: ${ "_C_STRUCT_B" put: #FFIExternalStructure; "at: $}" "_C_STRUCT_E""put: []." "NB: this is an aggregated of mine, to handle annonymous functions" - at: $? put: #FFICallback. + at: $? put: #FFIExternalObject; + at: $B put: #FFIBool + . ReversedMethodTypeMap := Dictionary new: 32. ReversedMethodTypeMap diff --git a/src/ObjectiveC/String.extension.st b/src/ObjectiveC/String.extension.st index d1924e7..a590993 100644 --- a/src/ObjectiveC/String.extension.st +++ b/src/ObjectiveC/String.extension.st @@ -2,14 +2,8 @@ Extension { #name : #String } { #category : #'*ObjectiveC' } String >> adaptToObjC [ - | str | - self flag: #todo. "I don't know why, strings smaller than 12 length are crashing the VM" - str := UTF8TextConverter new convertFromSystemString: self. - ^ str "^ str size >= 12 - ifTrue: [ str ] - ifFalse: [ str padRightTo: 12 with: Character space ]" - - + + ^ self utf8Encoded ] { #category : #'*ObjectiveC' }