diff options
Diffstat (limited to 'src/app.c')
-rw-r--r-- | src/app.c | 166 |
1 files changed, 145 insertions, 21 deletions
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
30 | #include "gmdocument.h" | 30 | #include "gmdocument.h" |
31 | #include "gmutil.h" | 31 | #include "gmutil.h" |
32 | #include "history.h" | 32 | #include "history.h" |
33 | #include "ipc.h" | ||
33 | #include "ui/certimportwidget.h" | 34 | #include "ui/certimportwidget.h" |
34 | #include "ui/color.h" | 35 | #include "ui/color.h" |
35 | #include "ui/command.h" | 36 | #include "ui/command.h" |
@@ -50,12 +51,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
50 | #include <the_Foundation/process.h> | 51 | #include <the_Foundation/process.h> |
51 | #include <the_Foundation/sortedarray.h> | 52 | #include <the_Foundation/sortedarray.h> |
52 | #include <the_Foundation/time.h> | 53 | #include <the_Foundation/time.h> |
53 | #include <SDL_events.h> | 54 | #include <SDL.h> |
54 | #include <SDL_filesystem.h> | ||
55 | #include <SDL_render.h> | ||
56 | #include <SDL_timer.h> | ||
57 | #include <SDL_video.h> | ||
58 | #include <SDL_version.h> | ||
59 | 55 | ||
60 | #include <stdio.h> | 56 | #include <stdio.h> |
61 | #include <stdarg.h> | 57 | #include <stdarg.h> |
@@ -422,8 +418,116 @@ static uint32_t postAutoReloadCommand_App_(uint32_t interval, void *param) { | |||
422 | return interval; | 418 | return interval; |
423 | } | 419 | } |
424 | 420 | ||
421 | static void terminate_App_(int rc) { | ||
422 | SDL_Quit(); | ||
423 | deinit_Foundation(); | ||
424 | exit(rc); | ||
425 | } | ||
426 | |||
427 | static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance, | ||
428 | const iStringList *openCmds) { | ||
429 | iString *cmds = new_String(); | ||
430 | const iProcessId pid = currentId_Process(); | ||
431 | iConstForEach(CommandLine, i, &d->args) { | ||
432 | if (i.argType == value_CommandLineArgType) { | ||
433 | continue; | ||
434 | } | ||
435 | if (equal_CommandLineConstIterator(&i, "go-home")) { | ||
436 | appendCStr_String(cmds, "navigate.home\n"); | ||
437 | } | ||
438 | else if (equal_CommandLineConstIterator(&i, "new-tab")) { | ||
439 | iCommandLineArg *arg = argument_CommandLineConstIterator(&i); | ||
440 | if (!isEmpty_StringList(&arg->values)) { | ||
441 | appendFormat_String(cmds, "open newtab:1 url:%s\n", | ||
442 | cstr_String(constAt_StringList(&arg->values, 0))); | ||
443 | } | ||
444 | else { | ||
445 | appendCStr_String(cmds, "tabs.new\n"); | ||
446 | } | ||
447 | iRelease(arg); | ||
448 | } | ||
449 | else if (equal_CommandLineConstIterator(&i, "close-tab")) { | ||
450 | appendCStr_String(cmds, "tabs.close\n"); | ||
451 | } | ||
452 | else if (equal_CommandLineConstIterator(&i, "list-tab-urls;L")) { | ||
453 | appendFormat_String(cmds, "ipc.list.urls pid:%d\n", pid); | ||
454 | } | ||
455 | } | ||
456 | if (!isEmpty_StringList(openCmds)) { | ||
457 | append_String(cmds, collect_String(joinCStr_StringList(openCmds, "\n"))); | ||
458 | } | ||
459 | if (isEmpty_String(cmds)) { | ||
460 | /* By default open a new tab. */ | ||
461 | appendCStr_String(cmds, "tabs.new\n"); | ||
462 | } | ||
463 | if (!isEmpty_String(cmds)) { | ||
464 | iString *result = communicate_Ipc(cmds); | ||
465 | if (result) { | ||
466 | puts(cstr_String(result)); | ||
467 | } | ||
468 | delete_String(result); | ||
469 | } | ||
470 | iUnused(instance); | ||
471 | // else { | ||
472 | // printf("Lagrange already running (PID %d)\n", instance); | ||
473 | // } | ||
474 | terminate_App_(0); | ||
475 | } | ||
476 | |||
425 | static void init_App_(iApp *d, int argc, char **argv) { | 477 | static void init_App_(iApp *d, int argc, char **argv) { |
426 | init_CommandLine(&d->args, argc, argv); | 478 | init_CommandLine(&d->args, argc, argv); |
479 | /* Configure the valid command line options. */ { | ||
480 | defineValues_CommandLine(&d->args, "close-tab", 0); | ||
481 | defineValues_CommandLine(&d->args, "echo;E", 0); | ||
482 | defineValues_CommandLine(&d->args, "go-home", 0); | ||
483 | defineValues_CommandLine(&d->args, "help", 0); | ||
484 | defineValues_CommandLine(&d->args, "list-tab-urls;L", 0); | ||
485 | defineValuesN_CommandLine(&d->args, "new-tab", 0, 1); | ||
486 | defineValues_CommandLine(&d->args, "sw", 0); | ||
487 | defineValues_CommandLine(&d->args, "version;V", 0); | ||
488 | } | ||
489 | iStringList *openCmds = new_StringList(); | ||
490 | /* Handle command line options. */ { | ||
491 | if (contains_CommandLine(&d->args, "help")) { | ||
492 | printf("Usage: lagrange [options] [URLs] [paths]\n"); | ||
493 | terminate_App_(0); | ||
494 | } | ||
495 | if (contains_CommandLine(&d->args, "version;V")) { | ||
496 | printf("Lagrange version " LAGRANGE_APP_VERSION "\n"); | ||
497 | terminate_App_(0); | ||
498 | } | ||
499 | /* Check for URLs. */ | ||
500 | iBool newTab = iFalse; | ||
501 | iConstForEach(CommandLine, i, &d->args) { | ||
502 | const iRangecc arg = i.entry; | ||
503 | if (i.argType == value_CommandLineArgType) { | ||
504 | /* URLs and file paths accepted. */ | ||
505 | const iBool isKnownScheme = | ||
506 | startsWithCase_Rangecc(arg, "gemini:") || startsWithCase_Rangecc(arg, "gopher:") || | ||
507 | startsWithCase_Rangecc(arg, "finger:") || startsWithCase_Rangecc(arg, "file:") || | ||
508 | startsWithCase_Rangecc(arg, "data:") || startsWithCase_Rangecc(arg, "about:"); | ||
509 | if (isKnownScheme || fileExistsCStr_FileInfo(cstr_Rangecc(arg))) { | ||
510 | pushBack_StringList( | ||
511 | openCmds, | ||
512 | collectNewFormat_String("open newtab:%d url:%s", | ||
513 | newTab, | ||
514 | isKnownScheme | ||
515 | ? cstr_Rangecc(arg) | ||
516 | : cstrCollect_String(makeFileUrl_String( | ||
517 | collectNewRange_String(arg))))); | ||
518 | newTab = iTrue; | ||
519 | } | ||
520 | else { | ||
521 | fprintf(stderr, "Invalid URL/file: %s\n", cstr_Rangecc(arg)); | ||
522 | terminate_App_(1); | ||
523 | } | ||
524 | } | ||
525 | else if (!isDefined_CommandLine(&d->args, collectNewRange_String(i.entry))) { | ||
526 | fprintf(stderr, "Unknown option: %s\n", cstr_Rangecc(arg)); | ||
527 | terminate_App_(1); | ||
528 | } | ||
529 | } | ||
530 | } | ||
427 | /* Where was the app started from? We ask SDL first because the command line alone is | 531 | /* Where was the app started from? We ask SDL first because the command line alone is |
428 | not a reliable source of this information, particularly when it comes to different | 532 | not a reliable source of this information, particularly when it comes to different |
429 | operating systems. */ { | 533 | operating systems. */ { |
@@ -437,6 +541,17 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
437 | } | 541 | } |
438 | SDL_free(exec); | 542 | SDL_free(exec); |
439 | } | 543 | } |
544 | init_Ipc(dataDir_App_()); | ||
545 | /* Only one instance is allowed to run at a time; the runtime files (bookmarks, etc.) | ||
546 | are not shareable. */ { | ||
547 | const iProcessId instance = check_Ipc(); | ||
548 | if (instance) { | ||
549 | communicateWithRunningInstance_App_(d, instance, openCmds); | ||
550 | terminate_App_(0); | ||
551 | } | ||
552 | listen_Ipc(); /* We'll respond to commands from other instances. */ | ||
553 | } | ||
554 | printf("Lagrange: A Beautiful Gemini Client\n"); | ||
440 | const iBool isFirstRun = | 555 | const iBool isFirstRun = |
441 | !fileExistsCStr_FileInfo(cleanedPath_CStr(concatPath_CStr(dataDir_App_(), "prefs.cfg"))); | 556 | !fileExistsCStr_FileInfo(cleanedPath_CStr(concatPath_CStr(dataDir_App_(), "prefs.cfg"))); |
442 | d->isFinishedLaunching = iFalse; | 557 | d->isFinishedLaunching = iFalse; |
@@ -445,7 +560,7 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
445 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); | 560 | init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_); |
446 | d->lastTickerTime = SDL_GetTicks(); | 561 | d->lastTickerTime = SDL_GetTicks(); |
447 | d->elapsedSinceLastTicker = 0; | 562 | d->elapsedSinceLastTicker = 0; |
448 | d->commandEcho = checkArgument_CommandLine(&d->args, "echo") != NULL; | 563 | d->commandEcho = checkArgument_CommandLine(&d->args, "echo;E") != NULL; |
449 | d->forceSoftwareRender = checkArgument_CommandLine(&d->args, "sw") != NULL; | 564 | d->forceSoftwareRender = checkArgument_CommandLine(&d->args, "sw") != NULL; |
450 | d->initialWindowRect = init_Rect(-1, -1, 900, 560); | 565 | d->initialWindowRect = init_Rect(-1, -1, 900, 560); |
451 | #if defined (iPlatformMsys) | 566 | #if defined (iPlatformMsys) |
@@ -529,21 +644,10 @@ static void init_App_(iApp *d, int argc, char **argv) { | |||
529 | } | 644 | } |
530 | } | 645 | } |
531 | /* URLs from the command line. */ { | 646 | /* URLs from the command line. */ { |
532 | iBool newTab = iFalse; | 647 | iConstForEach(StringList, i, openCmds) { |
533 | for (size_t i = 1; i < size_StringList(args_CommandLine(&d->args)); i++) { | 648 | postCommandString_App(i.value); |
534 | const iString *arg = constAt_StringList(args_CommandLine(&d->args), i); | ||
535 | const iBool isKnownScheme = | ||
536 | startsWithCase_String(arg, "gemini:") || startsWithCase_String(arg, "gopher:") || | ||
537 | startsWithCase_String(arg, "file:") || startsWithCase_String(arg, "data:") || | ||
538 | startsWithCase_String(arg, "about:"); | ||
539 | if (isKnownScheme || fileExists_FileInfo(arg)) { | ||
540 | postCommandf_App("open newtab:%d url:%s", | ||
541 | newTab, | ||
542 | isKnownScheme ? cstr_String(arg) | ||
543 | : cstrCollect_String(makeFileUrl_String(arg))); | ||
544 | newTab = iTrue; | ||
545 | } | ||
546 | } | 649 | } |
650 | iRelease(openCmds); | ||
547 | } | 651 | } |
548 | } | 652 | } |
549 | 653 | ||
@@ -567,6 +671,7 @@ static void deinit_App(iApp *d) { | |||
567 | deinit_CommandLine(&d->args); | 671 | deinit_CommandLine(&d->args); |
568 | iRelease(d->launchCommands); | 672 | iRelease(d->launchCommands); |
569 | delete_String(d->execPath); | 673 | delete_String(d->execPath); |
674 | deinit_Ipc(); | ||
570 | iRecycle(); | 675 | iRecycle(); |
571 | } | 676 | } |
572 | 677 | ||
@@ -954,6 +1059,9 @@ void postRefresh_App(void) { | |||
954 | void postCommand_App(const char *command) { | 1059 | void postCommand_App(const char *command) { |
955 | iApp *d = &app_; | 1060 | iApp *d = &app_; |
956 | iAssert(command); | 1061 | iAssert(command); |
1062 | if (strlen(command) == 0) { | ||
1063 | return; | ||
1064 | } | ||
957 | SDL_Event ev; | 1065 | SDL_Event ev; |
958 | if (*command == '!') { | 1066 | if (*command == '!') { |
959 | /* Global command; this is global context so just ignore. */ | 1067 | /* Global command; this is global context so just ignore. */ |
@@ -1784,6 +1892,22 @@ iBool handleCommand_App(const char *cmd) { | |||
1784 | } | 1892 | } |
1785 | return iFalse; | 1893 | return iFalse; |
1786 | } | 1894 | } |
1895 | else if (equal_Command(cmd, "ipc.list.urls")) { | ||
1896 | iProcessId pid = argLabel_Command(cmd, "pid"); | ||
1897 | if (pid) { | ||
1898 | iString *urls = collectNew_String(); | ||
1899 | iConstForEach(ObjectList, i, iClob(listDocuments_App())) { | ||
1900 | append_String(urls, url_DocumentWidget(i.object)); | ||
1901 | appendCStr_String(urls, "\n"); | ||
1902 | } | ||
1903 | write_Ipc(pid, urls, response_IpcWrite); | ||
1904 | } | ||
1905 | return iTrue; | ||
1906 | } | ||
1907 | else if (equal_Command(cmd, "ipc.signal")) { | ||
1908 | signal_Ipc(arg_Command(cmd)); | ||
1909 | return iTrue; | ||
1910 | } | ||
1787 | else { | 1911 | else { |
1788 | return iFalse; | 1912 | return iFalse; |
1789 | } | 1913 | } |