Closed dscso closed 3 weeks ago
port the mac-notificaiton-sys to objc2
I think there's a PR up for that already, btw: https://github.com/h4llow3En/mac-notification-sys/pull/51
I try to stick with the deprecated API
Makes sense, though I think that might be your issue? I haven't used NSUserNotification myself, but from my testing it seems like the existing mac-notification-sys
doesn't work either, and haven't for a while.
Maybe it's the __bundleIdentifier
hack that's broken? Have you tried running it inside an application bundle?
When running my creation I sometimes get a segfault
Hmm, that sounds problematic! Could you hook up a debugger and see where the crash occurs? I tried running it myself a few times, including using AddressSanitizer, and couldn't reproduce the crash.
I think there's a PR up for that already, btw: https://github.com/h4llow3En/mac-notification-sys/pull/51
Yes but the PR uses the User Notification Framework, so I want to get it running in the old deprecated way.
from my testing it seems like the existing mac-notification-sys doesn't work either, and haven't https://github.com/h4llow3En/mac-notification-sys/issues/33.
I got it working on my M1 Macbook running on MacOS 14.2.1 so it seems to still be working. Did it send notifications for you when you tried my code? Because it should send a notification and crash (sometimes/immediately after)
Maybe it's the __bundleIdentifier hack that's broken? Have you tried running it inside an application bundle?
Maybe I am doing something wrong here, but if I declare the delegate in the .m file, there are no problems, and I get my notification interactions. The problem starts if I go pure rust on the delegate and try to write the NSUserNotificationCenterDelegate
in pure Rust.
If you want to try, I created a branch: objc-delegate
The thing that is troubling me is if I add this to the notification.m file, it works perfectly. I would like to not use some dirty callback solution. Also my previous solution resulted in a lot of memory leaks.
@interface NotificationCenterDelegate: NSObject <NSUserNotificationCenterDelegate>
@end
@implementation NotificationCenterDelegate
// Implement the delegate method to handle the user's response to the notification
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
NSLog(@"Notification clicked");
[center removeDeliveredNotification:notification];
}
@end
and add this:
NotificationCenterDelegate *delegate = [[NotificationCenterDelegate alloc] init];
NSUserNotificationCenter* center = [NSUserNotificationCenter defaultUserNotificationCenter];
center.delegate = delegate;
Regarding the debugger: It seems to always fail in a different spot in a native library. maybe you know a better way to get more debug info, that might be helpful :).
$ lldb target/debug/examples/send
(lldb) target create "target/debug/examples/send"
Current executable set to '/Users/me/code/rust/notifications/target/debug/examples/send' (arm64).
(lldb) run
Process 54189 launched: '/Users/me/code/rust/notifications/target/debug/examples/send' (arm64)
aProcess 54189 exited with status = 0 (0x00000000)
(lldb) run
Process 54198 launched: '/Users/me/code/rust/notifications/target/debug/examples/send' (arm64)
Process 54198 stopped
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1e)
frame #0: 0x000000018cb648d8 libobjc.A.dylib`objc_opt_respondsToSelector + 52
libobjc.A.dylib`objc_opt_respondsToSelector:
-> 0x18cb648d8 <+52>: ldrsh w8, [x16, #0x1e]
0x18cb648dc <+56>: tbz w8, #0x1f, 0x18cb6496c ; <+200>
0x18cb648e0 <+60>: mov x1, x2
0x18cb648e4 <+64>: mov x2, x16
Target 0: (send) stopped.
(lldb) run
There is a running process, kill it and restart?: [Y/n] y
Process 54198 exited with status = 9 (0x00000009) killed
Process 54212 launched: '/Users/me/code/rust/notifications/target/debug/examples/send' (arm64)
Process 54212 stopped
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1f48aa0003f0)
frame #0: 0x000000018cb36ed8 libobjc.A.dylib`class_respondsToSelector_inst + 64
libobjc.A.dylib`class_respondsToSelector_inst:
-> 0x18cb36ed8 <+64>: ldr w9, [x9]
0x18cb36edc <+68>: tbnz w9, #0x0, 0x18cb36ef8 ; <+96>
0x18cb36ee0 <+72>: ldr x9, [x19]
0x18cb36ee4 <+76>: and x9, x9, #0x7ffffffffff8
Target 0: (send) stopped.
(lldb) run
There is a running process, kill it and restart?: [Y/n] y
Process 54212 exited with status = 9 (0x00000009) killed
Process 54226 launched: '/Users/me/code/rust/notifications/target/debug/examples/send' (arm64)
Process 54226 stopped
* thread #1, name = 'main', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x1e)
frame #0: 0x000000018cb648d8 libobjc.A.dylib`objc_opt_respondsToSelector + 52
libobjc.A.dylib`objc_opt_respondsToSelector:
-> 0x18cb648d8 <+52>: ldrsh w8, [x16, #0x1e]
0x18cb648dc <+56>: tbz w8, #0x1f, 0x18cb6496c ; <+200>
0x18cb648e0 <+60>: mov x1, x2
0x18cb648e4 <+64>: mov x2, x16
Target 0: (send) stopped.
(lldb)
After hours and hours I found the solution, and it was not due to this crate: I thought that if you pass a reference, it would retain delegate, as there is one ref existent. Of course this is not the case, and delegate gets dropped as soon as the scope ends.
notification_center.setDelegate(Some(ProtocolObject::from_ref(delegate.as_ref())));
I found a workaround using a static variable like so:
unsafe fn get_delegate() -> &'static Id<RustNotificationDelegate> {
static mut DELEGATE: MaybeUninit<Id<RustNotificationDelegate>> = MaybeUninit::uninit();
static ONCE: Once = Once::new();
ONCE.call_once(|| {
DELEGATE.write(RustNotificationDelegate::new());
});
DELEGATE.assume_init_ref()
}
fn init() {
//...
let notification_center = NSUserNotificationCenter::defaultUserNotificationCenter();
let delegate = get_delegate();
notification_center.setDelegate(Some(ProtocolObject::from_ref(delegate.as_ref())));
}
Thank you very much for your support and the library, as already said: it makes my live much easier!
if you pass a reference, it would retain delegate, as there is one ref existent
Yeah, delegates are usually only weakly retained, that's not something that objc2
has control over.
An alternative to the static would be to use ManuallyDrop
, and just leak the Id<RustNotificationDelegate>
.
Glad you figured it out though!
I am trying to port the mac-notificaiton-sys to objc2, to be able to respond on multiple notifications at the same time while not blocking the whole thread (as
mac-notification-sys
does)I have some problems declaring the delegate for
NSUserNotificationCenter
see in detail here. It seems to declare the class, but when interacting with a notification, callback does not get called. I have tried quite a lot, and tried to get inspired by multiple different sources (winit, etc.) but I can't figure it out :(.When running my creation I sometimes get a segfault, sometimes nothing and one time I got this:
Might there be a problem with the
NSUserNotificationCenterDelegate
implementation since it is deprecated? I would also use the User Notifications framework, but it requires signing the binaries[1]. Therefore, I try to stick with the deprecated API, since Electron and many other are still using it.If you want to try it just download it and run
btw thanks for this crate, it makes development with objc much easier (i've tried a lot now :D).