From 14b910141e24883964ec75a43f60b0b85a6faf99 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Tue, 25 Jul 2023 17:39:55 +0200 Subject: [PATCH 1/8] Adding menu support --- .../CocoaApplication.class.st | 2 +- src/ObjectiveC-Cocoa/CocoaConstants.class.st | 2 +- src/ObjectiveC-Cocoa/CocoaMenu.class.st | 90 +++++++++++++++++++ src/ObjectiveC-Cocoa/CocoaMenuItem.class.st | 44 +++++++++ src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st | 40 +++++++++ src/ObjectiveC-Cocoa/CocoaMorphView.class.st | 2 +- .../CocoaViewController.class.st | 2 +- .../CocoaWindowController.class.st | 2 +- .../CoreGraphicsLibrary.class.st | 2 +- .../MacOSXAlertDelegate.class.st | 2 +- .../MacOSXWindowDelegate.class.st | 2 +- src/ObjectiveC-Cocoa/NSPoint.class.st | 2 +- src/ObjectiveC-Cocoa/NSRect.class.st | 2 +- src/ObjectiveC-Cocoa/package.st | 2 +- .../ObjCCalloutMethodBuilder.class.st | 20 ++++- src/ObjectiveC/ObjCObject.class.st | 35 ++++++++ src/ObjectiveC/ObjCSpecParser.class.st | 4 +- src/ObjectiveC/String.extension.st | 10 +-- 18 files changed, 243 insertions(+), 22 deletions(-) create mode 100644 src/ObjectiveC-Cocoa/CocoaMenu.class.st create mode 100644 src/ObjectiveC-Cocoa/CocoaMenuItem.class.st create mode 100644 src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st 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..b234f98 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenu.class.st @@ -0,0 +1,90 @@ +Class { + #name : #CocoaMenu, + #superclass : #Object, + #instVars : [ + 'nsMenu', + 'title', + 'nsTitle', + 'items', + 'menuItem' + ], + #classVars : [ + 'MainMenu' + ], + #category : #'ObjectiveC-Cocoa' +} + +{ #category : #adding } +CocoaMenu >> addItemWithTitle: aString action: aFullBlockClosure [ + + ^ self addItemWithTitle: aString action: aFullBlockClosure shortcut: '' +] + +{ #category : #adding } +CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutString [ + + items add: (CocoaMenuItem new + title: aTitle; + action: actionBlock; + shortcut: shortcutString; + yourself) +] + +{ #category : #adding } +CocoaMenu >> addSubmenu: aTitle with: builderBlock [ + + | m | + m := self class new. + m title: aTitle. + builderBlock value: m. + items add: m. + ^ m +] + +{ #category : #adding } +CocoaMenu >> addToMenu: aCocoaMenu [ + + self buildNSMenu. + + menuItem := aCocoaMenu nsMenu addItemWithTitle: nsTitle action: ObjCObject nil keyEquivalent:'' asNSString. + + aCocoaMenu nsMenu setSubmenu: nsMenu forItem: menuItem. + +] + +{ #category : #building } +CocoaMenu >> buildNSMenu [ + + nsTitle := title asNSString. + nsMenu := #NSMenu inObjC alloc initWithTitle: nsTitle. + + items do: [ :i | i addToMenu: self ]. + + ^ nsMenu +] + +{ #category : #initialization } +CocoaMenu >> initialize [ + + super initialize. + items := OrderedCollection new +] + +{ #category : #accessing } +CocoaMenu >> nsMenu [ + ^ nsMenu +] + +{ #category : #initialization } +CocoaMenu >> setAsMainMenu [ + + self buildNSMenu. + #NSApplication inObjC sharedApplication setMainMenu: nsMenu. + + MainMenu := self +] + +{ #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..4e7a16e --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st @@ -0,0 +1,44 @@ +Class { + #name : #CocoaMenuItem, + #superclass : #Object, + #instVars : [ + 'title', + 'action', + 'shortcut', + 'target', + 'nsTitle', + 'nsShortcut', + 'menuItem' + ], + #category : #'ObjectiveC-Cocoa' +} + +{ #category : #accessing } +CocoaMenuItem >> action: aFullBlockClosure [ + action := aFullBlockClosure +] + +{ #category : #adding } +CocoaMenuItem >> addToMenu: aCocoaMenu [ + + target := CocoaMenuTarget new block: action; yourself. + ObjCProxyClass newFor: target. + + nsTitle := title asNSString. + nsShortcut := shortcut asNSString. + + menuItem := aCocoaMenu nsMenu addItemWithTitle: nsTitle action: #execute asObjCSelector keyEquivalent: nsShortcut. + 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/CocoaMenuTarget.class.st b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st new file mode 100644 index 0000000..bde4237 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st @@ -0,0 +1,40 @@ +Class { + #name : #CocoaMenuTarget, + #superclass : #Object, + #instVars : [ + 'proxy', + 'block' + ], + #category : #'ObjectiveC-Cocoa' +} + +{ #category : #accessing } +CocoaMenuTarget >> block [ + + ^ block +] + +{ #category : #accessing } +CocoaMenuTarget >> block: anObject [ + + block := anObject +] + +{ #category : #accessing } +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/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/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/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/ObjCObject.class.st b/src/ObjectiveC/ObjCObject.class.st index f4ebd89..c83cfc4 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" @@ -125,3 +152,11 @@ ObjCObject >> objCPerform: aSEL [ 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..971f476 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: #FFICallback; + 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' } From 417dfc19699c2113680bd5ef17deecf207fadc83 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Mon, 31 Jul 2023 11:18:40 +0200 Subject: [PATCH 2/8] Adding support for Objective-C Blocks --- .../CocoaNotificationCenterDelegate.class.st | 49 ++++++++ src/ObjectiveC/ObjCBlock.class.st | 41 +++++++ .../ObjCBlockDescriptorStructure.class.st | 48 ++++++++ src/ObjectiveC/ObjCBlockFunction.class.st | 24 ++++ src/ObjectiveC/ObjCBlockLibrary.class.st | 17 +++ src/ObjectiveC/ObjCBlockStructure.class.st | 105 ++++++++++++++++++ src/ObjectiveC/ObjCExternalBlock.class.st | 54 +++++++++ src/ObjectiveC/ObjCFunction.class.st | 2 +- src/ObjectiveC/ObjCObject.class.st | 1 + src/ObjectiveC/ObjCSpecParser.class.st | 4 +- 10 files changed, 342 insertions(+), 3 deletions(-) create mode 100644 src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st create mode 100644 src/ObjectiveC/ObjCBlock.class.st create mode 100644 src/ObjectiveC/ObjCBlockDescriptorStructure.class.st create mode 100644 src/ObjectiveC/ObjCBlockFunction.class.st create mode 100644 src/ObjectiveC/ObjCBlockLibrary.class.st create mode 100644 src/ObjectiveC/ObjCBlockStructure.class.st create mode 100644 src/ObjectiveC/ObjCExternalBlock.class.st diff --git a/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st b/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st new file mode 100644 index 0000000..c326fad --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st @@ -0,0 +1,49 @@ +Class { + #name : #CocoaNotificationCenterDelegate, + #superclass : #Object, + #instVars : [ + 'proxy' + ], + #category : #'ObjectiveC-Cocoa' +} + +{ #category : #accessing } +CocoaNotificationCenterDelegate >> proxy [ + + ^ proxy +] + +{ #category : #accessing } +CocoaNotificationCenterDelegate >> proxy: anObject [ + + proxy := anObject +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenterDelegate >> userNotificationCenter: center didReceiveNotificationResponse: response withCompletionHandler: completionHandler [ + + + +1halt. + thisContext method selector traceCr. +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenterDelegate >> userNotificationCenter: center openSettingsForNotification: notification [ + + + +1halt. + thisContext method selector traceCr. +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenterDelegate >> userNotificationCenter: center willPresentNotification: notification withCompletionHandler: completionHandler [ + + + + | block | + + block := ObjCExternalBlock fromHandle: completionHandler signature: #(void #(uint)). + block valueWithArguments: { 7 }. +] 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/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/ObjCObject.class.st b/src/ObjectiveC/ObjCObject.class.st index c83cfc4..f1035fa 100644 --- a/src/ObjectiveC/ObjCObject.class.st +++ b/src/ObjectiveC/ObjCObject.class.st @@ -150,6 +150,7 @@ ObjCObject >> objCPerform: aSEL [ { #category : #accessing } ObjCObject >> release [ + 1halt. self objCPerform: #release asObjCSelector ] diff --git a/src/ObjectiveC/ObjCSpecParser.class.st b/src/ObjectiveC/ObjCSpecParser.class.st index 971f476..85a3677 100644 --- a/src/ObjectiveC/ObjCSpecParser.class.st +++ b/src/ObjectiveC/ObjCSpecParser.class.st @@ -53,8 +53,8 @@ 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: $B put: #FFIBool + at: $? put: #FFIExternalObject; + at: $B put: #FFIBool . ReversedMethodTypeMap := Dictionary new: 32. From b52995e893d0f43c6d29aa18ca42d4c7ac869a78 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Tue, 8 Aug 2023 14:31:48 +0200 Subject: [PATCH 3/8] Adding Notifications --- src/ObjectiveC-Cocoa/CocoaMenu.class.st | 2 +- src/ObjectiveC-Cocoa/CocoaMenuItem.class.st | 2 +- src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st | 2 +- .../CocoaNotificationCenter.class.st | 129 ++++++++++++++++++ .../CocoaNotificationCenterDelegate.class.st | 24 +++- ...UNNotificationPresentationOptions.class.st | 49 +++++++ src/ObjectiveC/ObjCObject.class.st | 2 +- 7 files changed, 200 insertions(+), 10 deletions(-) create mode 100644 src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st create mode 100644 src/ObjectiveC-Cocoa/UNNotificationPresentationOptions.class.st diff --git a/src/ObjectiveC-Cocoa/CocoaMenu.class.st b/src/ObjectiveC-Cocoa/CocoaMenu.class.st index b234f98..4a20c42 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenu.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenu.class.st @@ -11,7 +11,7 @@ Class { #classVars : [ 'MainMenu' ], - #category : #'ObjectiveC-Cocoa' + #category : #'ObjectiveC-Cocoa-Menus' } { #category : #adding } diff --git a/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st index 4e7a16e..dfbe42d 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st @@ -10,7 +10,7 @@ Class { 'nsShortcut', 'menuItem' ], - #category : #'ObjectiveC-Cocoa' + #category : #'ObjectiveC-Cocoa-Menus' } { #category : #accessing } diff --git a/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st index bde4237..80eb8bf 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st @@ -5,7 +5,7 @@ Class { 'proxy', 'block' ], - #category : #'ObjectiveC-Cocoa' + #category : #'ObjectiveC-Cocoa-Menus' } { #category : #accessing } diff --git a/src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st b/src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st new file mode 100644 index 0000000..409fe04 --- /dev/null +++ b/src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st @@ -0,0 +1,129 @@ +Class { + #name : #CocoaNotificationCenter, + #superclass : #Object, + #instVars : [ + 'delegate' + ], + #classVars : [ + 'UniqueInstance' + ], + #category : #'ObjectiveC-Cocoa-Notifications' +} + +{ #category : #examples } +CocoaNotificationCenter class >> example [ + + + + CocoaNotificationCenter uniqueInstance + showNotificationTitle: 'I am a notification' + body: 'with a nice body' +] + +{ #category : #accessing } +CocoaNotificationCenter class >> new [ + + ^ self error: 'Use uniqueInstance instead' +] + +{ #category : #accessing } +CocoaNotificationCenter class >> uniqueInstance [ + + UniqueInstance ifNil: [ UniqueInstance := self basicNew initialize; yourself ]. + ^ UniqueInstance + ensureDelegate; + yourself +] + +{ #category : #private } +CocoaNotificationCenter >> currentNotificationCenter [ + + ^ #UNUserNotificationCenter inObjC currentNotificationCenter +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenter >> 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: [ + | errorObject | + errorObject := ObjCObject fromHandle: error. + errorDescription := error + localizedDescription + UTF8String asByteArray + utf8Decoded ]. + sem signal ]. + self currentNotificationCenter + addNotificationRequest: request + withCompletionHandler: completionHandler. + + sem wait. + + errorDescription ifNotNil: [ self error: errorDescription ] +] + +{ #category : #private } +CocoaNotificationCenter >> ensureDelegate [ + + self currentNotificationCenter delegate isNull ifFalse: [ ^ self ]. + + delegate := ObjCProxyClass newFor: CocoaNotificationCenterDelegate new. + self currentNotificationCenter setDelegate: delegate +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenter >> 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: [ + | errorObject | + errorObject := ObjCObject fromHandle: error. + errorDescription := error localizedDescription UTF8String + asByteArray utf8Decoded ]. + sem signal ]. + + center := self currentNotificationCenter. + center + requestAuthorizationWithOptions: permissions + completionHandler: blk. + + sem wait. + + hasPermissions ifFalse: [ self error: errorDescription ] +] + +{ #category : #'as yet unclassified' } +CocoaNotificationCenter >> showNotificationTitle: aTitle body: aBody [ + + self requestPermissions. + + CocoaApplication withAutoreleasePoolDo: [ + self doShowNotificationTitle: aTitle body: aBody + ] +] diff --git a/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st b/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st index c326fad..2f9eb5f 100644 --- a/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st +++ b/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st @@ -4,9 +4,18 @@ Class { #instVars : [ 'proxy' ], - #category : #'ObjectiveC-Cocoa' + #category : #'ObjectiveC-Cocoa-Notifications' } +{ #category : #'as yet unclassified' } +CocoaNotificationCenterDelegate >> notificationPresentationOption [ + + ^ UNNotificationPresentationOptions UNNotificationPresentationOptionBadge value + | UNNotificationPresentationOptions UNNotificationPresentationOptionBanner value + | UNNotificationPresentationOptions UNNotificationPresentationOptionList value + | UNNotificationPresentationOptions UNNotificationPresentationOptionSound value +] + { #category : #accessing } CocoaNotificationCenterDelegate >> proxy [ @@ -24,8 +33,12 @@ CocoaNotificationCenterDelegate >> userNotificationCenter: center didReceiveNoti -1halt. - thisContext method selector traceCr. + "Just Delegating to the default action" + + | block | + + block := ObjCExternalBlock fromHandle: completionHandler signature: #(void #()). + block valueWithArguments: {}. ] { #category : #'as yet unclassified' } @@ -33,8 +46,7 @@ CocoaNotificationCenterDelegate >> userNotificationCenter: center openSettingsFo -1halt. - thisContext method selector traceCr. + "Our App does not provide configuration in App for the settings" ] { #category : #'as yet unclassified' } @@ -45,5 +57,5 @@ CocoaNotificationCenterDelegate >> userNotificationCenter: center willPresentNot | block | block := ObjCExternalBlock fromHandle: completionHandler signature: #(void #(uint)). - block valueWithArguments: { 7 }. + 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/ObjCObject.class.st b/src/ObjectiveC/ObjCObject.class.st index f1035fa..5e97095 100644 --- a/src/ObjectiveC/ObjCObject.class.st +++ b/src/ObjectiveC/ObjCObject.class.st @@ -150,7 +150,7 @@ ObjCObject >> objCPerform: aSEL [ { #category : #accessing } ObjCObject >> release [ - 1halt. + self objCPerform: #release asObjCSelector ] From 35f8029cd30b8eb2a9440bdd7a5d0cce55211023 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Tue, 8 Aug 2023 14:54:00 +0200 Subject: [PATCH 4/8] Renaming to have the same name than in the framework --- ...class.st => UNNotificationCenter.class.st} | 52 +++++++++++-------- ... => UNNotificationCenterDelegate.class.st} | 14 ++--- 2 files changed, 36 insertions(+), 30 deletions(-) rename src/ObjectiveC-Cocoa/{CocoaNotificationCenter.class.st => UNNotificationCenter.class.st} (66%) rename src/ObjectiveC-Cocoa/{CocoaNotificationCenterDelegate.class.st => UNNotificationCenterDelegate.class.st} (69%) diff --git a/src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st similarity index 66% rename from src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st rename to src/ObjectiveC-Cocoa/UNNotificationCenter.class.st index 409fe04..a5a99f3 100644 --- a/src/ObjectiveC-Cocoa/CocoaNotificationCenter.class.st +++ b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st @@ -1,5 +1,5 @@ Class { - #name : #CocoaNotificationCenter, + #name : #UNNotificationCenter, #superclass : #Object, #instVars : [ 'delegate' @@ -11,23 +11,29 @@ Class { } { #category : #examples } -CocoaNotificationCenter class >> example [ +UNNotificationCenter class >> example [ - CocoaNotificationCenter uniqueInstance + "To send notifications the application should be signed, for development we can use ad hoc signing with the command: + + codesign -dv -r- MyApp.app + + " + + UNNotificationCenter uniqueInstance showNotificationTitle: 'I am a notification' body: 'with a nice body' ] { #category : #accessing } -CocoaNotificationCenter class >> new [ +UNNotificationCenter class >> new [ ^ self error: 'Use uniqueInstance instead' ] { #category : #accessing } -CocoaNotificationCenter class >> uniqueInstance [ +UNNotificationCenter class >> uniqueInstance [ UniqueInstance ifNil: [ UniqueInstance := self basicNew initialize; yourself ]. ^ UniqueInstance @@ -36,13 +42,13 @@ CocoaNotificationCenter class >> uniqueInstance [ ] { #category : #private } -CocoaNotificationCenter >> currentNotificationCenter [ +UNNotificationCenter >> currentNotificationCenter [ ^ #UNUserNotificationCenter inObjC currentNotificationCenter ] -{ #category : #'as yet unclassified' } -CocoaNotificationCenter >> doShowNotificationTitle: aTitle body: aBody [ +{ #category : #private } +UNNotificationCenter >> doShowNotificationTitle: aTitle body: aBody [ | notificationContent identifier request sem completionHandler errorDescription | notificationContent := #UNMutableNotificationContent inObjC alloc @@ -64,14 +70,9 @@ CocoaNotificationCenter >> doShowNotificationTitle: aTitle body: aBody [ completionHandler := ObjCBlock signature: #( void #( ObjCObject error ) ) block: [ :error | - error isNull ifFalse: [ - | errorObject | - errorObject := ObjCObject fromHandle: error. - errorDescription := error - localizedDescription - UTF8String asByteArray - utf8Decoded ]. + error isNull ifFalse: [ errorDescription := self extractErrorDescription: error]. sem signal ]. + self currentNotificationCenter addNotificationRequest: request withCompletionHandler: completionHandler. @@ -82,16 +83,24 @@ CocoaNotificationCenter >> doShowNotificationTitle: aTitle body: aBody [ ] { #category : #private } -CocoaNotificationCenter >> ensureDelegate [ +UNNotificationCenter >> ensureDelegate [ self currentNotificationCenter delegate isNull ifFalse: [ ^ self ]. - delegate := ObjCProxyClass newFor: CocoaNotificationCenterDelegate new. + 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' } -CocoaNotificationCenter >> requestPermissions [ +UNNotificationCenter >> requestPermissions [ | sem hasPermissions blk center errorDescription permissions | permissions := 1 << 1 + (1 << 2). "Sound" "Alert" @@ -102,10 +111,7 @@ CocoaNotificationCenter >> requestPermissions [ block: [ :granted :error | hasPermissions := granted = 1. error isNull ifFalse: [ - | errorObject | - errorObject := ObjCObject fromHandle: error. - errorDescription := error localizedDescription UTF8String - asByteArray utf8Decoded ]. + errorDescription := self extractErrorDescription: error ]. sem signal ]. center := self currentNotificationCenter. @@ -119,7 +125,7 @@ CocoaNotificationCenter >> requestPermissions [ ] { #category : #'as yet unclassified' } -CocoaNotificationCenter >> showNotificationTitle: aTitle body: aBody [ +UNNotificationCenter >> showNotificationTitle: aTitle body: aBody [ self requestPermissions. diff --git a/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st b/src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st similarity index 69% rename from src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st rename to src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st index 2f9eb5f..e497f58 100644 --- a/src/ObjectiveC-Cocoa/CocoaNotificationCenterDelegate.class.st +++ b/src/ObjectiveC-Cocoa/UNNotificationCenterDelegate.class.st @@ -1,5 +1,5 @@ Class { - #name : #CocoaNotificationCenterDelegate, + #name : #UNNotificationCenterDelegate, #superclass : #Object, #instVars : [ 'proxy' @@ -8,7 +8,7 @@ Class { } { #category : #'as yet unclassified' } -CocoaNotificationCenterDelegate >> notificationPresentationOption [ +UNNotificationCenterDelegate >> notificationPresentationOption [ ^ UNNotificationPresentationOptions UNNotificationPresentationOptionBadge value | UNNotificationPresentationOptions UNNotificationPresentationOptionBanner value @@ -17,19 +17,19 @@ CocoaNotificationCenterDelegate >> notificationPresentationOption [ ] { #category : #accessing } -CocoaNotificationCenterDelegate >> proxy [ +UNNotificationCenterDelegate >> proxy [ ^ proxy ] { #category : #accessing } -CocoaNotificationCenterDelegate >> proxy: anObject [ +UNNotificationCenterDelegate >> proxy: anObject [ proxy := anObject ] { #category : #'as yet unclassified' } -CocoaNotificationCenterDelegate >> userNotificationCenter: center didReceiveNotificationResponse: response withCompletionHandler: completionHandler [ +UNNotificationCenterDelegate >> userNotificationCenter: center didReceiveNotificationResponse: response withCompletionHandler: completionHandler [ @@ -42,7 +42,7 @@ CocoaNotificationCenterDelegate >> userNotificationCenter: center didReceiveNoti ] { #category : #'as yet unclassified' } -CocoaNotificationCenterDelegate >> userNotificationCenter: center openSettingsForNotification: notification [ +UNNotificationCenterDelegate >> userNotificationCenter: center openSettingsForNotification: notification [ @@ -50,7 +50,7 @@ CocoaNotificationCenterDelegate >> userNotificationCenter: center openSettingsFo ] { #category : #'as yet unclassified' } -CocoaNotificationCenterDelegate >> userNotificationCenter: center willPresentNotification: notification withCompletionHandler: completionHandler [ +UNNotificationCenterDelegate >> userNotificationCenter: center willPresentNotification: notification withCompletionHandler: completionHandler [ From 7cc902e043b5d6e0a368ffe6e4af8a7a536e2cd6 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Wed, 9 Aug 2023 15:14:27 +0200 Subject: [PATCH 5/8] Fixing comment and isAvailable --- src/ObjectiveC-Cocoa/UNNotificationCenter.class.st | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st index a5a99f3..eefd7f7 100644 --- a/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st +++ b/src/ObjectiveC-Cocoa/UNNotificationCenter.class.st @@ -17,7 +17,8 @@ UNNotificationCenter class >> example [ "To send notifications the application should be signed, for development we can use ad hoc signing with the command: - codesign -dv -r- MyApp.app + To Sign: codesign --force --deep -s - MyApp.app + To Check: codesign -dv -r- MyApp.app " @@ -26,6 +27,12 @@ UNNotificationCenter class >> example [ body: 'with a nice body' ] +{ #category : #accessing } +UNNotificationCenter class >> isAvailable [ + + ^ #UNUserNotificationCenter inObjC isNull not +] + { #category : #accessing } UNNotificationCenter class >> new [ From 3585f4ad640aae34fbad05701fd80f7450a7c3e2 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Thu, 28 Mar 2024 14:16:12 +0100 Subject: [PATCH 6/8] Update BaselineOfObjCBridge.class.st --- src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st b/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st index 4ceda95..c16f6f0 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 pacakge: 'ObjectiveC-Cocoa']. ] From 10397923fc607f3c4bd114770b0802d2152fe9b1 Mon Sep 17 00:00:00 2001 From: Pablo Tesone Date: Thu, 28 Mar 2024 14:21:20 +0100 Subject: [PATCH 7/8] Update BaselineOfObjCBridge.class.st --- src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st b/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st index c16f6f0..3a883d7 100644 --- a/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st +++ b/src/BaselineOfObjCBridge/BaselineOfObjCBridge.class.st @@ -10,5 +10,5 @@ BaselineOfObjCBridge >> baseline: spec [ spec for: #common do: [ spec package: 'ObjectiveC'. - spec pacakge: 'ObjectiveC-Cocoa']. + spec package: 'ObjectiveC-Cocoa']. ] From f4a356090547a13e2d8295e0d39da666ff517a81 Mon Sep 17 00:00:00 2001 From: DEMAREY Christophe Date: Tue, 16 Apr 2024 14:50:21 +0200 Subject: [PATCH 8/8] add an API to easily build Mac Os menus --- src/ObjectiveC-Cocoa/CocoaMenu.class.st | 102 +++++++++---- src/ObjectiveC-Cocoa/CocoaMenuItem.class.st | 33 +++-- .../CocoaMenuSeparator.class.st | 17 +++ src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st | 5 +- .../CocoaServicesMenu.class.st | 42 ++++++ src/ObjectiveC-Cocoa/String.extension.st | 14 +- src/ObjectiveC/ObjCLibrary.class.st | 135 ++++++++++++++++++ 7 files changed, 307 insertions(+), 41 deletions(-) create mode 100644 src/ObjectiveC-Cocoa/CocoaMenuSeparator.class.st create mode 100644 src/ObjectiveC-Cocoa/CocoaServicesMenu.class.st diff --git a/src/ObjectiveC-Cocoa/CocoaMenu.class.st b/src/ObjectiveC-Cocoa/CocoaMenu.class.st index 4a20c42..b2256c6 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenu.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenu.class.st @@ -1,11 +1,15 @@ +" +I represent an item of a Cocoa Menu. +I can hold items or submenus. +" Class { #name : #CocoaMenu, #superclass : #Object, #instVars : [ 'nsMenu', + 'items', 'title', 'nsTitle', - 'items', 'menuItem' ], #classVars : [ @@ -14,15 +18,23 @@ Class { #category : #'ObjectiveC-Cocoa-Menus' } +{ #category : #'instance creation' } +CocoaMenu class >> with: aNSMenu [ + + ^ self new + setMenu: aNSMenu; + yourself +] + { #category : #adding } -CocoaMenu >> addItemWithTitle: aString action: aFullBlockClosure [ +CocoaMenu >> addItemWithTitle: aString action: aBlock [ - ^ self addItemWithTitle: aString action: aFullBlockClosure shortcut: '' + ^ self addItemWithTitle: aString action: aBlock shortcut: '' ] { #category : #adding } CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutString [ - + items add: (CocoaMenuItem new title: aTitle; action: actionBlock; @@ -31,60 +43,96 @@ CocoaMenu >> addItemWithTitle: aTitle action: actionBlock shortcut: shortcutStri ] { #category : #adding } -CocoaMenu >> addSubmenu: aTitle with: builderBlock [ - - | m | - m := self class new. - m title: aTitle. - builderBlock value: m. - items add: m. - ^ m +CocoaMenu >> addSeparator [ + + items add: CocoaMenuSeparator new ] { #category : #adding } -CocoaMenu >> addToMenu: aCocoaMenu [ +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. + menuItem := aCocoaMenu nsMenu + addItemWithTitle: nsTitle + action: ObjCObject nil + keyEquivalent: '' asNSString. aCocoaMenu nsMenu setSubmenu: nsMenu forItem: menuItem. ] -{ #category : #building } +{ #category : #configuring } +CocoaMenu >> beMainMenu [ + + self buildNSMenu. + #NSApplication inObjC sharedApplication setMainMenu: nsMenu. + MainMenu := self +] + +{ #category : #private } CocoaMenu >> buildNSMenu [ + - nsTitle := title asNSString. + nsTitle := (title ifNil: [ '' ]) asNSString. nsMenu := #NSMenu inObjC alloc initWithTitle: nsTitle. - - items do: [ :i | i addToMenu: self ]. - + items do: [ :item | item addToMenu: self ]. ^ nsMenu ] { #category : #initialization } CocoaMenu >> initialize [ - + super initialize. items := OrderedCollection new ] { #category : #accessing } -CocoaMenu >> nsMenu [ +CocoaMenu >> nsMenu [ + ^ nsMenu ] -{ #category : #initialization } -CocoaMenu >> setAsMainMenu [ +{ #category : #printing } +CocoaMenu >> printOn: aStream [ + aStream << self class name << '(' << (title ifNil: [ '' ]) << ')' +] + +{ #category : #removing } +CocoaMenu >> removeAllItems [ + + nsMenu removeAllItems. - self buildNSMenu. - #NSApplication inObjC sharedApplication setMainMenu: nsMenu. + self flag: 'TODO: release ObjC menu items'. + items := OrderedCollection new. + +] - MainMenu := self +{ #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 index dfbe42d..13f1165 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenuItem.class.st @@ -1,10 +1,14 @@ +" +I represent an item of a Cocoa menu. +I can hold menu items or submenus. +" Class { #name : #CocoaMenuItem, #superclass : #Object, #instVars : [ - 'title', 'action', 'shortcut', + 'title', 'target', 'nsTitle', 'nsShortcut', @@ -14,31 +18,40 @@ Class { } { #category : #accessing } -CocoaMenuItem >> action: aFullBlockClosure [ - action := aFullBlockClosure +CocoaMenuItem >> action: aBlock [ + + action := aBlock ] { #category : #adding } -CocoaMenuItem >> addToMenu: aCocoaMenu [ - - target := CocoaMenuTarget new block: action; yourself. - ObjCProxyClass newFor: target. +CocoaMenuItem >> addToMenu: aCocoaMenu [ + nsTitle := title asNSString. nsShortcut := shortcut asNSString. - - menuItem := aCocoaMenu nsMenu addItemWithTitle: nsTitle action: #execute asObjCSelector keyEquivalent: nsShortcut. + + 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. + 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 index 80eb8bf..09408ca 100644 --- a/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st +++ b/src/ObjectiveC-Cocoa/CocoaMenuTarget.class.st @@ -1,3 +1,6 @@ +" +I represent a Cocoa menu target, i.e. the ObjectiveC object that will receive the callbacks. +" Class { #name : #CocoaMenuTarget, #superclass : #Object, @@ -20,7 +23,7 @@ CocoaMenuTarget >> block: anObject [ block := anObject ] -{ #category : #accessing } +{ #category : #execution } CocoaMenuTarget >> execute [ 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/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/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' +]