| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- #import <Cocoa/Cocoa.h>
- #include "systray.h"
- #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101400
- #ifndef NSControlStateValueOff
- #define NSControlStateValueOff NSOffState
- #endif
- #ifndef NSControlStateValueOn
- #define NSControlStateValueOn NSOnState
- #endif
- #endif
- @interface MenuItem : NSObject
- {
- @public
- NSNumber* menuId;
- NSNumber* parentMenuId;
- NSString* title;
- NSString* tooltip;
- short disabled;
- short checked;
- }
- -(id) initWithId: (int)theMenuId
- withParentMenuId: (int)theParentMenuId
- withTitle: (const char*)theTitle
- withTooltip: (const char*)theTooltip
- withDisabled: (short)theDisabled
- withChecked: (short)theChecked;
- @end
- @implementation MenuItem
- -(id) initWithId: (int)theMenuId
- withParentMenuId: (int)theParentMenuId
- withTitle: (const char*)theTitle
- withTooltip: (const char*)theTooltip
- withDisabled: (short)theDisabled
- withChecked: (short)theChecked
- {
- menuId = [NSNumber numberWithInt:theMenuId];
- parentMenuId = [NSNumber numberWithInt:theParentMenuId];
- title = [[NSString alloc] initWithCString:theTitle
- encoding:NSUTF8StringEncoding];
- tooltip = [[NSString alloc] initWithCString:theTooltip
- encoding:NSUTF8StringEncoding];
- disabled = theDisabled;
- checked = theChecked;
- return self;
- }
- @end
- @interface AppDelegate: NSObject <NSApplicationDelegate>
- - (void) add_or_update_menu_item:(MenuItem*) item;
- - (IBAction)menuHandler:(id)sender;
- @property (assign) IBOutlet NSWindow *window;
- @end
- @implementation AppDelegate
- {
- NSStatusItem *statusItem;
- NSMenu *menu;
- NSCondition* cond;
- }
- @synthesize window = _window;
- - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
- {
- self->statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
- self->menu = [[NSMenu alloc] init];
- [self->menu setAutoenablesItems: FALSE];
- [self->statusItem setMenu:self->menu];
- systray_ready();
- }
- - (void)applicationWillTerminate:(NSNotification *)aNotification
- {
- systray_on_exit();
- }
- - (void)setIcon:(NSImage *)image {
- statusItem.button.image = image;
- [self updateTitleButtonStyle];
- }
- - (void)setTitle:(NSString *)title {
- statusItem.button.title = title;
- [self updateTitleButtonStyle];
- }
- -(void)updateTitleButtonStyle {
- if (statusItem.button.image != nil) {
- if ([statusItem.button.title length] == 0) {
- statusItem.button.imagePosition = NSImageOnly;
- } else {
- statusItem.button.imagePosition = NSImageLeft;
- }
- } else {
- statusItem.button.imagePosition = NSNoImage;
- }
- }
- - (void)setTooltip:(NSString *)tooltip {
- statusItem.button.toolTip = tooltip;
- }
- - (IBAction)menuHandler:(id)sender
- {
- NSNumber* menuId = [sender representedObject];
- systray_menu_item_selected(menuId.intValue);
- }
- - (void)add_or_update_menu_item:(MenuItem *)item {
- NSMenu *theMenu = self->menu;
- NSMenuItem *parentItem;
- if ([item->parentMenuId integerValue] > 0) {
- parentItem = find_menu_item(menu, item->parentMenuId);
- if (parentItem.hasSubmenu) {
- theMenu = parentItem.submenu;
- } else {
- theMenu = [[NSMenu alloc] init];
- [theMenu setAutoenablesItems:NO];
- [parentItem setSubmenu:theMenu];
- }
- }
-
- NSMenuItem *menuItem;
- menuItem = find_menu_item(theMenu, item->menuId);
- if (menuItem == NULL) {
- menuItem = [theMenu addItemWithTitle:item->title
- action:@selector(menuHandler:)
- keyEquivalent:@""];
- [menuItem setRepresentedObject:item->menuId];
- }
- [menuItem setTitle:item->title];
- [menuItem setTag:[item->menuId integerValue]];
- [menuItem setTarget:self];
- [menuItem setToolTip:item->tooltip];
- if (item->disabled == 1) {
- menuItem.enabled = FALSE;
- } else {
- menuItem.enabled = TRUE;
- }
- if (item->checked == 1) {
- menuItem.state = NSControlStateValueOn;
- } else {
- menuItem.state = NSControlStateValueOff;
- }
- }
- NSMenuItem *find_menu_item(NSMenu *ourMenu, NSNumber *menuId) {
- NSMenuItem *foundItem = [ourMenu itemWithTag:[menuId integerValue]];
- if (foundItem != NULL) {
- return foundItem;
- }
- NSArray *menu_items = ourMenu.itemArray;
- int i;
- for (i = 0; i < [menu_items count]; i++) {
- NSMenuItem *i_item = [menu_items objectAtIndex:i];
- if (i_item.hasSubmenu) {
- foundItem = find_menu_item(i_item.submenu, menuId);
- if (foundItem != NULL) {
- return foundItem;
- }
- }
- }
- return NULL;
- };
- - (void) add_separator:(NSNumber*) parentMenuId
- {
- if (parentMenuId.integerValue != 0) {
- NSMenuItem* menuItem = find_menu_item(menu, parentMenuId);
- if (menuItem != NULL) {
- [menuItem.submenu addItem: [NSMenuItem separatorItem]];
- return;
- }
- }
- [menu addItem: [NSMenuItem separatorItem]];
- }
- - (void) hide_menu_item:(NSNumber*) menuId
- {
- NSMenuItem* menuItem = find_menu_item(menu, menuId);
- if (menuItem != NULL) {
- [menuItem setHidden:TRUE];
- }
- }
- - (void) setMenuItemIcon:(NSArray*)imageAndMenuId {
- NSImage* image = [imageAndMenuId objectAtIndex:0];
- NSNumber* menuId = [imageAndMenuId objectAtIndex:1];
- NSMenuItem* menuItem;
- menuItem = find_menu_item(menu, menuId);
- if (menuItem == NULL) {
- return;
- }
- menuItem.image = image;
- }
- - (void) show_menu_item:(NSNumber*) menuId
- {
- NSMenuItem* menuItem = find_menu_item(menu, menuId);
- if (menuItem != NULL) {
- [menuItem setHidden:FALSE];
- }
- }
- - (void) remove_menu_item:(NSNumber*) menuId
- {
- NSMenuItem* menuItem = find_menu_item(menu, menuId);
- if (menuItem != NULL) {
- [menuItem.menu removeItem:menuItem];
- }
- }
- - (void) reset_menu
- {
- [self->menu removeAllItems];
- }
- - (void) quit
- {
- [NSApp terminate:self];
- }
- @end
- bool internalLoop = false;
- AppDelegate *owner;
- void setInternalLoop(bool i) {
- internalLoop = i;
- }
- void registerSystray(void) {
- if (!internalLoop) { // with an external loop we don't take ownership of the app
- return;
- }
- owner = [[AppDelegate alloc] init];
- [[NSApplication sharedApplication] setDelegate:owner];
- // A workaround to avoid crashing on macOS versions before Catalina. Somehow
- // SIGSEGV would happen inside AppKit if [NSApp run] is called from a
- // different function, even if that function is called right after this.
- if (floor(NSAppKitVersionNumber) <= /*NSAppKitVersionNumber10_14*/ 1671){
- [NSApp run];
- }
- }
- void nativeEnd(void) {
- systray_on_exit();
- }
- int nativeLoop(void) {
- if (floor(NSAppKitVersionNumber) > /*NSAppKitVersionNumber10_14*/ 1671){
- [NSApp run];
- }
- return EXIT_SUCCESS;
- }
- void nativeStart(void) {
- owner = [[AppDelegate alloc] init];
- NSNotification *launched = [NSNotification notificationWithName:NSApplicationDidFinishLaunchingNotification
- object:[NSApplication sharedApplication]];
- [owner applicationDidFinishLaunching:launched];
- }
- void runInMainThread(SEL method, id object) {
- [owner
- performSelectorOnMainThread:method
- withObject:object
- waitUntilDone: YES];
- }
- void setIcon(const char* iconBytes, int length, bool template) {
- NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
- @autoreleasepool {
- NSImage *image = [[NSImage alloc] initWithData:buffer];
- [image setSize:NSMakeSize(16, 16)];
- image.template = template;
- runInMainThread(@selector(setIcon:), (id)image);
- }
- }
- void setMenuItemIcon(const char* iconBytes, int length, int menuId, bool template) {
- NSData* buffer = [NSData dataWithBytes: iconBytes length:length];
- @autoreleasepool {
- NSImage *image = [[NSImage alloc] initWithData:buffer];
- [image setSize:NSMakeSize(16, 16)];
- image.template = template;
- NSNumber *mId = [NSNumber numberWithInt:menuId];
- runInMainThread(@selector(setMenuItemIcon:), @[image, (id)mId]);
- }
- }
- void setTitle(char* ctitle) {
- NSString* title = [[NSString alloc] initWithCString:ctitle
- encoding:NSUTF8StringEncoding];
- free(ctitle);
- runInMainThread(@selector(setTitle:), (id)title);
- }
- void setTooltip(char* ctooltip) {
- NSString* tooltip = [[NSString alloc] initWithCString:ctooltip
- encoding:NSUTF8StringEncoding];
- free(ctooltip);
- runInMainThread(@selector(setTooltip:), (id)tooltip);
- }
- void add_or_update_menu_item(int menuId, int parentMenuId, char* title, char* tooltip, short disabled, short checked, short isCheckable) {
- MenuItem* item = [[MenuItem alloc] initWithId: menuId withParentMenuId: parentMenuId withTitle: title withTooltip: tooltip withDisabled: disabled withChecked: checked];
- free(title);
- free(tooltip);
- runInMainThread(@selector(add_or_update_menu_item:), (id)item);
- }
- void add_separator(int menuId, int parentId) {
- NSNumber *pId = [NSNumber numberWithInt:parentId];
- runInMainThread(@selector(add_separator:), (id)pId);
- }
- void hide_menu_item(int menuId) {
- NSNumber *mId = [NSNumber numberWithInt:menuId];
- runInMainThread(@selector(hide_menu_item:), (id)mId);
- }
- void remove_menu_item(int menuId) {
- NSNumber *mId = [NSNumber numberWithInt:menuId];
- runInMainThread(@selector(remove_menu_item:), (id)mId);
- }
- void show_menu_item(int menuId) {
- NSNumber *mId = [NSNumber numberWithInt:menuId];
- runInMainThread(@selector(show_menu_item:), (id)mId);
- }
- void reset_menu() {
- runInMainThread(@selector(reset_menu), nil);
- }
- void quit() {
- runInMainThread(@selector(quit), nil);
- }
|