summaryrefslogtreecommitdiff
path: root/src/app.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-02-28 18:07:04 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-02-28 18:07:04 +0200
commitb97462993b00ecc5cd04051b4fc2abd164fefb04 (patch)
tree6575f4c47b658a69c7da60e72d1f748e866e0aaf /src/app.c
parent2583e020e7f1f1036f6df8f5b93668e7a0187d60 (diff)
Single app instance; IPC mechanism
Only one instance of Lagrange is allowed to run per a runtime directory. Otherwise the instances will overwrite each others' files. Added a check at startup to see if an instance is already running. If so, options can be used to communicate with it. By default, a new tab is opened in the running instance. IssueID #37 IssueID #164 IssueID #174 IssueID #178
Diffstat (limited to 'src/app.c')
-rw-r--r--src/app.c166
1 files changed, 145 insertions, 21 deletions
diff --git a/src/app.c b/src/app.c
index b76c4aaa..8a02ec3f 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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
421static void terminate_App_(int rc) {
422 SDL_Quit();
423 deinit_Foundation();
424 exit(rc);
425}
426
427static 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
425static void init_App_(iApp *d, int argc, char **argv) { 477static 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) {
954void postCommand_App(const char *command) { 1059void 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 }