diff options
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | dillorc | 11 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/actions.c | 65 | ||||
-rw-r--r-- | src/actions.h | 32 | ||||
-rw-r--r-- | src/dillo.cc | 2 | ||||
-rw-r--r-- | src/html.cc | 2 | ||||
-rw-r--r-- | src/menu.cc | 121 | ||||
-rw-r--r-- | src/menu.hh | 3 | ||||
-rw-r--r-- | src/prefs.c | 1 | ||||
-rw-r--r-- | src/prefs.h | 1 | ||||
-rw-r--r-- | src/prefsparser.cc | 1 | ||||
-rw-r--r-- | src/uicmd.cc | 4 | ||||
-rw-r--r-- | src/uicmd.hh | 2 |
14 files changed, 233 insertions, 16 deletions
@@ -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. @@ -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, |