source: launchers/macosx/obj-cpp/main.mm @ 0b01cc5

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

Adding codebase for the Objective-C++ part of the Mac OS X launcher/wrapper.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1#include <functional>
2#include <memory>
3#include <iostream>
4#include <algorithm>
5#include <string>
6#include <list>
7#include <experimental/optional>
8
9#import <Foundation/Foundation.h>
10
11#include <CoreFoundation/CoreFoundation.h>
12#include <CoreFoundation/CFStream.h>
13#include <CoreFoundation/CFPropertyList.h>
14#include <CoreFoundation/CFDictionary.h>
15#include <CoreFoundation/CFArray.h>
16#include <CoreFoundation/CFString.h>
17#include <CoreFoundation/CFPreferences.h>
18
19#import <objc/Object.h>
20#import <Cocoa/Cocoa.h>
21#import <AppKit/AppKit.h>
22#import <AppKit/NSApplication.h>
23
24#include "AppDelegate.h"
25#include "StatusItemButton.h"
26#include "JavaRunner.h"
27#include "JavaHelper.h"
28
29#define DEF_I2P_VERSION "0.9.35"
30#define APPDOMAIN "net.i2p.launcher"
31#define NSAPPDOMAIN @APPDOMAIN
32#define CFAPPDOMAIN CFSTR(APPDOMAIN)
33
34#define debug(format, ...) CFShow([NSString stringWithFormat:format, ## __VA_ARGS__]);
35
36JvmListSharedPtr gRawJvmList = nullptr;
37
38
39@interface MenuBarCtrl () <StatusItemButtonDelegate, NSMenuDelegate>
40@end
41
42@interface AppDelegate () <NSUserNotificationCenterDelegate, NSApplicationDelegate>
43@end
44
45
46@implementation MenuBarCtrl
47
48- (void) statusItemButtonLeftClick: (StatusItemButton *) button
49{
50  CFShow(CFSTR("Left button clicked!"));
51  NSEvent *event = [NSApp currentEvent];
52}
53
54- (void) statusItemButtonRightClick: (StatusItemButton *) button
55{
56  CFShow(CFSTR("Right button clicked!"));
57  NSEvent *event = [NSApp currentEvent];
58  [self.statusItem popUpStatusItemMenu: self.menu];
59}
60
61- (void)statusBarImageBtnClicked
62{
63  [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(btnPressedAction) userInfo:nil repeats:NO];
64}
65
66- (void)btnPressedAction:(id)sender
67{
68  NSLog(@"Button presseeeeeeed");
69  NSEvent *event = [NSApp currentEvent];
70}
71
72- (void) startJavaRouterBtnHandler: (NSMenuItem *) menuItem
73{
74  NSLog(@"Clicked startJavaRouterBtnHandler");
75}
76
77- (void) restartJavaRouterBtnHandler: (NSMenuItem *) menuItem
78{
79  NSLog(@"Clicked restartJavaRouterBtnHandler");
80}
81
82- (void) stopJavaRouterBtnHandler: (NSMenuItem *) menuItem
83{
84  NSLog(@"Clicked stopJavaRouterBtnHandler");
85}
86
87- (void) quitWrapperBtnHandler: (NSMenuItem *) menuItem
88{
89  NSLog(@"quitWrapper event handler called!");
90  [[NSApplication sharedApplication] terminate:self];
91}
92
93- (MenuBarCtrl *) init
94{
95  self.menu = [self createStatusBarMenu];
96  self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
97
98  self.image = [NSImage imageNamed:@"ItoopieTransparent.png"];
99  [self.image setTemplate:YES];
100  self.statusItem.image = self.image;
101
102  self.statusItem.highlightMode = NO;
103  self.statusItem.toolTip = @"I2P Router Controller";
104
105  self.statusBarButton = [[StatusItemButton alloc] initWithImage:self.image];
106  self.statusBarButton.menu = self.menu;
107
108  // Selecting action
109  //[self.statusBarButton setAction:@selector(statusBarImageBtnClicked)];
110  //[self.statusBarButton setTarget:self];
111  self.statusBarButton.delegate = self;
112  [self.statusItem popUpStatusItemMenu: self.menu];
113
114  [self.statusItem setView: self.statusBarButton];
115  NSLog(@"Initialized statusbar and such");
116  return self;
117}
118
119-(void) dealloc
120{
121  [self.image release];
122  [self.menu release];
123}
124
125- (NSMenu *)createStatusBarMenu
126{
127  NSMenu *menu = [[NSMenu alloc] init];
128  [menu setAutoenablesItems:NO];
129  NSMenuItem *startI2Pbtn =
130    [[NSMenuItem alloc] initWithTitle:@"Start I2P"
131                        action:@selector(startJavaRouterBtnHandler:)
132                        keyEquivalent:@""];
133  [startI2Pbtn setTarget:self];
134  if ([self.userPreferences boolForKey:@"autoStartRouter"])
135  {
136    [startI2Pbtn setEnabled:NO];
137  } else {
138    [startI2Pbtn setEnabled:YES];
139  }
140
141  NSMenuItem *restartI2Pbtn =
142    [[NSMenuItem alloc] initWithTitle:@"Restart I2P"
143                        action:@selector(restartJavaRouterBtnHandler:)
144                        keyEquivalent:@""];
145  [restartI2Pbtn setTarget:self];
146  [restartI2Pbtn setEnabled:YES];
147
148  NSMenuItem *stopI2Pbtn =
149    [[NSMenuItem alloc] initWithTitle:@"Stop I2P"
150                        action:@selector(stopJavaRouterBtnHandler:)
151                        keyEquivalent:@""];
152  [stopI2Pbtn setTarget:self];
153  [stopI2Pbtn setEnabled:YES];
154
155  NSMenuItem *quitWrapperBtn =
156    [[NSMenuItem alloc] initWithTitle:@"Quit I2P Wrapper"
157                        action:@selector(quitWrapperBtnHandler:)
158                        keyEquivalent:@""];
159  [quitWrapperBtn setTarget:self];
160  [quitWrapperBtn setEnabled:YES];
161
162
163  [menu addItem:startI2Pbtn];
164  [menu addItem:stopI2Pbtn];
165  [menu addItem:restartI2Pbtn];
166  [menu addItem:quitWrapperBtn];
167  return menu;
168}
169
170@end
171
172
173@implementation AppDelegate
174
175- (NSString *)userSelectJavaHome:(JvmListPtr)rawJvmList
176{
177  NSString *appleScriptString = @"set jvmlist to {\"Newest\"";
178  for (auto item : *rawJvmList) {
179    auto str = strprintf(",\"%s\"", item->JVMName.c_str()).c_str();
180    NSString* tmp = [NSString stringWithUTF8String:str];
181    appleScriptString = [appleScriptString stringByAppendingString:tmp];
182  }
183  appleScriptString = [appleScriptString stringByAppendingString:@"}\nchoose from list jvmlist\n"];
184  NSAppleScript *theScript = [[NSAppleScript alloc] initWithSource:appleScriptString];
185  NSDictionary *theError = nil;
186  NSString* userResult = [[theScript executeAndReturnError: &theError] stringValue];
187  NSLog(@"User choosed %@.\n", userResult);
188  if (theError != nil)
189  {
190    NSLog(@"Error: %@.\n", theError);
191  }
192  return userResult;
193}
194
195
196- (void)userChooseJavaHome {
197  listAllJavaInstallsAvailable();
198  std::shared_ptr<JvmHomeContext> appContext = std::shared_ptr<JvmHomeContext>( new JvmHomeContext() );
199  for (auto item : *appContext->getJvmList()) {
200    printf("JVM %s (Version: %s, Directory: %s)\n", item->JVMName.c_str(), item->JVMPlatformVersion.c_str(), item->JVMHomePath.c_str());
201  }
202  JvmListPtr rawJvmList = appContext->getJvmList();
203  NSString * userJavaHome = [self userSelectJavaHome: rawJvmList];
204  // TODO: Add logic so user can set preferred JVM
205}
206
207- (void)setApplicationDefaultPreferences {
208  auto defaultJVMHome = check_output({"/usr/libexec/java_home","-v",DEF_MIN_JVM_VER});
209  auto tmpStdStr = std::string(defaultJVMHome.buf.data());
210  trim(tmpStdStr);
211  auto cfDefaultHome  = CFStringCreateWithCString(NULL, const_cast<const char *>(tmpStdStr.c_str()), kCFStringEncodingUTF8);
212  [self.userPreferences registerDefaults:@{
213    @"javaHome" : (NSString *)cfDefaultHome,
214    @"lastI2PVersion" : (NSString *)CFSTR(DEF_I2P_VERSION),
215    @"enableLogging": @true,
216    @"enableVerboseLogging": @true,
217    @"autoStartRouter": @true
218  }];
219  if (self.enableVerboseLogging) NSLog(@"Default JVM home preference set to: %@", (NSString *)cfDefaultHome);
220
221  auto dict = [self.userPreferences dictionaryRepresentation];
222  [self.userPreferences setPersistentDomain:dict forName:NSAPPDOMAIN];
223
224  CFPreferencesSetMultiple((CFDictionaryRef)dict, NULL, CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
225  CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
226  //CFPreferencesSetAppValue(@"javaHome", (CFPropertyListRef)cfDefaultHome, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
227 
228  if (self.enableVerboseLogging) NSLog(@"Default preferences stored!");
229}
230
231
232- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
233  // Init application here
234  // Start with user preferences
235  self.userPreferences = [NSUserDefaults standardUserDefaults];
236  [self setApplicationDefaultPreferences];
237  self.enableLogging = [self.userPreferences boolForKey:@"enableLogging"];
238  self.enableVerboseLogging = [self.userPreferences boolForKey:@"enableVerboseLogging"];
239
240  gRawJvmList = std::make_shared<std::list<JvmVersionPtr> >(std::list<JvmVersionPtr>());
241  // In case we are unbundled, make us a proper UI application
242  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
243  [NSApp activateIgnoringOtherApps:YES];
244  //auto prefArray = CFPreferencesCopyKeyList(CFAPPDOMAIN, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
245  //CFShow(prefArray);
246  auto javaHomePref = [self.userPreferences stringForKey:@"javaHome"];
247  if (self.enableVerboseLogging) NSLog(@"Java home from preferences: %@", javaHomePref);
248
249  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
250
251  // This is the only GUI the user experience on a regular basis.
252  self.menuBarCtrl = [[MenuBarCtrl alloc] init];
253
254  NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
255  if (self.enableVerboseLogging) NSLog(@"Appdomain is: %@", appDomain);
256
257  NSLog(@"We should have started the statusbar object by now...");
258  //[statusBarButton setAction:@selector(itemClicked:)];
259  //dispatch_async(dispatch_get_main_queue(), ^{
260  //});
261  auto pref = self.userPreferences;
262  self.menuBarCtrl.userPreferences = self.userPreferences;
263  self.menuBarCtrl.enableLogging = self.enableLogging;
264  self.menuBarCtrl.enableVerboseLogging = self.enableVerboseLogging;
265
266  if (self.enableVerboseLogging) NSLog(@"processinfo %@", [[NSProcessInfo processInfo] arguments]);
267
268  auto getJavaHomeLambda = [&pref,&self]() -> std::string {
269      NSString* val = @"";
270      val = [pref stringForKey:@"javaHome"];
271      if (val == NULL) val = @"";
272      if (self.enableVerboseLogging) NSLog(@"Javahome: %@", val);
273      return std::string([val UTF8String]);;
274  };
275
276
277  auto launchLambda = [&pref](JavaRunner *javaRun) {
278    javaRun->javaProcess->start_process();
279    auto pid = javaRun->javaProcess->pid();
280    std::cout << "I2P Router process id = " << pid << std::endl;
281
282    // Blocking
283    javaRun->javaProcess->wait();
284  };
285  auto callbackAfterExit = [=](){
286    printf("Callback after exit\n");
287  };
288
289  try {
290
291    // Get Java home
292    auto javaHome = getJavaHomeLambda();
293    trim(javaHome); // Trim to remove endline
294    auto javaBin = std::string(javaHome);
295    javaBin += "/bin/java"; // Append java binary to path.
296    //printf("hello world: %s\n", javaBin.c_str());
297    if (self.enableVerboseLogging) NSLog(@"Defaults: %@", [pref dictionaryRepresentation]);
298
299    auto r = new JavaRunner{ javaBin, launchLambda, callbackAfterExit };
300    r->execute();
301  } catch (std::exception &err) {
302    std::cerr << "Exception: " << err.what() << std::endl;
303  }
304}
305
306
307
308/**
309 *
310 * Exit sequence
311 *
312 **/
313- (void)applicationWillTerminate:(NSNotification *)aNotification {
314  // Tear down here
315  NSString *string = @"applicationWillTerminate executed";
316  NSLog(@"%@", string);
317  [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
318}
319
320
321/* wrapper for main */
322- (AppDelegate *)initWithArgc:(int)argc argv:(const char **)argv {
323  return self;
324}
325@end
326
327
328
329int main(int argc, const char **argv)
330{
331  NSApplication *app = [NSApplication sharedApplication];
332  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
333
334
335
336  app.delegate = [[AppDelegate alloc] initWithArgc:argc argv:argv];
337  [NSBundle loadNibNamed:@"I2Launcher" owner:NSApp];
338
339  [NSApp run];
340  // Handle any errors
341  //CFRelease(javaHomes);
342  //CFRelease(err);
343  [pool drain];
344  return 0;
345}
346
347
348
Note: See TracBrowser for help on using the repository browser.