github.com/shurcool/trayhost@v0.0.0-20181020202213-114974ef9e16/platform/darwin/tray.m (about) 1 #import <Cocoa/Cocoa.h> 2 3 NSMenu * appMenu; 4 char * clipboardString; 5 6 extern void tray_callback(int itemId); 7 extern BOOL tray_enabled(int itemId); 8 extern void notification_callback(); 9 10 @interface ManageHandler : NSObject<NSUserNotificationCenterDelegate> 11 - (void)manage:(id)sender; 12 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem; 13 - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification; 14 - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; 15 - (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification; 16 @end 17 18 ManageHandler * uncDelegate; 19 20 @implementation ManageHandler 21 - (void)manage:(id)sender { 22 tray_callback([[sender representedObject] intValue]); 23 } 24 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { 25 //NSLog(@"in tray.m validateMenuItem\n"); 26 return tray_enabled([[menuItem representedObject] intValue]); 27 } 28 - (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification { 29 NSLog(@"in tray.m shouldPresentNotification\n"); 30 return YES; 31 } 32 - (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { 33 NSLog(@"in tray.m didActivateNotification\n"); 34 [center removeDeliveredNotification: notification]; 35 36 // Call the handler for notification activation. 37 int notificationId = [[[notification userInfo] objectForKey:@"notificationId"] intValue]; 38 notification_callback(notificationId); 39 } 40 - (void)userNotificationCenter:(NSUserNotificationCenter *)center didDeliverNotification:(NSUserNotification *)notification { 41 NSLog(@"in tray.m didDeliverNotification\n"); 42 //[center removeDeliveredNotification: notification]; 43 44 NSTimeInterval timeout = [[[notification userInfo] objectForKey:@"timeout"] doubleValue]; 45 if (timeout > 0.0) { 46 NSLog(@"starting timer (timeout = %f) for %p\n", timeout, notification); 47 [NSTimer scheduledTimerWithTimeInterval:timeout 48 target:uncDelegate 49 selector:@selector(clearNotificationTimer:) 50 userInfo:notification 51 repeats:NO]; 52 } 53 } 54 - (void)clearNotificationTimer:(NSTimer *)timer { 55 NSUserNotification * notification = [timer userInfo]; 56 NSLog(@"in clearNotificationTimer %p\n", notification); 57 [[NSUserNotificationCenter defaultUserNotificationCenter] removeDeliveredNotification: notification]; 58 } 59 @end 60 61 void add_menu_item(int itemId, const char * title, int disabled) { 62 NSString * manageTitle = [NSString stringWithUTF8String:title]; 63 NSMenuItem * menuItem = [[[NSMenuItem alloc] initWithTitle:manageTitle 64 action:@selector(manage:) keyEquivalent:@""] 65 autorelease]; 66 67 [menuItem setRepresentedObject:[NSNumber numberWithInt:itemId]]; 68 [menuItem setTarget:uncDelegate]; 69 [appMenu addItem:menuItem]; 70 } 71 72 void clear_menu_items() { 73 [appMenu removeAllItems]; 74 } 75 76 void add_separator_item() { 77 [appMenu addItem:[NSMenuItem separatorItem]]; 78 } 79 80 void native_loop() { 81 [NSApp run]; 82 } 83 84 void exit_loop() { 85 // Clear all notifications. 86 [[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications]; 87 88 [NSApp stop:nil]; 89 } 90 91 int init(const char * title, struct image img) { 92 [NSAutoreleasePool new]; 93 94 [NSApplication sharedApplication]; 95 96 // This is needed to avoid having a dock icon (and entry in Cmd+Tab list). 97 // [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; 98 // However, it causes the tooltip to not appear. So LSUIElement should be used instead. 99 100 appMenu = [[NSMenu new] autorelease]; 101 102 // Set self as NSUserNotificationCenter delegate. 103 uncDelegate = [[ManageHandler alloc] init]; 104 NSLog(@"[NSUserNotificationCenter defaultUserNotificationCenter] -> %p", [NSUserNotificationCenter defaultUserNotificationCenter]); 105 [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: uncDelegate]; 106 107 // If we were opened from a user notification, do the corresponding action. 108 /*{ 109 NSUserNotification * launchNotification = [[aNotification userInfo] objectForKey: NSApplicationLaunchUserNotificationKey]; 110 if (launchNotification) 111 [self userNotificationCenter: nil didActivateNotification: launchNotification]; 112 }*/ 113 114 NSSize iconSize = NSMakeSize(16, 16); 115 NSImage * icon = [[NSImage alloc] initWithSize:iconSize]; 116 NSData * iconData = [NSData dataWithBytes:img.bytes length:img.length]; 117 [icon addRepresentation:[NSBitmapImageRep imageRepWithData:iconData]]; 118 [icon setTemplate:YES]; 119 120 NSStatusItem * statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; 121 [statusItem setMenu:appMenu]; 122 [statusItem setImage:icon]; 123 [statusItem setHighlightMode:YES]; 124 [statusItem setToolTip:[NSString stringWithUTF8String:title]]; 125 126 return 0; 127 } 128 129 void set_clipboard_string(const char * string) { 130 NSArray * types = [NSArray arrayWithObjects:NSPasteboardTypeString, nil]; 131 132 NSPasteboard * pasteboard = [NSPasteboard generalPasteboard]; 133 [pasteboard declareTypes:types owner:nil]; 134 [pasteboard setString:[NSString stringWithUTF8String:string] 135 forType:NSPasteboardTypeString]; 136 } 137 138 struct clipboard_content get_clipboard_content() { 139 struct clipboard_content cc; 140 cc.text = NULL; 141 cc.image.kind = NULL; 142 cc.image.bytes = NULL; 143 cc.image.length = 0; 144 cc.files.names = NULL; 145 cc.files.count = 0; 146 147 NSPasteboard * pasteboard = [NSPasteboard generalPasteboard]; 148 NSData * object = NULL; 149 NSString * string = NULL; 150 NSArray * filenames = NULL; 151 152 if ([[pasteboard types] containsObject:NSFilenamesPboardType] && 153 (filenames = [pasteboard propertyListForType:NSFilenamesPboardType]) != NULL) { 154 155 const int count = [filenames count]; 156 if (count) { 157 NSEnumerator * e = [filenames objectEnumerator]; 158 char ** names = calloc(count, sizeof(char *)); 159 for (int i = 0; i < count; i++) { 160 names[i] = strdup([[e nextObject] UTF8String]); 161 } 162 163 cc.files.names = (const char **)(names); 164 cc.files.count = count; 165 166 // TODO: Fix memory leak. 167 /*for (i = 0; i < count; i++) 168 free(names[i]); 169 free(names);*/ 170 } 171 } else if ([[pasteboard types] containsObject:NSPasteboardTypePNG] && 172 (object = [pasteboard dataForType:NSPasteboardTypePNG]) != NULL) { 173 174 cc.image.kind = "png"; 175 cc.image.bytes = [object bytes]; 176 cc.image.length = [object length]; 177 } else if ([[pasteboard types] containsObject:NSPasteboardTypeTIFF] && 178 (object = [pasteboard dataForType:NSPasteboardTypeTIFF]) != NULL) { 179 180 cc.image.kind = "tiff"; 181 cc.image.bytes = [object bytes]; 182 cc.image.length = [object length]; 183 } else if ([[pasteboard types] containsObject:NSPasteboardTypeString] && 184 (string = [pasteboard stringForType:NSPasteboardTypeString]) != NULL) { 185 186 free(clipboardString); // Free previous string (noop first time this is called since the value is NULL). 187 clipboardString = strdup([string UTF8String]); 188 189 cc.text = clipboardString; 190 } 191 192 return cc; 193 } 194 195 void display_notification(int notificationId, const char * title, const char * body, struct image img, double timeout) { 196 NSUserNotification * notification = [[NSUserNotification alloc] init]; 197 [notification setTitle: [NSString stringWithUTF8String:title]]; 198 [notification setInformativeText: [NSString stringWithUTF8String:body]]; 199 [notification setSoundName: NSUserNotificationDefaultSoundName]; 200 201 if (img.kind != NULL) { 202 NSImage * image = [[NSImage alloc] initWithData:[NSData dataWithBytes:img.bytes length:img.length]]; 203 [notification setContentImage: image]; 204 } 205 206 NSDictionary * dictionary = [NSDictionary dictionaryWithObjectsAndKeys: 207 [NSNumber numberWithDouble:timeout], @"timeout", 208 [NSNumber numberWithInt:notificationId], @"notificationId", 209 nil]; 210 [notification setUserInfo: dictionary]; 211 212 NSUserNotificationCenter * center = [NSUserNotificationCenter defaultUserNotificationCenter]; 213 [center deliverNotification: notification]; 214 //[center removeDeliveredNotification: notification]; 215 }