summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-09-11 14:25:03 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-09-11 14:25:03 +0300
commit25456ce008c7fe78378ddaaf784c74209a2d8c14 (patch)
treefbb86324f468ab984ca1be1c25f92872499ed948 /src
parent2fc802367acd63b86fecda92461d63d029d7378c (diff)
macOS: Handling launch URLs and drop'n'drop
Improved drop and drop event handling: multiple dropped files/URLs open in new tabs. The application registers gemini: as a handled URL scheme.
Diffstat (limited to 'src')
-rw-r--r--src/app.c57
-rw-r--r--src/app.h1
-rw-r--r--src/gmrequest.c3
-rw-r--r--src/macos.m53
-rw-r--r--src/main.c2
-rw-r--r--src/ui/window.c7
6 files changed, 110 insertions, 13 deletions
diff --git a/src/app.c b/src/app.c
index 0144c6d7..03e9b036 100644
--- a/src/app.c
+++ b/src/app.c
@@ -88,6 +88,9 @@ struct Impl_App {
88 iBool running; 88 iBool running;
89 iBool pendingRefresh; 89 iBool pendingRefresh;
90 int tabEnum; 90 int tabEnum;
91 iStringList *launchCommands;
92 iBool isFinishedLaunching;
93 iTime lastDropTime; /* for detecting drops of multiple items */
91 /* Preferences: */ 94 /* Preferences: */
92 iBool commandEcho; /* --echo */ 95 iBool commandEcho; /* --echo */
93 iBool retainWindowSize; 96 iBool retainWindowSize;
@@ -283,6 +286,9 @@ static void saveState_App_(const iApp *d) {
283} 286}
284 287
285static void init_App_(iApp *d, int argc, char **argv) { 288static void init_App_(iApp *d, int argc, char **argv) {
289 d->isFinishedLaunching = iFalse;
290 d->launchCommands = new_StringList();
291 iZap(d->lastDropTime);
286 init_CommandLine(&d->args, argc, argv); 292 init_CommandLine(&d->args, argc, argv);
287 init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); 293 init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_);
288 d->lastTickerTime = SDL_GetTicks(); 294 d->lastTickerTime = SDL_GetTicks();
@@ -304,6 +310,9 @@ static void init_App_(iApp *d, int argc, char **argv) {
304 init_String(&d->gopherProxy); 310 init_String(&d->gopherProxy);
305 init_String(&d->httpProxy); 311 init_String(&d->httpProxy);
306 setThemePalette_Color(d->theme); 312 setThemePalette_Color(d->theme);
313#if defined (iPlatformApple)
314 setupApplication_MacOS();
315#endif
307 loadPrefs_App_(d); 316 loadPrefs_App_(d);
308 load_Visited(d->visited, dataDir_App_); 317 load_Visited(d->visited, dataDir_App_);
309 load_Bookmarks(d->bookmarks, dataDir_App_); 318 load_Bookmarks(d->bookmarks, dataDir_App_);
@@ -324,6 +333,12 @@ static void init_App_(iApp *d, int argc, char **argv) {
324 postCommand_App("navigate.home"); 333 postCommand_App("navigate.home");
325 } 334 }
326 postCommand_App("window.unfreeze"); 335 postCommand_App("window.unfreeze");
336 d->isFinishedLaunching = iTrue;
337 /* Run any commands that were pending completion of launch. */ {
338 iForEach(StringList, i, d->launchCommands) {
339 postCommandString_App(i.value);
340 }
341 }
327} 342}
328 343
329static void deinit_App(iApp *d) { 344static void deinit_App(iApp *d) {
@@ -340,6 +355,7 @@ static void deinit_App(iApp *d) {
340 delete_Window(d->window); 355 delete_Window(d->window);
341 d->window = NULL; 356 d->window = NULL;
342 deinit_CommandLine(&d->args); 357 deinit_CommandLine(&d->args);
358 iRelease(d->launchCommands);
343} 359}
344 360
345const iString *execPath_App(void) { 361const iString *execPath_App(void) {
@@ -350,6 +366,21 @@ const iString *dataDir_App(void) {
350 return collect_String(cleanedCStr_Path(dataDir_App_)); 366 return collect_String(cleanedCStr_Path(dataDir_App_));
351} 367}
352 368
369const iString *debugInfo_App(void) {
370 iApp *d = &app_;
371 iString *msg = collectNew_String();
372 format_String(msg, "# Debug information\n");
373 appendFormat_String(msg, "## Launch arguments\n");
374 iConstForEach(StringList, i, args_CommandLine(&d->args)) {
375 appendFormat_String(msg, "* %zu: %s\n", i.pos, cstr_String(i.value));
376 }
377 appendFormat_String(msg, "## Launch commands\n");
378 iConstForEach(StringList, j, d->launchCommands) {
379 appendFormat_String(msg, "%s\n", cstr_String(j.value));
380 }
381 return msg;
382}
383
353iLocalDef iBool isWaitingAllowed_App_(const iApp *d) { 384iLocalDef iBool isWaitingAllowed_App_(const iApp *d) {
354 return !d->pendingRefresh && isEmpty_SortedArray(&d->tickers); 385 return !d->pendingRefresh && isEmpty_SortedArray(&d->tickers);
355} 386}
@@ -365,9 +396,22 @@ void processEvents_App(enum iAppEventMode eventMode) {
365 case SDL_QUIT: 396 case SDL_QUIT:
366 d->running = iFalse; 397 d->running = iFalse;
367 goto backToMainLoop; 398 goto backToMainLoop;
368 case SDL_DROPFILE: 399 case SDL_DROPFILE: {
369 postCommandf_App("open url:file://%s", ev.drop.file); 400 iBool newTab = iFalse;
401 if (elapsedSeconds_Time(&d->lastDropTime) < 0.1) {
402 /* Each additional drop gets a new tab. */
403 newTab = iTrue;
404 }
405 d->lastDropTime = now_Time();
406 if (startsWithCase_CStr(ev.drop.file, "gemini:") ||
407 startsWithCase_CStr(ev.drop.file, "file:")) {
408 postCommandf_App("~open newtab:%d url:%s", newTab, ev.drop.file);
409 }
410 else {
411 postCommandf_App("~open newtab:%d url:file://%s", newTab, ev.drop.file);
412 }
370 break; 413 break;
414 }
371 default: { 415 default: {
372 iBool wasUsed = processEvent_Window(d->window, &ev); 416 iBool wasUsed = processEvent_Window(d->window, &ev);
373 if (ev.type == SDL_USEREVENT && ev.user.code == command_UserEventCode) { 417 if (ev.type == SDL_USEREVENT && ev.user.code == command_UserEventCode) {
@@ -488,12 +532,21 @@ void postRefresh_App(void) {
488} 532}
489 533
490void postCommand_App(const char *command) { 534void postCommand_App(const char *command) {
535 iApp *d = &app_;
491 iAssert(command); 536 iAssert(command);
492 SDL_Event ev; 537 SDL_Event ev;
493 if (*command == '!') { 538 if (*command == '!') {
494 /* Global command; this is global context so just ignore. */ 539 /* Global command; this is global context so just ignore. */
495 command++; 540 command++;
496 } 541 }
542 if (*command == '~') {
543 /* Requires launch to be finished; defer it if needed. */
544 command++;
545 if (!d->isFinishedLaunching) {
546 pushBackCStr_StringList(d->launchCommands, command);
547 return;
548 }
549 }
497 ev.user.type = SDL_USEREVENT; 550 ev.user.type = SDL_USEREVENT;
498 ev.user.code = command_UserEventCode; 551 ev.user.code = command_UserEventCode;
499 ev.user.windowID = get_Window() ? SDL_GetWindowID(get_Window()->win) : 0; 552 ev.user.windowID = get_Window() ? SDL_GetWindowID(get_Window()->win) : 0;
diff --git a/src/app.h b/src/app.h
index da86c37e..69b8d274 100644
--- a/src/app.h
+++ b/src/app.h
@@ -48,6 +48,7 @@ enum iUserEventCode {
48 48
49const iString *execPath_App (void); 49const iString *execPath_App (void);
50const iString *dataDir_App (void); 50const iString *dataDir_App (void);
51const iString *debugInfo_App (void);
51 52
52int run_App (int argc, char **argv); 53int run_App (int argc, char **argv);
53void processEvents_App (enum iAppEventMode mode); 54void processEvents_App (enum iAppEventMode mode);
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 137e8303..843d2f46 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -308,6 +308,9 @@ static const iBlock *aboutPageSource_(iRangecc path) {
308 if (equalCase_Rangecc(path, "version")) { 308 if (equalCase_Rangecc(path, "version")) {
309 return &blobVersion_Embedded; 309 return &blobVersion_Embedded;
310 } 310 }
311 if (equalCase_Rangecc(path, "debug")) {
312 return utf8_String(debugInfo_App());
313 }
311 return src; 314 return src;
312} 315}
313 316
diff --git a/src/macos.m b/src/macos.m
index d44be7d0..bbbaaa19 100644
--- a/src/macos.m
+++ b/src/macos.m
@@ -121,8 +121,9 @@ enum iTouchBarVariant {
121} 121}
122- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl; 122- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl;
123//- (NSTouchBar *)makeTouchBar; 123//- (NSTouchBar *)makeTouchBar;
124/* SDL needs to do its own thing. */ 124- (BOOL)application:(NSApplication *)app openFile:(NSString *)filename;
125- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; 125- (void)application:(NSApplication *)app openFiles:(NSArray<NSString *> *)filenames;
126- (void)application:(NSApplication *)app openURLs:(NSArray<NSURL *> *)urls;
126- (void)applicationDidFinishLaunching:(NSNotification *)notifications; 127- (void)applicationDidFinishLaunching:(NSNotification *)notifications;
127@end 128@end
128 129
@@ -151,7 +152,7 @@ enum iTouchBarVariant {
151static void appearanceChanged_MacOS_(NSString *name) { 152static void appearanceChanged_MacOS_(NSString *name) {
152 const iBool isDark = [name containsString:@"Dark"]; 153 const iBool isDark = [name containsString:@"Dark"];
153 const iBool isHighContrast = [name containsString:@"HighContrast"]; 154 const iBool isHighContrast = [name containsString:@"HighContrast"];
154 postCommandf_App("os.theme.changed dark:%d contrast:%d", isDark ? 1 : 0, isHighContrast ? 1 : 0); 155 postCommandf_App("~os.theme.changed dark:%d contrast:%d", isDark ? 1 : 0, isHighContrast ? 1 : 0);
155// printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]); 156// printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]);
156// fflush(stdout); 157// fflush(stdout);
157} 158}
@@ -170,8 +171,23 @@ static void appearanceChanged_MacOS_(NSString *name) {
170 [menuCommands setObject:command forKey:[menuItem title]]; 171 [menuCommands setObject:command forKey:[menuItem title]];
171} 172}
172 173
173- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { 174- (BOOL)application:(NSApplication *)app openFile:(NSString *)filename {
174 return [sdlDelegate application:theApplication openFile:filename]; 175 return [sdlDelegate application:app openFile:filename];
176}
177
178- (void)application:(NSApplication *)app openFiles:(NSArray<NSString *> *)filenames {
179 /* TODO: According to AppKit docs, this method won't be called when openURLs is defined. */
180 for (NSString *fn in filenames) {
181 NSLog(@"openFiles: %@", fn);
182 [self application:app openFile:fn];
183 }
184}
185
186- (void)application:(NSApplication *)app openURLs:(NSArray<NSURL *> *)urls {
187 for (NSURL *url in urls) {
188 NSLog(@"openURLs: %@", [url absoluteString]);
189 [sdlDelegate application:app openFile:[url absoluteString]];
190 }
175} 191}
176 192
177- (void)applicationDidFinishLaunching:(NSNotification *)notification { 193- (void)applicationDidFinishLaunching:(NSNotification *)notification {
@@ -397,9 +413,30 @@ void enableMomentumScroll_MacOS(void) {
397 forKey: @"AppleMomentumScrollSupported"]; 413 forKey: @"AppleMomentumScrollSupported"];
398} 414}
399 415
416@interface UrlHandler : NSObject
417- (void)handleURLEvent:(NSAppleEventDescriptor*)event
418 withReplyEvent:(NSAppleEventDescriptor*)replyEvent;
419@end
420
421@implementation UrlHandler
422- (void)handleURLEvent:(NSAppleEventDescriptor*)event
423 withReplyEvent:(NSAppleEventDescriptor*)replyEvent {
424 NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
425 postCommandf_App("~open url:%s", [url cStringUsingEncoding:NSUTF8StringEncoding]);
426}
427@end
428
429void registerURLHandler_MacOS(void) {
430 UrlHandler *handler = [[UrlHandler alloc] init];
431 [[NSAppleEventManager sharedAppleEventManager]
432 setEventHandler:handler
433 andSelector:@selector(handleURLEvent:withReplyEvent:)
434 forEventClass:kInternetEventClass
435 andEventID:kAEGetURL];
436}
437
400void setupApplication_MacOS(void) { 438void setupApplication_MacOS(void) {
401 NSApplication *app = [NSApplication sharedApplication]; 439 NSApplication *app = [NSApplication sharedApplication];
402 //appearanceChanged_MacOS_([[app effectiveAppearance] name]);
403 /* Our delegate will override SDL's delegate. */ 440 /* Our delegate will override SDL's delegate. */
404 MyDelegate *myDel = [[MyDelegate alloc] initWithSDLDelegate:app.delegate]; 441 MyDelegate *myDel = [[MyDelegate alloc] initWithSDLDelegate:app.delegate];
405 [myDel setAppearance:[[app effectiveAppearance] name]]; 442 [myDel setAppearance:[[app effectiveAppearance] name]];
@@ -502,3 +539,7 @@ void handleCommand_MacOS(const char *cmd) {
502 } 539 }
503#endif 540#endif
504} 541}
542
543void log_MacOS(const char *msg) {
544 NSLog(@"%s", msg);
545}
diff --git a/src/main.c b/src/main.c
index c13b75d1..247c4b40 100644
--- a/src/main.c
+++ b/src/main.c
@@ -31,11 +31,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
31 31
32#if defined (iPlatformApple) 32#if defined (iPlatformApple)
33extern void enableMomentumScroll_MacOS(void); 33extern void enableMomentumScroll_MacOS(void);
34extern void registerURLHandler_MacOS(void);
34#endif 35#endif
35 36
36int main(int argc, char **argv) { 37int main(int argc, char **argv) {
37#if defined (iPlatformApple) 38#if defined (iPlatformApple)
38 enableMomentumScroll_MacOS(); 39 enableMomentumScroll_MacOS();
40 registerURLHandler_MacOS();
39#endif 41#endif
40#if defined (iPlatformMsys) 42#if defined (iPlatformMsys)
41 /* MSYS runtime takes care of WinMain. */ 43 /* MSYS runtime takes care of WinMain. */
diff --git a/src/ui/window.c b/src/ui/window.c
index 87ca2614..0a63a941 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -138,8 +138,8 @@ static const iMenuItem viewMenuItems[] = {
138 { "Show Page Outline", '4', KMOD_PRIMARY, "sidebar.mode arg:3 toggle:1" }, 138 { "Show Page Outline", '4', KMOD_PRIMARY, "sidebar.mode arg:3 toggle:1" },
139 { "Toggle Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" }, 139 { "Toggle Sidebar", SDLK_l, KMOD_PRIMARY | KMOD_SHIFT, "sidebar.toggle" },
140 { "---", 0, 0, NULL }, 140 { "---", 0, 0, NULL },
141 { "Go Back", navigateBack_KeyShortcut, "navigate.back" }, 141 { "Go Back", SDLK_LEFTBRACKET, KMOD_PRIMARY, "navigate.back" },
142 { "Go Forward", navigateForward_KeyShortcut, "navigate.forward" }, 142 { "Go Forward", SDLK_RIGHTBRACKET, KMOD_PRIMARY, "navigate.forward" },
143 { "Reload Page", reload_KeyShortcut, "navigate.reload" }, 143 { "Reload Page", reload_KeyShortcut, "navigate.reload" },
144 { "---", 0, 0, NULL }, 144 { "---", 0, 0, NULL },
145 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" }, 145 { "Zoom In", SDLK_EQUALS, KMOD_PRIMARY, "zoom.delta arg:10" },
@@ -546,9 +546,6 @@ void init_Window(iWindow *d, iRect rect) {
546 d->presentTime = 0.0; 546 d->presentTime = 0.0;
547 setId_Widget(d->root, "root"); 547 setId_Widget(d->root, "root");
548 init_Text(d->render); 548 init_Text(d->render);
549#if defined (iPlatformApple) && !defined (iPlatformIOS)
550 setupApplication_MacOS();
551#endif
552 setupUserInterface_Window(d); 549 setupUserInterface_Window(d);
553 updateRootSize_Window_(d); 550 updateRootSize_Window_(d);
554} 551}