macOS version in obj-c runtime
This commit is contained in:
156
tray.h
156
tray.h
@@ -90,92 +90,118 @@ static void tray_exit() { loop_result = -1; }
|
||||
|
||||
#elif defined(TRAY_APPKIT)
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <objc/objc-runtime.h>
|
||||
#include <limits.h>
|
||||
|
||||
static NSAutoreleasePool *pool;
|
||||
static NSStatusBar *statusBar;
|
||||
static id app;
|
||||
static id pool;
|
||||
static id statusBar;
|
||||
static id statusItem;
|
||||
static id statusBarButton;
|
||||
|
||||
@interface Tray : NSObject <NSApplicationDelegate>
|
||||
- (void)menuCallback:(id)sender;
|
||||
@end
|
||||
@implementation Tray
|
||||
- (void)menuCallback:(id)sender {
|
||||
struct tray_menu *m =
|
||||
(struct tray_menu *)[[sender representedObject] pointerValue];
|
||||
if (m != NULL && m->cb != NULL) {
|
||||
m->cb(m);
|
||||
}
|
||||
}
|
||||
@end
|
||||
static id _tray_menu(struct tray_menu *m) {
|
||||
id menu = objc_msgSend((id)objc_getClass("NSMenu"), sel_registerName("new"));
|
||||
objc_msgSend(menu, sel_registerName("autorelease"));
|
||||
objc_msgSend(menu, sel_registerName("setAutoenablesItems:"), false);
|
||||
|
||||
static NSMenu *_tray_menu(struct tray_menu *m) {
|
||||
NSMenu *menu = [NSMenu new];
|
||||
[menu autorelease];
|
||||
[menu setAutoenablesItems:NO];
|
||||
for (; m != NULL && m->text != NULL; m++) {
|
||||
if (strcmp(m->text, "-") == 0) {
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
} else {
|
||||
NSMenuItem *menuItem = [NSMenuItem alloc];
|
||||
[menuItem autorelease];
|
||||
[menuItem initWithTitle:[NSString stringWithUTF8String:m->text]
|
||||
action:@selector(menuCallback:)
|
||||
keyEquivalent:@""];
|
||||
[menuItem setEnabled:(m->disabled ? NO : YES)];
|
||||
[menuItem setState:(m->checked ? NSOnState : NSOffState)];
|
||||
[menuItem setRepresentedObject:[NSValue valueWithPointer:m]];
|
||||
|
||||
[menu addItem:menuItem];
|
||||
|
||||
if (m->submenu != NULL) {
|
||||
[menu setSubmenu:_tray_menu(m->submenu) forItem:menuItem];
|
||||
for (; m != NULL && m->text != NULL; m++) {
|
||||
if (strcmp(m->text, "-") == 0) {
|
||||
objc_msgSend(menu, sel_registerName("addItem:"),
|
||||
objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("separatorItem")));
|
||||
} else {
|
||||
id menuItem = objc_msgSend((id)objc_getClass("NSMenuItem"), sel_registerName("alloc"));
|
||||
objc_msgSend(menuItem, sel_registerName("autorelease"));
|
||||
objc_msgSend(menuItem, sel_registerName("initWithTitle:action:keyEquivalent:"),
|
||||
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), m->text),
|
||||
sel_registerName("menuCallback:"),
|
||||
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), ""));
|
||||
|
||||
objc_msgSend(menuItem, sel_registerName("setEnabled:"), (m->disabled ? false : true));
|
||||
objc_msgSend(menuItem, sel_registerName("setState:"), (m->checked ? 1 : 0));
|
||||
objc_msgSend(menuItem, sel_registerName("setRepresentedObject:"),
|
||||
objc_msgSend((id)objc_getClass("NSValue"), sel_registerName("valueWithPointer:"), m));
|
||||
|
||||
objc_msgSend(menu, sel_registerName("addItem:"), menuItem);
|
||||
|
||||
if (m->submenu != NULL) {
|
||||
objc_msgSend(menu, sel_registerName("setSubmenu:forItem:"), _tray_menu(m->submenu), menuItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
static void menu_callback(id self, SEL cmd, id sender) {
|
||||
struct tray_menu *m =
|
||||
(struct tray_menu *)objc_msgSend(objc_msgSend(sender, sel_registerName("representedObject")),
|
||||
sel_registerName("pointerValue"));
|
||||
|
||||
if (m != NULL && m->cb != NULL) {
|
||||
m->cb(m);
|
||||
}
|
||||
}
|
||||
|
||||
static int tray_init(struct tray *tray) {
|
||||
pool = [NSAutoreleasePool new];
|
||||
[NSApplication sharedApplication];
|
||||
|
||||
Tray *trayDelegate = [Tray new];
|
||||
[NSApp setDelegate:trayDelegate];
|
||||
|
||||
statusBar = [NSStatusBar systemStatusBar];
|
||||
statusItem = [statusBar statusItemWithLength:NSVariableStatusItemLength];
|
||||
[statusItem retain];
|
||||
[statusItem setHighlightMode:YES];
|
||||
statusBarButton = [statusItem button];
|
||||
|
||||
tray_update(tray);
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
return 0;
|
||||
pool = objc_msgSend((id)objc_getClass("NSAutoreleasePool"),
|
||||
sel_registerName("new"));
|
||||
|
||||
objc_msgSend((id)objc_getClass("NSApplication"),
|
||||
sel_registerName("sharedApplication"));
|
||||
|
||||
Class trayDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "Tray", 0);
|
||||
class_addProtocol(trayDelegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||
class_addMethod(trayDelegateClass, sel_registerName("menuCallback:"), (IMP)menu_callback, "v@:@");
|
||||
objc_registerClassPair(trayDelegateClass);
|
||||
|
||||
id trayDelegate = objc_msgSend((id)trayDelegateClass,
|
||||
sel_registerName("new"));
|
||||
|
||||
app = objc_msgSend((id)objc_getClass("NSApplication"),
|
||||
sel_registerName("sharedApplication"));
|
||||
|
||||
objc_msgSend(app, sel_registerName("setDelegate:"), trayDelegate);
|
||||
|
||||
statusBar = objc_msgSend((id)objc_getClass("NSStatusBar"),
|
||||
sel_registerName("systemStatusBar"));
|
||||
|
||||
statusItem = objc_msgSend(statusBar, sel_registerName("statusItemWithLength:"), -1.0);
|
||||
|
||||
objc_msgSend(statusItem, sel_registerName("retain"));
|
||||
objc_msgSend(statusItem, sel_registerName("setHighlightMode:"), true);
|
||||
statusBarButton = objc_msgSend(statusItem, sel_registerName("button"));
|
||||
tray_update(tray);
|
||||
objc_msgSend(app, sel_registerName("activateIgnoringOtherApps:"), true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tray_loop(int blocking) {
|
||||
NSEvent *event;
|
||||
NSDate *until = (blocking ? [NSDate distantFuture] : [NSDate distantPast]);
|
||||
event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
||||
untilDate:until
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (event) {
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
return 0;
|
||||
id until = (blocking ?
|
||||
objc_msgSend((id)objc_getClass("NSDate"), sel_registerName("distantFuture")) :
|
||||
objc_msgSend((id)objc_getClass("NSDate"), sel_registerName("distantPast")));
|
||||
|
||||
id event = objc_msgSend(app, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"),
|
||||
ULONG_MAX,
|
||||
until,
|
||||
objc_msgSend((id)objc_getClass("NSString"),
|
||||
sel_registerName("stringWithUTF8String:"),
|
||||
"kCFRunLoopDefaultMode"),
|
||||
true);
|
||||
if (event) {
|
||||
objc_msgSend(app, sel_registerName("sendEvent:"), event);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tray_update(struct tray *tray) {
|
||||
[statusBarButton
|
||||
setImage:[NSImage imageNamed:[NSString stringWithUTF8String:tray->icon]]];
|
||||
objc_msgSend(statusBarButton, sel_registerName("setImage:"),
|
||||
objc_msgSend((id)objc_getClass("NSImage"), sel_registerName("imageNamed:"),
|
||||
objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), tray->icon)));
|
||||
|
||||
[statusItem setMenu:_tray_menu(tray->menu)];
|
||||
objc_msgSend(statusItem, sel_registerName("setMenu:"), _tray_menu(tray->menu));
|
||||
}
|
||||
|
||||
static void tray_exit() { [NSApp terminate:NSApp]; }
|
||||
static void tray_exit() { objc_msgSend(app, sel_registerName("terminate:"), app); }
|
||||
|
||||
#elif defined(TRAY_WINAPI)
|
||||
#include <windows.h>
|
||||
|
||||
Reference in New Issue
Block a user