aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRodrigo Arias Mallo <rodarima@gmail.com>2024-06-14 22:27:18 +0200
committerRodrigo Arias Mallo <rodarima@gmail.com>2024-12-17 23:10:59 +0100
commit140d9ebd912ed803dd916c6ab3783a79a2a724e3 (patch)
tree04d1ec36e60f94adc63b79491fc8d05e7bd2c9f4
parent0f49ee5f746592352b6ac26449e87339cc3702a8 (diff)
Add support for user actions in the link menu
Allows the user to define additional entries in the link menu which will execute the given program/script. Each actions is defined using the "link_action" option. The link URL is stored in the $url enviroment variable and the current page in $origin, so the user can customize how do the handling. Here is a simple example to add three new entries: link_action="Debug variables:echo url=$url origin=$origin" link_action="Open in MPV:mpv $url" link_action="Open in Firefox:firefox $url" The command is spawned in a forked process using the system() call, which uses the shell to expand any variable. In particular, the $url variable is set to the current URL being opened. Fixes: https://github.com/dillo-browser/dillo/issues/3
-rw-r--r--ChangeLog2
-rw-r--r--dillorc11
-rw-r--r--src/Makefile.am2
-rw-r--r--src/actions.c65
-rw-r--r--src/actions.h32
-rw-r--r--src/dillo.cc2
-rw-r--r--src/html.cc2
-rw-r--r--src/menu.cc121
-rw-r--r--src/menu.hh3
-rw-r--r--src/prefs.c1
-rw-r--r--src/prefs.h1
-rw-r--r--src/prefsparser.cc1
-rw-r--r--src/uicmd.cc4
-rw-r--r--src/uicmd.hh2
14 files changed, 233 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index 4f1d23ac..89348d3c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -29,6 +29,8 @@ dillo-3.2.0 [Not released yet]
- Reload current page on SIGUSR1 signal
- Print library versions and enabled features with dillo -v.
- Allow image formats to be ignored with the "ignore_image_formats" option.
+ - Add the "link_action" option to define custom menu entries to open links
+ with external programs or scripts.
Patches: Rodrigo Arias Mallo
+- Add primitive support for SVG using the nanosvg.h library.
- Add support for ch, rem, vw, vh, vmin and vmax CSS units.
diff --git a/dillorc b/dillorc
index 88afe11a..484185c2 100644
--- a/dillorc
+++ b/dillorc
@@ -64,6 +64,17 @@
# jump to a given position.
#scrollbar_page_mode=NO
+# Define custom actions for the link menu. The format is <label>:<cmd>. The
+# command will be executed in the system shell using the system() call. You can
+# implement your own handling logic in a script or program. The following
+# environment variables are set:
+# $url: URL being opened
+# $origin: URL of the current document
+# Examples:
+# link_action="Debug variables:echo url=$url origin=$origin"
+# link_action="Open in MPV:mpv $url"
+# link_action="Open in Firefox:firefox $url"
+
#-------------------------------------------------------------------------
# RENDERING SECTION
#-------------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index b2c7e28a..225a364a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,6 +39,8 @@ dillo_SOURCES = \
bw.c \
cookies.c \
cookies.h \
+ actions.c \
+ actions.h \
hsts.c \
hsts.h \
auth.c \
diff --git a/src/actions.c b/src/actions.c
new file mode 100644
index 00000000..7e197615
--- /dev/null
+++ b/src/actions.c
@@ -0,0 +1,65 @@
+/*
+ * File: actions.c
+ *
+ * Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "actions.h"
+#include "msg.h"
+#include "../dlib/dlib.h"
+#include <errno.h>
+
+static Dlist *link_actions = NULL;
+
+void
+action_parse(char *line)
+{
+ char *label = strtok(line, ":");
+
+ if (label == NULL || strlen(label) == 0) {
+ MSG("Missing action label, ignoring '%s'\n", line);
+ return;
+ }
+
+ //MSG("Got label='%s'\n", label);
+
+ char *cmd = strtok(NULL, "");
+
+ if (cmd == NULL || strlen(cmd) == 0) {
+ MSG("Missing action command, ignoring '%s'\n", line);
+ return;
+ }
+
+ //MSG("Got action label='%s' cmd='%s'\n", label, cmd);
+
+ Action *action = dMalloc(sizeof(Action));
+ action->label = dStrdup(label);
+ action->cmd = dStrdup(cmd);
+
+ dList_append(link_actions, action);
+}
+
+void
+a_Actions_init(void)
+{
+ int n = dList_length(prefs.link_actions);
+
+ link_actions = dList_new(n);
+
+ for (int i = 0; i < n; i++) {
+ char *line = dList_nth_data(prefs.link_actions, i);
+ if (line)
+ action_parse(line);
+ }
+}
+
+Dlist *
+a_Actions_link_get(void)
+{
+ return link_actions;
+}
diff --git a/src/actions.h b/src/actions.h
new file mode 100644
index 00000000..b5319bea
--- /dev/null
+++ b/src/actions.h
@@ -0,0 +1,32 @@
+/*
+ * File: actions.h
+ *
+ * Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef ACTIONS_H
+#define ACTIONS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "dlib/dlib.h"
+
+typedef struct {
+ char *label;
+ char *cmd;
+} Action;
+
+void a_Actions_init(void);
+Dlist *a_Actions_link_get(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* ACTIONS_H*/
diff --git a/src/dillo.cc b/src/dillo.cc
index 2a65e12c..405f4c3e 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -57,6 +57,7 @@
#include "capi.h"
#include "dicache.h"
#include "cookies.h"
+#include "actions.h"
#include "hsts.h"
#include "domain.h"
#include "auth.h"
@@ -489,6 +490,7 @@ int main(int argc, char **argv)
a_Dicache_init();
a_Bw_init();
a_Cookies_init();
+ a_Actions_init();
a_Hsts_init(Paths::getPrefsFP(PATHS_HSTS_PRELOAD));
a_Auth_init();
a_UIcmd_init();
diff --git a/src/html.cc b/src/html.cc
index 1c5a08f4..69b495d6 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -780,7 +780,7 @@ bool DilloHtml::HtmlLinkReceiver::press (Widget *widget, int link, int img,
a_UIcmd_page_popup(bw, bw->num_page_bugs != 0, html->cssUrls);
ret = true;
} else {
- a_UIcmd_link_popup(bw, html->links->get(link));
+ a_UIcmd_link_popup(bw, html->links->get(link), html->page_url);
ret = true;
}
}
diff --git a/src/menu.cc b/src/menu.cc
index 7cea24a7..3cdc2f77 100644
--- a/src/menu.cc
+++ b/src/menu.cc
@@ -18,9 +18,12 @@
#include <FL/Fl.H>
#include <FL/Fl_Menu_Item.H>
+#include <unistd.h>
+#include <errno.h>
#include "lout/misc.hh" /* SimpleVector */
#include "msg.h"
#include "menu.hh"
+#include "actions.h"
#include "uicmd.hh"
#include "history.h"
#include "html.hh"
@@ -430,30 +433,123 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
a_Timeout_add(0.0, Menu_popup_cb, (void*)&page_data);
}
-static Fl_Menu_Item link_menu[] = {
+static Fl_Menu_Item link_menu_[] = {
{"Open link in new tab", 0, Menu_open_url_nt_cb,0,0,0,0,0,0},
{"Open link in new window", 0, Menu_open_url_nw_cb,0,FL_MENU_DIVIDER,0,0,
0,0},
{"Bookmark this link", 0, Menu_add_bookmark_cb,0,0,0,0,0,0},
{"Copy link location", 0, Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0},
- {"Save link as...", 0, Menu_save_link_cb,0,0,0,0,0,0},
+ {"Save link as...", 0, Menu_save_link_cb,0,FL_MENU_DIVIDER,0,0,0,0},
{0,0,0,0,0,0,0,0,0}
};
-static void Menu_set_link_menu_user_data(void *user_data)
+/* As we can only provide a pointer to the link menu items, we need to
+ * create an auxiliary structure to hold the current URL and the program
+ * that should run on each item. */
+struct link_menu_item {
+ const DilloUrl *url;
+ const DilloUrl *origin;
+ Action *action;
+};
+
+/**
+ * Open URL following a custom action
+ */
+static void Menu_open_url_action_cb(Fl_Widget*, void *user_data)
+{
+ /* Don't use popup_url because it is used for the image URL when coming from
+ * the image menu. We should get rid of the global variables and pass them
+ * via the user_data. */
+
+ struct link_menu_item *mitem = (struct link_menu_item *) user_data;
+ const DilloUrl *url = mitem->url;
+ const DilloUrl *origin = mitem->origin;
+ Action *action = mitem->action;
+
+ /* Set the environment variables */
+ setenv("url", URL_STR(url), 1);
+ setenv("origin", URL_STR(origin), 1);
+
+ if (fork() == 0) {
+ /* Child */
+ errno = 0;
+ int ret = system(action->cmd);
+ if (ret == -1) {
+ MSG("Cannot run '%s': %s\n", action->cmd, strerror(errno));
+ exit(1);
+ } else if (ret != 0) {
+ MSG("Command exited with '%d': %s\n", ret, action->cmd);
+ exit(1);
+ } else {
+ /* All good, terminate the child */
+ exit(0);
+ }
+ }
+}
+
+static Fl_Menu_Item *get_link_menu(void)
{
- int i;
+ static Fl_Menu_Item *link_menu = NULL;
+ static struct link_menu_item *link_menu_item = NULL;
+
+ /* Already initialized */
+ if (link_menu != NULL)
+ return link_menu;
- for (i = 0; link_menu[i].label(); i++)
- link_menu[i].user_data(user_data);
+ Dlist *actions = a_Actions_link_get();
+ int nactions = dList_length(actions);
+
+ /* Count static menu entries */
+ int nstatic = 0;
+ while (link_menu_[nstatic].text)
+ nstatic++;
+
+ int ntotal = nstatic + nactions;
+ link_menu = (Fl_Menu_Item *) calloc(ntotal + 1, sizeof(Fl_Menu_Item));
+ link_menu_item = (struct link_menu_item *) calloc(nactions, sizeof(struct link_menu_item));
+
+ /* Just copy the static entries */
+ for (int i = 0; i < nstatic; i++) {
+ memcpy(&link_menu[i], &link_menu_[i], sizeof(Fl_Menu_Item));
+ }
+
+ /* And append the dynamic ones */
+ for (int i = 0; i < nactions; i++) {
+ Action *action = (Action *) dList_nth_data(actions, i);
+ struct link_menu_item *mitem = &link_menu_item[i];
+ mitem->url = NULL; /* Not known yet */
+ mitem->action = action;
+
+ Fl_Menu_Item *item = &link_menu[nstatic + i];
+ item->text = action->label;
+ item->callback_ = Menu_open_url_action_cb;
+ item->user_data_ = mitem;
+ }
+
+ return link_menu;
+}
+
+static void Menu_set_link_menu_user_data(const DilloUrl *url, const DilloUrl *page_url)
+{
+ Fl_Menu_Item *link_menu = get_link_menu();
+ for (int i = 0; link_menu[i].label(); i++) {
+ if (link_menu[i].callback_ == Menu_open_url_action_cb) {
+ struct link_menu_item *mitem = (struct link_menu_item *) link_menu[i].user_data_;
+ /* Set the url and origin */
+ mitem->url = url;
+ mitem->origin = page_url;
+ } else {
+ link_menu[i].user_data_ = (void *) url;
+ }
+ }
}
/**
* Link popup menu (construction & popup)
*/
-void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
+void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url, const DilloUrl *page_url)
{
- static Menu_popup_data_t link_data = {"Link menu", NULL, link_menu};
+ static Menu_popup_data_t link_data = {"Link menu", NULL, NULL};
popup_x = Fl::event_x();
popup_y = Fl::event_y();
@@ -461,7 +557,10 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
a_Url_free(popup_url);
popup_url = a_Url_dup(url);
- Menu_set_link_menu_user_data(popup_url);
+ Fl_Menu_Item *link_menu = get_link_menu();
+ link_data.menu = link_menu;
+
+ Menu_set_link_menu_user_data(popup_url, page_url);
a_Timeout_add(0.0, Menu_popup_cb, (void*)&link_data);
}
@@ -484,7 +583,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
{"Bookmark this image", 0, Menu_add_bookmark_cb,0,0,0,0,0,0},
{"Copy image location", 0,Menu_copy_urlstr_cb,0,FL_MENU_DIVIDER,0,0,0,0},
{"Save image as...", 0, Menu_save_link_cb, 0, FL_MENU_DIVIDER,0,0,0,0},
- {"Link menu", 0, Menu_nop_cb, link_menu, FL_SUBMENU_POINTER,0,0,0,0},
+ {"Link menu", 0, Menu_nop_cb, get_link_menu(), FL_SUBMENU_POINTER,0,0,0,0},
{0,0,0,0,0,0,0,0,0}
};
static Menu_popup_data_t image_data = {"Image menu", NULL, pm};
@@ -517,7 +616,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
if (link_url) {
pm[7].activate();
- Menu_set_link_menu_user_data(popup_link_url);
+ Menu_set_link_menu_user_data(popup_link_url, popup_page_url);
} else {
pm[7].deactivate();
}
diff --git a/src/menu.hh b/src/menu.hh
index a8170e89..60d230a4 100644
--- a/src/menu.hh
+++ b/src/menu.hh
@@ -9,7 +9,8 @@ extern "C" {
void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
bool_t has_bugs, void *v_cssUrls);
-void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url);
+void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *page_url);
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
bool_t loaded_img, DilloUrl *page_url,
DilloUrl *link_url);
diff --git a/src/prefs.c b/src/prefs.c
index 4a68e4c9..a57c792c 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -85,6 +85,7 @@ void a_Prefs_init(void)
prefs.scroll_switches_tabs = TRUE;
prefs.scroll_switches_tabs_reverse = FALSE;
prefs.no_proxy = dStrdup(PREFS_NO_PROXY);
+ prefs.link_actions = dList_new(16);
prefs.panel_size = P_medium;
prefs.parse_embedded_css=TRUE;
prefs.save_dir = dStrdup(PREFS_SAVE_DIR);
diff --git a/src/prefs.h b/src/prefs.h
index 03bc1e56..0027e53c 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -124,6 +124,7 @@ typedef struct {
int penalty_hyphen, penalty_hyphen_2;
int penalty_em_dash_left, penalty_em_dash_right, penalty_em_dash_right_2;
int stretchability_factor;
+ Dlist *link_actions;
} DilloPrefs;
/** Global Data */
diff --git a/src/prefsparser.cc b/src/prefsparser.cc
index ee1d9d02..b5ab1b17 100644
--- a/src/prefsparser.cc
+++ b/src/prefsparser.cc
@@ -248,6 +248,7 @@ void PrefsParser::parse(FILE *fp)
PREFS_FRACTION_100, 0 },
{ "stretchability_factor", &prefs.stretchability_factor,
PREFS_FRACTION_100, 0 },
+ { "link_action", &prefs.link_actions, PREFS_STRINGS, 0 },
{ "zoom_factor", &prefs.zoom_factor, PREFS_DOUBLE, 0 }
};
// changing the LC_NUMERIC locale (temporarily) to C
diff --git a/src/uicmd.cc b/src/uicmd.cc
index 18ac2ff1..273bcb9f 100644
--- a/src/uicmd.cc
+++ b/src/uicmd.cc
@@ -1269,9 +1269,9 @@ void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls)
/*
* Popup the link menu
*/
-void a_UIcmd_link_popup(void *vbw, const DilloUrl *url)
+void a_UIcmd_link_popup(void *vbw, const DilloUrl *url, const DilloUrl *page_url)
{
- a_Menu_link_popup((BrowserWindow*)vbw, url);
+ a_Menu_link_popup((BrowserWindow*)vbw, url, page_url);
}
/*
diff --git a/src/uicmd.hh b/src/uicmd.hh
index f75d9a48..fe72486a 100644
--- a/src/uicmd.hh
+++ b/src/uicmd.hh
@@ -59,7 +59,7 @@ void a_UIcmd_findbar_toggle(BrowserWindow *bw, int on);
void a_UIcmd_focus_main_area(BrowserWindow *bw);
void a_UIcmd_focus_location(void *vbw);
void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls);
-void a_UIcmd_link_popup(void *vbw, const DilloUrl *url);
+void a_UIcmd_link_popup(void *vbw, const DilloUrl *url, const DilloUrl *page_url);
void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
DilloUrl *page_url, DilloUrl *link_url);
void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,