source: launchers/macosx/main.mm @ 7615b92

Last change on this file since 7615b92 was 7615b92, checked in by meeh <meeh@…>, 2 years ago

Adding all new code, removed a lot obsolete code and fixed import paths etc.
Mac OS X launcher:

  • UI built on Swift
    • Why?
      • Apple seems to on purpose make it harder to get into Objective-C these days
      • Swift is compiled to native code, but has easiness of Javascript in programming
      • Perfect for the OS X UI, many guides & tutorials as well
  • "Backend" in Objective-C++ / C++14
    • Why?
      • Originally written in Objective-C / C++14 with C++17 backports
      • Only for backend because of the time the development takes *

Short summary of features:

  • Java
    • It can detect java from:
      • JAVA_HOME environment variable
      • "Internet Plug-Ins" Apple stuff
      • By the /usr/libexec/java_home binary helper
    • It can unpack a new version of I2P
      • Unpacks to ~/Library/I2P
      • Can check currently unpacked version in ~/Library/I2P via i2p.jar's "net.i2p.CoreVersion?"
  • User Interface (a popover, see https://youtu.be/k8L3lQ5rUq0 for example of this concept)
    • Router control tab view
      • It can start the router
      • It can stop the router
      • It can detect already running router, then avoid fireing up one
      • It can show basic information about the router state & version
    • Log view tab (not yet done)
  • While left-click triggers popover, right-click draws a minimal context menu
  • Property mode set to 100644
File size: 19.1 KB
Line 
1#include <functional>
2#include <memory>
3#include <iostream>
4#include <algorithm>
5#include <string>
6#include <list>
7#include <sys/stat.h>
8#include <stdlib.h>
9#include <future>
10#include <vector>
11
12#import <Foundation/Foundation.h>
13
14#include <CoreFoundation/CoreFoundation.h>
15#include <CoreFoundation/CFStream.h>
16#include <CoreFoundation/CFPropertyList.h>
17#include <CoreFoundation/CFDictionary.h>
18#include <CoreFoundation/CFArray.h>
19#include <CoreFoundation/CFString.h>
20#include <CoreFoundation/CFPreferences.h>
21
22#import <objc/Object.h>
23#import <Cocoa/Cocoa.h>
24#import <AppKit/AppKit.h>
25#import <AppKit/NSApplication.h>
26
27#include "AppDelegate.h"
28#include "StatusItemButton.h"
29#include "RouterTask.h"
30#include "JavaHelper.h"
31#include "fn.h"
32#include "optional.hpp"
33#include "portcheck.h"
34
35#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
36
37JvmListSharedPtr gRawJvmList = nullptr;
38
39
40@interface MenuBarCtrl () <StatusItemButtonDelegate, NSMenuDelegate>
41@end
42
43@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
44@end
45
46std::vector<std::string> buildClassPath(std::string basePath)
47{
48    return globVector(basePath+std::string("/lib/*.jar"));
49}
50
51maybeAnRouterRunner getGlobalRouterObject()
52{
53    std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
54    return globalRouterStatus;
55}
56
57void setGlobalRouterObject(I2PRouterTask* newRouter)
58{
59    std::lock_guard<std::mutex> lock(globalRouterStatusMutex);
60    globalRouterStatus.emplace(newRouter);
61}
62
63
64pthread_mutex_t mutex;
65
66bool getGlobalRouterIsRunning()
67{
68    pthread_mutex_lock(&mutex);
69    bool current = isRuterRunning;
70    pthread_mutex_unlock(&mutex);
71    return current;
72}
73void setGlobalRouterIsRunning(bool running)
74{
75    pthread_mutex_lock(&mutex);
76    isRuterRunning = running;
77    pthread_mutex_unlock(&mutex);
78}
79
80std::future<int> startupRouter(NSString* javaBin, NSArray<NSString*>* arguments, NSString* i2pBaseDir) {
81    @try {
82        RTaskOptions* options = [RTaskOptions alloc];
83        options.binPath = javaBin;
84        options.arguments = arguments;
85        options.i2pBaseDir = i2pBaseDir;
86        auto instance = [[[I2PRouterTask alloc] initWithOptions: options] autorelease];
87        setGlobalRouterObject(instance);
88        //NSThread *thr = [[NSThread alloc] initWithTarget:instance selector:@selector(execute) object:nil];
89        [instance execute];
90        sendUserNotification(APP_IDSTR, @"The I2P router is starting up.");
91        auto pid = [instance getPID];
92        return std::async(std::launch::async, [&pid]{
93          return pid;
94        });
95    }
96    @catch (NSException *e)
97        {
98        auto errStr = [NSString stringWithFormat:@"Expection occurred %@",[e reason]];
99                NSLog(@"%@", errStr);
100        sendUserNotification(APP_IDSTR, errStr);
101        return std::async(std::launch::async, [&]{
102          return 0;
103        });
104        }
105}
106
107void openUrl(NSString* url)
108{
109    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: url]];
110}
111
112@implementation I2PStatusMenu
113
114- (BOOL)validateMenuItem:(NSMenuItem *)item
115{
116  NSLog(@"item is: %@",item);
117  return YES;
118}
119
120@end
121
122@implementation MenuBarCtrl
123
124- (void) statusItemButtonLeftClick: (StatusItemButton *) button
125{
126  CFShow(CFSTR("Left button clicked!"));
127  NSEvent *event = [NSApp currentEvent];
128}
129
130- (void) statusItemButtonRightClick: (StatusItemButton *) button
131{
132  CFShow(CFSTR("Right button clicked!"));
133  NSEvent *event = [NSApp currentEvent];
134  [self.statusItem popUpStatusItemMenu: self.menu];
135}
136
137- (void)statusBarImageBtnClicked
138{
139  [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(btnPressedAction) userInfo:nil repeats:NO];
140}
141
142- (void)btnPressedAction:(id)sender
143{
144  NSLog(@"Button presseeeeeeed");
145  NSEvent *event = [NSApp currentEvent];
146}
147
148- (void) openRouterConsoleBtnHandler: (NSMenuItem *) menuItem
149{
150  NSLog(@"Clicked openRouterConsoleBtnHandler");
151  openUrl(@"http://127.0.0.1:7657");
152}
153
154- (void) startJavaRouterBtnHandler: (NSMenuItem *) menuItem
155{
156  NSLog(@"Clicked startJavaRouterBtnHandler");
157  AppDelegate *appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
158  [appDelegate startupI2PRouter];
159}
160
161- (void) restartJavaRouterBtnHandler: (NSMenuItem *) menuItem
162{
163  NSLog(@"Clicked restartJavaRouterBtnHandler");
164  if (getGlobalRouterObject().has_value())
165  {
166      sendUserNotification(APP_IDSTR, @"Requesting the I2P router to restart.");
167      [getGlobalRouterObject().value() requestRestart];
168      NSLog(@"Requested restart");
169  }
170}
171
172- (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem
173{
174  NSLog(@"Clicked stopJavaRouterBtnHandler");
175  if (getGlobalRouterObject().has_value())
176  {
177      sendUserNotification(APP_IDSTR, @"Requesting the I2P router to shutdown.");
178      [getGlobalRouterObject().value() requestShutdown];
179      NSLog(@"Requested shutdown");
180  }
181}
182
183- (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem
184{
185  NSLog(@"quitWrapper event handler called!");
186  [[NSApplication sharedApplication] terminate:self];
187}
188
189- (MenuBarCtrl *) init
190{
191  self.menu = [self createStatusBarMenu];
192  self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
193
194  self.image = [NSImage imageNamed:@"ItoopieTransparent.png"];
195  [self.image setTemplate:YES];
196  self.statusItem.image = self.image;
197
198  self.statusItem.highlightMode = NO;
199  self.statusItem.toolTip = @"I2P Router Controller";
200
201  self.statusBarButton = [[StatusItemButton alloc] initWithImage:self.image];
202  self.statusBarButton.menu = self.menu;
203
204  // Selecting action
205  //[self.statusBarButton setAction:@selector(statusBarImageBtnClicked)];
206  //[self.statusBarButton setTarget:self];
207  self.statusBarButton.delegate = self;
208  [self.statusItem popUpStatusItemMenu: self.menu];
209
210  [self.statusItem setView: self.statusBarButton];
211  NSLog(@"Initialized statusbar and such");
212  return self;
213}
214
215-(void) dealloc
216{
217  [self.image release];
218  [self.menu release];
219}
220
221- (I2PStatusMenu *)createStatusBarMenu
222{
223  I2PStatusMenu *menu = [[I2PStatusMenu alloc] init];
224  [menu setAutoenablesItems:NO];
225
226  NSMenuItem *openConsoleI2Pbtn =
227    [[NSMenuItem alloc] initWithTitle:@"Open Console"
228                        action:@selector(openRouterConsoleBtnHandler:)
229                        keyEquivalent:@""];
230  [openConsoleI2Pbtn setTarget:self];
231  [openConsoleI2Pbtn setEnabled:YES];
232
233  NSMenuItem *startI2Pbtn =
234    [[NSMenuItem alloc] initWithTitle:@"Start I2P"
235                        action:@selector(startJavaRouterBtnHandler:)
236                        keyEquivalent:@""];
237  [startI2Pbtn setTarget:self];
238  if ([self.userPreferences boolForKey:@"autoStartRouter"])
239  {
240    [startI2Pbtn setEnabled:NO];
241  } else {
242    [startI2Pbtn setEnabled:YES];
243  }
244
245  NSMenuItem *restartI2Pbtn =
246    [[NSMenuItem alloc] initWithTitle:@"Restart I2P"
247                        action:@selector(restartJavaRouterBtnHandler:)
248                        keyEquivalent:@""];
249  [restartI2Pbtn setTarget:self];
250  [restartI2Pbtn setEnabled:YES];
251
252  NSMenuItem *stopI2Pbtn =
253    [[NSMenuItem alloc] initWithTitle:@"Stop I2P"
254                        action:@selector(stopJavaRouterBtnHandler:)
255                        keyEquivalent:@""];
256  [stopI2Pbtn setTarget:self];
257  [stopI2Pbtn setEnabled:YES];
258
259  NSMenuItem *quitWrapperBtn =
260    [[NSMenuItem alloc] initWithTitle:@"Quit I2P Wrapper"
261                        action:@selector(quitWrapperBtnHandler:)
262                        keyEquivalent:@""];
263  [quitWrapperBtn setTarget:self];
264  [quitWrapperBtn setEnabled:YES];
265
266
267  [menu addItem:openConsoleI2Pbtn];
268  [menu addItem:startI2Pbtn];
269  [menu addItem:stopI2Pbtn];
270  [menu addItem:restartI2Pbtn];
271  [menu addItem:quitWrapperBtn];
272  return menu;
273}
274
275@end
276
277@implementation ExtractMetaInfo
278@end
279
280@implementation AppDelegate
281
282- (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
283{
284  std::string basePath([self.metaInfo.i2pBase UTF8String]);
285  NSParameterAssert(self.metaInfo.i2pBase);
286  NSError *error = NULL;
287  BOOL success;
288  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
289
290    // Get paths
291    NSBundle *launcherBundle = [NSBundle mainBundle];
292
293    std::string basearg("-Di2p.dir.base=");
294    basearg += basePath;
295
296    std::string zippath("-Di2p.base.zip=");
297    zippath += [self.metaInfo.zipFile UTF8String];
298
299    std::string jarfile("-cp ");
300    jarfile += [self.metaInfo.jarFile UTF8String];
301
302    // Create directory
303    mkdir(basePath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
304
305    auto cli = JavaRunner::defaultFlagsForExtractorJob;
306    setenv("I2PBASE", basePath.c_str(), true);
307    setenv("ZIPPATH", zippath.c_str(), true);
308    //setenv("DYLD_LIBRARY_PATH",".:/usr/lib:/lib:/usr/local/lib", true);
309
310    cli.push_back(basearg);
311    cli.push_back(zippath);
312    cli.push_back(jarfile);
313    cli.push_back("net.i2p.launchers.BaseExtractor");
314
315    //auto charCli = map(cli, [](std::string str){ return str.c_str(); });
316    std::string execStr = [self.metaInfo.javaBinary UTF8String];
317    for_each(cli, [&execStr](std::string str){ execStr += std::string(" ") + str; });
318
319    NSLog(@"Trying cmd: %@", [NSString stringWithUTF8String:execStr.c_str()]);
320    try {
321        sendUserNotification(APP_IDSTR, @"Please hold on while we extract I2P. You'll get a new message once done!", self.contentImage);
322        int extractStatus = Popen(execStr.c_str(), environment{{
323            {"ZIPPATH", zippath.c_str()},
324            {"I2PBASE", basePath.c_str()}
325        }}).wait();
326        NSLog(@"Extraction exit code %@",[NSString stringWithUTF8String:(std::to_string(extractStatus)).c_str()]);
327        if (extractStatus == 0)
328        {
329            //success = YES;
330        }
331    } catch (subprocess::OSError &err) {
332        auto errMsg = [NSString stringWithUTF8String:err.what()];
333        //success = NO;
334        NSLog(@"Exception: %@", errMsg);
335        sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg], self.contentImage);
336    }
337
338    // All done. Assume success and error are already set.
339    dispatch_async(dispatch_get_main_queue(), ^{
340      sendUserNotification(APP_IDSTR, @"Extraction complete!", self.contentImage);
341      if (completion) {
342        completion(success, error);
343      }
344    });
345  });
346}
347
348- (void)startupI2PRouter
349{
350  std::string basePath([self.metaInfo.i2pBase UTF8String]);
351
352  // Get paths
353  NSBundle *launcherBundle = [NSBundle mainBundle];
354  auto jarList = buildClassPath(basePath);
355  std::string classpathStrHead = "-classpath";
356  std::string classpathStr = "";
357  classpathStr += [[launcherBundle pathForResource:@"launcher" ofType:@"jar"] UTF8String];
358  std::string prefix(basePath);
359  prefix += "/lib/";
360  for_each(jarList, [&classpathStr](std::string str){ classpathStr += std::string(":") + str; });
361  //if (self.enableVerboseLogging) NSLog(@"Classpath: %@\n",[NSString stringWithUTF8String:classpathStr.c_str()]);
362
363  try {
364    auto argList = JavaRunner::defaultStartupFlags;
365
366    std::string baseDirArg("-Di2p.dir.base=");
367    baseDirArg += basePath;
368    std::string javaLibArg("-Djava.library.path=");
369    javaLibArg += basePath;
370    // TODO: pass this to JVM
371    auto java_opts = getenv("JAVA_OPTS");
372
373    argList.push_back([NSString stringWithUTF8String:baseDirArg.c_str()]);
374    argList.push_back([NSString stringWithUTF8String:javaLibArg.c_str()]);
375    argList.push_back([NSString stringWithUTF8String:classpathStrHead.c_str()]);
376    argList.push_back([NSString stringWithUTF8String:classpathStr.c_str()]);
377    argList.push_back(@"net.i2p.router.Router");
378    auto javaBin = std::string([self.metaInfo.javaBinary UTF8String]);
379
380
381    sendUserNotification(APP_IDSTR, @"I2P Router is starting up!", self.contentImage);
382    auto nsJavaBin = self.metaInfo.javaBinary;
383    auto nsBasePath = self.metaInfo.i2pBase;
384    NSArray* arrArguments = [NSArray arrayWithObjects:&argList[0] count:argList.size()];
385    startupRouter(nsJavaBin, arrArguments, nsBasePath);
386    //if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]);
387  } catch (std::exception &err) {
388    auto errMsg = [NSString stringWithUTF8String:err.what()];
389    NSLog(@"Exception: %@", errMsg);
390    sendUserNotification(APP_IDSTR, [NSString stringWithFormat:@"Error: %@", errMsg], self.contentImage);
391  }
392}
393
394- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center
395                               shouldPresentNotification:(NSUserNotification *)notification {
396    return YES;
397}
398
399- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList
400{
401  NSString *appleScriptString = @"set jvmlist to {\"Newest\"";
402  for (auto item : *rawJvmList) {
403    auto str = strprintf(",\"%s\"", item->JVMName.c_str()).c_str();
404    NSString* tmp = [NSString stringWithUTF8String:str];
405    appleScriptString = [appleScriptString stringByAppendingString:tmp];
406  }
407  appleScriptString = [appleScriptString stringByAppendingString:@"}\nchoose from list jvmlist\n"];
408  NSAppleScript *theScript = [[NSAppleScript alloc] initWithSource:appleScriptString];
409  NSDictionary *theError = nil;
410  NSString* userResult = [[theScript executeAndReturnError: &theError] stringValue];
411  NSLog(@"User choosed %@.\n", userResult);
412  if (theError != nil)
413  {
414    NSLog(@"Error: %@.\n", theError);
415  }
416  return userResult;
417}
418
419
420- (void)userChooseJavaHome {
421  listAllJavaInstallsAvailable();
422  std::shared_ptr<JvmHomeContext> appContext = std::shared_ptr<JvmHomeContext>( new JvmHomeContext() );
423  for (auto item : *appContext->getJvmList()) {
424    printf("JVM %s (Version: %s, Directory: %s)\n", item->JVMName.c_str(), item->JVMPlatformVersion.c_str(), item->JVMHomePath.c_str());
425  }
426  JvmListPtr rawJvmList = appContext->getJvmList();
427  NSString * userJavaHome = [self userSelectJavaHome: rawJvmList];
428  // TODO: Add logic so user can set preferred JVM
429}
430
431- (void)setApplicationDefaultPreferences {
432  auto defaultJVMHome = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER});
433  auto tmpStdStr = std::string(defaultJVMHome.buf.data());
434  trim(tmpStdStr);
435  auto cfDefaultHome  = CFStringCreateWithCString(NULL, const_cast<const char *>(tmpStdStr.c_str()), kCFStringEncodingUTF8);
436  [self.userPreferences registerDefaults:@{
437    @"javaHome" : (NSString *)cfDefaultHome,
438    @"lastI2PVersion" : (NSString *)CFSTR(DEF_I2P_VERSION),
439    @"enableLogging": @true,
440    @"enableVerboseLogging": @true,
441    @"autoStartRouter": @true,
442    @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
443  }];
444  if (self.enableVerboseLogging) NSLog(@"Default JVM home preference set to: %@", (NSString *)cfDefaultHome);
445
446  auto dict = [self.userPreferences dictionaryRepresentation];
447  [self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
448
449  CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
450  CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
451  //CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
452
453  if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
454}
455
456
457- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
458  // Init application here
459  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
460  // Start with user preferences
461  self.userPreferences = [NSUserDefaults standardUserDefaults];
462  [self setApplicationDefaultPreferences];
463  self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
464  self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
465
466
467  // Get paths
468  NSBundle *launcherBundle = [NSBundle mainBundle];
469  auto iconImage = [launcherBundle pathForResource:@"ItoopieTransparent" ofType:@"png"];
470  self.contentImage = [NSImage imageNamed:iconImage];
471
472  gRawJvmList = std::make_shared<std::list<JvmVersionPtr> >(std::list<JvmVersionPtr>());
473  // In case we are unbundled, make us a proper UI application
474  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
475  [NSApp activateIgnoringOtherApps:YES];
476  auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"];
477  if (self.enableVerboseLogging) NSLog(@"Java home from preferences: %@", javaHomePref);
478
479  // This is the only GUI the user experience on a regular basis.
480  self.menuBarCtrl = [[MenuBarCtrl alloc] init];
481
482  NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
483  if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain);
484
485  NSLog(@"We should have started the statusbar object by now...");
486
487  std::string i2pBaseDir(getDefaultBaseDir());
488  //if (self.enableVerboseLogging) printf("Home directory is: %s\n", buffer);
489
490
491  //[statusBarButton setAction:@selector(itemClicked:)];
492  //dispatch_async(dispatch_get_main_queue(), ^{
493  //});
494  auto pref = self.userPreferences;
495  self.menuBarCtrl.userPreferences = self.userPreferences;
496  self.menuBarCtrl.enableLogging = self.enableLogging;
497  self.menuBarCtrl.enableVerboseLogging = self.enableVerboseLogging;
498
499
500
501  if (port_check() != 0)
502  {
503    NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
504    return;
505  }
506
507  if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]);
508
509  auto getJavaBin = [&pref,&self]() -> std::string {
510    // Get Java home
511    NSString* val = @"";
512    val = [pref stringForKey:@"javaHome"];
513    if (val == NULL) val = @"";
514    if (self.enableVerboseLogging) NSLog(@"Javahome: %@", val);
515    auto javaHome = std::string([val UTF8String]);
516    trim(javaHome); // Trim to remove endline
517    auto javaBin = std::string(javaHome);
518    javaBin += "/bin/java"; // Append java binary to path.
519    return javaBin;
520  };
521
522
523  self.metaInfo = [[ExtractMetaInfo alloc] init];
524  self.metaInfo.i2pBase = [NSString stringWithUTF8String:i2pBaseDir.c_str()];
525  self.metaInfo.javaBinary = [NSString stringWithUTF8String:getJavaBin().c_str()];
526  self.metaInfo.jarFile = [launcherBundle pathForResource:@"launcher" ofType:@"jar"];
527  self.metaInfo.zipFile = [launcherBundle pathForResource:@"base" ofType:@"zip"];
528
529  std::string basearg("-Di2p.dir.base=");
530  basearg += i2pBaseDir;
531
532  std::string jarfile("-cp ");
533  jarfile += [self.metaInfo.zipFile UTF8String];
534
535  struct stat sb;
536  if ( !(stat(i2pBaseDir.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) )
537  {
538    // I2P is not extracted.
539    if (self.enableVerboseLogging) NSLog(@"I2P Directory don't exists!");
540
541    [self extractI2PBaseDir:^(BOOL success, NSError *error) {
542        //__typeof__(self) strongSelf = weakSelf;
543        //if (strongSelf == nil) return;
544        [self startupI2PRouter];
545    }];
546
547  } else {
548      if (self.enableVerboseLogging) NSLog(@"I2P directory found!");
549      [self startupI2PRouter];
550  }
551
552}
553
554
555
556/**
557 *
558 * Exit sequence
559 *
560 **/
561- (void)applicationWillTerminate:(NSNotification *)aNotification {
562  // Tear down here
563  NSString *string = @"applicationWillTerminate executed";
564  NSLog(@"%@", string);
565  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
566}
567
568
569/* wrapper for main */
570- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
571  return self;
572}
573@end
574
575
576
577int main(int argc, const char **argv)
578{
579  NSApplication *app = [NSApplication sharedApplication];
580  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
581
582
583
584  app.delegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
585  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
586
587  [NSApp run];
588  // Handle any errors
589  //CFRelease(javaHomes);
590  //CFRelease(err);
591  [pool drain];
592  return 0;
593}
594
595
596
Note: See TracBrowser for help on using the repository browser.