@atom-js-org/runtime 0.4.2-alpha.0 → 0.4.4-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/native-host.cjs +156 -34
- package/src/runtime/macos-native-host.m +111 -13
package/package.json
CHANGED
package/src/native-host.cjs
CHANGED
|
@@ -10,7 +10,8 @@ let singleton = null;
|
|
|
10
10
|
|
|
11
11
|
class NativeHost {
|
|
12
12
|
constructor(appName) {
|
|
13
|
-
this.
|
|
13
|
+
this.metadata = resolveMacHostMetadata(appName);
|
|
14
|
+
this.appName = this.metadata.appName;
|
|
14
15
|
this.child = null;
|
|
15
16
|
this.startPromise = null;
|
|
16
17
|
this.readyResolve = null;
|
|
@@ -23,16 +24,23 @@ class NativeHost {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async createWindow(window, config) {
|
|
27
|
+
const windowId = Number(window.id);
|
|
26
28
|
await this.ensureStarted();
|
|
27
|
-
this.windows.set(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
this.windows.set(windowId, window);
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await this.request({
|
|
33
|
+
command: 'create',
|
|
34
|
+
windowId,
|
|
35
|
+
config
|
|
36
|
+
}, 15000);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.windows.delete(windowId);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
async request(message) {
|
|
43
|
+
async request(message, timeoutMs = 30000) {
|
|
36
44
|
await this.ensureStarted();
|
|
37
45
|
|
|
38
46
|
const requestId = `${process.pid}-${this.nextRequestId++}`;
|
|
@@ -40,7 +48,7 @@ class NativeHost {
|
|
|
40
48
|
const timeout = setTimeout(() => {
|
|
41
49
|
this.pending.delete(requestId);
|
|
42
50
|
reject(new Error(`AtomJS native host request timed out: ${message.command}`));
|
|
43
|
-
},
|
|
51
|
+
}, timeoutMs);
|
|
44
52
|
|
|
45
53
|
this.pending.set(requestId, { resolve, reject, timeout });
|
|
46
54
|
|
|
@@ -79,10 +87,15 @@ class NativeHost {
|
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
async _start() {
|
|
82
|
-
const executable = await resolveNativeHostExecutable();
|
|
90
|
+
const executable = await resolveNativeHostExecutable(this.metadata);
|
|
91
|
+
const args = [
|
|
92
|
+
'--app-name', this.metadata.appName,
|
|
93
|
+
'--app-id', this.metadata.appId
|
|
94
|
+
];
|
|
95
|
+
if (this.metadata.iconPath) args.push('--app-icon', this.metadata.iconPath);
|
|
83
96
|
|
|
84
97
|
await new Promise((resolve, reject) => {
|
|
85
|
-
const child = spawn(executable,
|
|
98
|
+
const child = spawn(executable, args, {
|
|
86
99
|
cwd: process.env.ATOM_PROJECT_ROOT || process.cwd(),
|
|
87
100
|
env: process.env,
|
|
88
101
|
stdio: ['pipe', 'pipe', 'inherit']
|
|
@@ -196,6 +209,17 @@ class NativeHost {
|
|
|
196
209
|
return;
|
|
197
210
|
}
|
|
198
211
|
|
|
212
|
+
if (event.type === 'host-error') {
|
|
213
|
+
const error = new Error(event.error || 'AtomJS native macOS host reported an error.');
|
|
214
|
+
for (const pending of this.pending.values()) {
|
|
215
|
+
clearTimeout(pending.timeout);
|
|
216
|
+
pending.reject(error);
|
|
217
|
+
}
|
|
218
|
+
this.pending.clear();
|
|
219
|
+
console.error(`[AtomJS] ${error.message}`);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
199
223
|
if (event.type === 'response' && event.requestId) {
|
|
200
224
|
const pending = this.pending.get(String(event.requestId));
|
|
201
225
|
if (!pending) return;
|
|
@@ -266,7 +290,63 @@ async function stopNativeHost() {
|
|
|
266
290
|
await host.stop();
|
|
267
291
|
}
|
|
268
292
|
|
|
269
|
-
|
|
293
|
+
function resolveMacHostMetadata(appName) {
|
|
294
|
+
const projectRoot = path.resolve(process.env.ATOM_PROJECT_ROOT || process.cwd());
|
|
295
|
+
const packageJson = readJsonIfExists(path.join(projectRoot, 'package.json')) || {};
|
|
296
|
+
const atomConfig = readJsonIfExists(path.join(projectRoot, 'atom.config.json')) || {};
|
|
297
|
+
|
|
298
|
+
const resolvedName = String(
|
|
299
|
+
process.env.ATOM_APP_NAME ||
|
|
300
|
+
appName ||
|
|
301
|
+
atomConfig.productName ||
|
|
302
|
+
packageJson.productName ||
|
|
303
|
+
packageJson.name ||
|
|
304
|
+
'AtomJS App'
|
|
305
|
+
);
|
|
306
|
+
const appId = sanitizeBundleIdentifier(
|
|
307
|
+
process.env.ATOM_APP_ID ||
|
|
308
|
+
atomConfig.appId ||
|
|
309
|
+
`com.atomjs.${packageJson.name || resolvedName}`
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const configuredIcon = process.env.ATOM_APP_ICON || atomConfig.icon || null;
|
|
313
|
+
const iconPath = configuredIcon
|
|
314
|
+
? path.resolve(projectRoot, configuredIcon)
|
|
315
|
+
: null;
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
appName: resolvedName,
|
|
319
|
+
appId,
|
|
320
|
+
iconPath: iconPath && fs.existsSync(iconPath) ? iconPath : null
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function readJsonIfExists(filePath) {
|
|
325
|
+
try {
|
|
326
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function sanitizeBundleIdentifier(value) {
|
|
333
|
+
const normalized = String(value || 'com.atomjs.app')
|
|
334
|
+
.toLowerCase()
|
|
335
|
+
.replace(/[^a-z0-9.-]+/g, '-')
|
|
336
|
+
.replace(/^[^a-z]+/, '')
|
|
337
|
+
.replace(/\.{2,}/g, '.')
|
|
338
|
+
.replace(/^[.-]+|[.-]+$/g, '');
|
|
339
|
+
return normalized.includes('.') ? normalized : `com.atomjs.${normalized || 'app'}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function sanitizeMacExecutableName(value) {
|
|
343
|
+
return String(value || 'AtomJS App')
|
|
344
|
+
.replace(/[/:\x00-\x1f]/g, '-')
|
|
345
|
+
.replace(/\s+/g, ' ')
|
|
346
|
+
.trim() || 'AtomJS App';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function resolveNativeHostExecutable(metadata) {
|
|
270
350
|
const bundled = process.env.ATOM_MACOS_HOST_EXECUTABLE;
|
|
271
351
|
if (bundled) {
|
|
272
352
|
const resolved = path.resolve(bundled);
|
|
@@ -282,42 +362,84 @@ async function resolveNativeHostExecutable() {
|
|
|
282
362
|
}
|
|
283
363
|
|
|
284
364
|
const sourceData = await fs.promises.readFile(source);
|
|
285
|
-
const
|
|
365
|
+
const hashBuilder = crypto
|
|
286
366
|
.createHash('sha256')
|
|
287
367
|
.update(sourceData)
|
|
288
368
|
.update(process.arch)
|
|
289
|
-
.
|
|
290
|
-
|
|
369
|
+
.update(JSON.stringify({ appName: metadata.appName, appId: metadata.appId }));
|
|
370
|
+
if (metadata.iconPath) hashBuilder.update(await fs.promises.readFile(metadata.iconPath));
|
|
371
|
+
const hash = hashBuilder.digest('hex').slice(0, 20);
|
|
291
372
|
|
|
373
|
+
const executableName = sanitizeMacExecutableName(metadata.appName);
|
|
292
374
|
const outputDirectory = path.join(os.tmpdir(), 'atomjs-native-host', hash);
|
|
293
|
-
const
|
|
375
|
+
const appBundle = path.join(outputDirectory, `${executableName}.app`);
|
|
376
|
+
const executable = path.join(appBundle, 'Contents', 'MacOS', executableName);
|
|
294
377
|
if (fs.existsSync(executable)) return executable;
|
|
295
378
|
|
|
296
379
|
await fs.promises.mkdir(outputDirectory, { recursive: true });
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
source,
|
|
307
|
-
'-o', temporary
|
|
308
|
-
]);
|
|
309
|
-
|
|
310
|
-
await fs.promises.chmod(temporary, 0o755);
|
|
380
|
+
const temporaryBundle = `${appBundle}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
|
|
381
|
+
const contents = path.join(temporaryBundle, 'Contents');
|
|
382
|
+
const macosDirectory = path.join(contents, 'MacOS');
|
|
383
|
+
const resourcesDirectory = path.join(contents, 'Resources');
|
|
384
|
+
const temporaryExecutable = path.join(macosDirectory, executableName);
|
|
385
|
+
|
|
386
|
+
await fs.promises.mkdir(macosDirectory, { recursive: true });
|
|
387
|
+
await fs.promises.mkdir(resourcesDirectory, { recursive: true });
|
|
388
|
+
|
|
311
389
|
try {
|
|
312
|
-
await
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
390
|
+
await runCompiler([
|
|
391
|
+
'clang',
|
|
392
|
+
'-fobjc-arc',
|
|
393
|
+
'-fmodules',
|
|
394
|
+
'-mmacosx-version-min=12.0',
|
|
395
|
+
'-framework', 'Cocoa',
|
|
396
|
+
'-framework', 'WebKit',
|
|
397
|
+
source,
|
|
398
|
+
'-o', temporaryExecutable
|
|
399
|
+
]);
|
|
400
|
+
await fs.promises.chmod(temporaryExecutable, 0o755);
|
|
401
|
+
|
|
402
|
+
let iconEntry = '';
|
|
403
|
+
if (metadata.iconPath && path.extname(metadata.iconPath).toLowerCase() === '.icns') {
|
|
404
|
+
await fs.promises.copyFile(metadata.iconPath, path.join(resourcesDirectory, 'AppIcon.icns'));
|
|
405
|
+
iconEntry = '<key>CFBundleIconFile</key><string>AppIcon</string>';
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
409
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
410
|
+
<plist version="1.0"><dict>
|
|
411
|
+
<key>CFBundleExecutable</key><string>${xmlEscape(executableName)}</string>
|
|
412
|
+
<key>CFBundleIdentifier</key><string>${xmlEscape(metadata.appId)}.dev-host</string>
|
|
413
|
+
<key>CFBundleName</key><string>${xmlEscape(metadata.appName)}</string>
|
|
414
|
+
<key>CFBundleDisplayName</key><string>${xmlEscape(metadata.appName)}</string>
|
|
415
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
416
|
+
<key>LSMinimumSystemVersion</key><string>12.0</string>
|
|
417
|
+
<key>NSHighResolutionCapable</key><true/>
|
|
418
|
+
${iconEntry}
|
|
419
|
+
</dict></plist>`;
|
|
420
|
+
await fs.promises.writeFile(path.join(contents, 'Info.plist'), plist, 'utf8');
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
await fs.promises.rename(temporaryBundle, appBundle);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (error.code !== 'EEXIST' && error.code !== 'ENOTEMPTY') throw error;
|
|
426
|
+
}
|
|
427
|
+
} finally {
|
|
428
|
+
await fs.promises.rm(temporaryBundle, { recursive: true, force: true });
|
|
316
429
|
}
|
|
317
430
|
|
|
318
431
|
return executable;
|
|
319
432
|
}
|
|
320
433
|
|
|
434
|
+
function xmlEscape(value) {
|
|
435
|
+
return String(value)
|
|
436
|
+
.replace(/&/g, '&')
|
|
437
|
+
.replace(/</g, '<')
|
|
438
|
+
.replace(/>/g, '>')
|
|
439
|
+
.replace(/"/g, '"')
|
|
440
|
+
.replace(/'/g, ''');
|
|
441
|
+
}
|
|
442
|
+
|
|
321
443
|
async function runCompiler(args) {
|
|
322
444
|
const xcrun = '/usr/bin/xcrun';
|
|
323
445
|
if (!fs.existsSync(xcrun)) {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#import <Cocoa/Cocoa.h>
|
|
2
2
|
#import <WebKit/WebKit.h>
|
|
3
3
|
#include <string.h>
|
|
4
|
+
#include <unistd.h>
|
|
5
|
+
#include <errno.h>
|
|
4
6
|
|
|
5
7
|
static NSString *const AtomJSEventPrefix = @"__ATOMJS_EVENT__";
|
|
6
8
|
|
|
7
9
|
@class AtomJSWindowController;
|
|
8
10
|
static NSMutableDictionary<NSNumber *, AtomJSWindowController *> *atomWindows;
|
|
9
11
|
static NSString *atomAppName = @"AtomJS App";
|
|
12
|
+
static NSString *atomAppIdentifier = @"com.atomjs.app";
|
|
13
|
+
static NSString *atomAppIconPath = nil;
|
|
10
14
|
|
|
11
15
|
static void AtomJSEmit(NSDictionary *payload) {
|
|
12
16
|
if (![NSJSONSerialization isValidJSONObject:payload]) return;
|
|
@@ -66,6 +70,46 @@ static NSColor *AtomJSColor(NSString *hex) {
|
|
|
66
70
|
return [NSColor colorWithSRGBRed:red green:green blue:blue alpha:alpha];
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
static NSImage *AtomJSDefaultApplicationIcon(void) {
|
|
74
|
+
NSSize size = NSMakeSize(512.0, 512.0);
|
|
75
|
+
NSImage *image = [[NSImage alloc] initWithSize:size];
|
|
76
|
+
[image lockFocus];
|
|
77
|
+
|
|
78
|
+
NSRect canvas = NSMakeRect(0.0, 0.0, size.width, size.height);
|
|
79
|
+
NSBezierPath *background = [NSBezierPath bezierPathWithRoundedRect:NSInsetRect(canvas, 18.0, 18.0) xRadius:112.0 yRadius:112.0];
|
|
80
|
+
[[NSColor colorWithSRGBRed:0.055 green:0.075 blue:0.11 alpha:1.0] setFill];
|
|
81
|
+
[background fill];
|
|
82
|
+
|
|
83
|
+
NSDictionary *letterAttributes = @{
|
|
84
|
+
NSFontAttributeName: [NSFont systemFontOfSize:252.0 weight:NSFontWeightSemibold],
|
|
85
|
+
NSForegroundColorAttributeName: [NSColor colorWithSRGBRed:0.42 green:0.78 blue:1.0 alpha:1.0]
|
|
86
|
+
};
|
|
87
|
+
NSString *letter = @"A";
|
|
88
|
+
NSSize letterSize = [letter sizeWithAttributes:letterAttributes];
|
|
89
|
+
[letter drawAtPoint:NSMakePoint((size.width - letterSize.width) / 2.0, 154.0) withAttributes:letterAttributes];
|
|
90
|
+
|
|
91
|
+
NSDictionary *suffixAttributes = @{
|
|
92
|
+
NSFontAttributeName: [NSFont systemFontOfSize:82.0 weight:NSFontWeightMedium],
|
|
93
|
+
NSForegroundColorAttributeName: [NSColor whiteColor]
|
|
94
|
+
};
|
|
95
|
+
NSString *suffix = @"JS";
|
|
96
|
+
NSSize suffixSize = [suffix sizeWithAttributes:suffixAttributes];
|
|
97
|
+
[suffix drawAtPoint:NSMakePoint((size.width - suffixSize.width) / 2.0, 82.0) withAttributes:suffixAttributes];
|
|
98
|
+
|
|
99
|
+
[image unlockFocus];
|
|
100
|
+
return image;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static void AtomJSConfigureApplicationIdentity(NSApplication *application) {
|
|
104
|
+
[[NSProcessInfo processInfo] setProcessName:atomAppName];
|
|
105
|
+
|
|
106
|
+
NSImage *icon = nil;
|
|
107
|
+
if (atomAppIconPath.length > 0) {
|
|
108
|
+
icon = [[NSImage alloc] initWithContentsOfFile:atomAppIconPath];
|
|
109
|
+
}
|
|
110
|
+
application.applicationIconImage = icon ?: AtomJSDefaultApplicationIcon();
|
|
111
|
+
}
|
|
112
|
+
|
|
69
113
|
@interface AtomJSWindowController : NSObject <NSWindowDelegate, WKNavigationDelegate>
|
|
70
114
|
@property(nonatomic, strong) NSNumber *windowId;
|
|
71
115
|
@property(nonatomic, strong) NSWindow *window;
|
|
@@ -124,7 +168,9 @@ static NSColor *AtomJSColor(NSString *hex) {
|
|
|
124
168
|
[self navigate:AtomJSString(config[@"url"], @"about:blank")];
|
|
125
169
|
|
|
126
170
|
if (AtomJSBoolean(config[@"show"], YES)) {
|
|
171
|
+
[NSApp unhide:nil];
|
|
127
172
|
[_window makeKeyAndOrderFront:nil];
|
|
173
|
+
[_window orderFrontRegardless];
|
|
128
174
|
[NSApp activateIgnoringOtherApps:YES];
|
|
129
175
|
}
|
|
130
176
|
|
|
@@ -355,10 +401,23 @@ static void AtomJSHandleMessage(NSDictionary *message) {
|
|
|
355
401
|
if ([command isEqualToString:@"create"]) {
|
|
356
402
|
NSNumber *windowId = AtomJSNumber(message[@"windowId"], nil);
|
|
357
403
|
NSDictionary *config = [message[@"config"] isKindOfClass:[NSDictionary class]] ? message[@"config"] : @{};
|
|
358
|
-
if (!windowId)
|
|
404
|
+
if (!windowId) {
|
|
405
|
+
AtomJSRespond(requestId, NO, nil, @"The create command did not include a valid window ID.");
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
359
408
|
|
|
360
409
|
AtomJSWindowController *controller = [[AtomJSWindowController alloc] initWithWindowId:windowId config:config];
|
|
410
|
+
if (!controller || !controller.window || !controller.webView) {
|
|
411
|
+
AtomJSRespond(requestId, NO, nil, @"AppKit could not create the AtomJS window.");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
361
415
|
atomWindows[windowId] = controller;
|
|
416
|
+
AtomJSRespond(requestId, YES, @{ @"windowId": windowId }, nil);
|
|
417
|
+
AtomJSEmit(@{
|
|
418
|
+
@"type": @"created",
|
|
419
|
+
@"windowId": windowId
|
|
420
|
+
});
|
|
362
421
|
return;
|
|
363
422
|
}
|
|
364
423
|
|
|
@@ -391,12 +450,16 @@ static void AtomJSHandleMessage(NSDictionary *message) {
|
|
|
391
450
|
if ([command isEqualToString:@"navigate"]) {
|
|
392
451
|
[controller navigate:AtomJSString(message[@"url"], @"about:blank")];
|
|
393
452
|
} else if ([command isEqualToString:@"show"]) {
|
|
453
|
+
[NSApp unhide:nil];
|
|
394
454
|
[controller.window makeKeyAndOrderFront:nil];
|
|
455
|
+
[controller.window orderFrontRegardless];
|
|
395
456
|
[NSApp activateIgnoringOtherApps:YES];
|
|
396
457
|
} else if ([command isEqualToString:@"hide"]) {
|
|
397
458
|
[controller.window orderOut:nil];
|
|
398
459
|
} else if ([command isEqualToString:@"focus"]) {
|
|
460
|
+
[NSApp unhide:nil];
|
|
399
461
|
[controller.window makeKeyAndOrderFront:nil];
|
|
462
|
+
[controller.window orderFrontRegardless];
|
|
400
463
|
[NSApp activateIgnoringOtherApps:YES];
|
|
401
464
|
} else if ([command isEqualToString:@"close"] || [command isEqualToString:@"destroy"]) {
|
|
402
465
|
[controller.window close];
|
|
@@ -433,14 +496,22 @@ static void AtomJSHandleMessage(NSDictionary *message) {
|
|
|
433
496
|
|
|
434
497
|
static void AtomJSConsumeInput(void) {
|
|
435
498
|
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
|
|
436
|
-
NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
|
|
437
499
|
NSMutableData *buffer = [NSMutableData data];
|
|
438
500
|
|
|
439
501
|
while (YES) {
|
|
440
502
|
@autoreleasepool {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
503
|
+
unsigned char bytes[4096];
|
|
504
|
+
ssize_t count = read(STDIN_FILENO, bytes, sizeof(bytes));
|
|
505
|
+
if (count < 0) {
|
|
506
|
+
if (errno == EINTR) continue;
|
|
507
|
+
AtomJSEmit(@{
|
|
508
|
+
@"type": @"host-error",
|
|
509
|
+
@"error": [NSString stringWithFormat:@"Could not read native-host input: %s", strerror(errno)]
|
|
510
|
+
});
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
if (count == 0) break;
|
|
514
|
+
[buffer appendBytes:bytes length:(NSUInteger)count];
|
|
444
515
|
|
|
445
516
|
while (YES) {
|
|
446
517
|
const unsigned char *bytes = buffer.bytes;
|
|
@@ -492,26 +563,53 @@ static void AtomJSInstallMenu(void) {
|
|
|
492
563
|
NSApp.mainMenu = mainMenu;
|
|
493
564
|
}
|
|
494
565
|
|
|
566
|
+
@interface AtomJSApplicationDelegate : NSObject <NSApplicationDelegate>
|
|
567
|
+
@end
|
|
568
|
+
|
|
569
|
+
@implementation AtomJSApplicationDelegate
|
|
570
|
+
|
|
571
|
+
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
|
|
572
|
+
AtomJSConfigureApplicationIdentity(NSApp);
|
|
573
|
+
AtomJSInstallMenu();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
|
577
|
+
AtomJSConsumeInput();
|
|
578
|
+
AtomJSEmit(@{
|
|
579
|
+
@"type": @"ready",
|
|
580
|
+
@"pid": @([[NSProcessInfo processInfo] processIdentifier]),
|
|
581
|
+
@"appName": atomAppName,
|
|
582
|
+
@"appId": atomAppIdentifier
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
|
|
587
|
+
return NO;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
@end
|
|
591
|
+
|
|
495
592
|
int main(int argc, const char *argv[]) {
|
|
496
593
|
@autoreleasepool {
|
|
497
594
|
for (int index = 1; index + 1 < argc; index += 1) {
|
|
498
595
|
if (strcmp(argv[index], "--app-name") == 0) {
|
|
499
596
|
atomAppName = [NSString stringWithUTF8String:argv[index + 1]] ?: @"AtomJS App";
|
|
500
597
|
index += 1;
|
|
598
|
+
} else if (strcmp(argv[index], "--app-id") == 0) {
|
|
599
|
+
atomAppIdentifier = [NSString stringWithUTF8String:argv[index + 1]] ?: @"com.atomjs.app";
|
|
600
|
+
index += 1;
|
|
601
|
+
} else if (strcmp(argv[index], "--app-icon") == 0) {
|
|
602
|
+
atomAppIconPath = [NSString stringWithUTF8String:argv[index + 1]];
|
|
603
|
+
index += 1;
|
|
501
604
|
}
|
|
502
605
|
}
|
|
503
606
|
|
|
607
|
+
[[NSProcessInfo processInfo] setProcessName:atomAppName];
|
|
504
608
|
atomWindows = [NSMutableDictionary dictionary];
|
|
505
609
|
NSApplication *application = [NSApplication sharedApplication];
|
|
610
|
+
AtomJSApplicationDelegate *delegate = [[AtomJSApplicationDelegate alloc] init];
|
|
611
|
+
application.delegate = delegate;
|
|
506
612
|
[application setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
507
|
-
AtomJSInstallMenu();
|
|
508
|
-
AtomJSConsumeInput();
|
|
509
|
-
|
|
510
|
-
AtomJSEmit(@{
|
|
511
|
-
@"type": @"ready",
|
|
512
|
-
@"pid": @([[NSProcessInfo processInfo] processIdentifier])
|
|
513
|
-
});
|
|
514
|
-
|
|
515
613
|
[application run];
|
|
516
614
|
}
|
|
517
615
|
|