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  }