This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch fix-modern-macos
in repository efl.
View the commit online.
commit c3b2aeab7784900fda1ac320042f70de8c1f7894
Author: Cedric BAIL <[email protected]>
AuthorDate: Wed Apr 29 17:16:26 2026 -0600
feat(elementary): add elm_open_url/_file/_email portable helpers
Three small wrappers around the OS's 'open this externally' primitive:
- Linux/BSD: xdg-open / xdg-email
- macOS: open
- Windows: ShellExecuteW with 'open' verb
Lets apps replace #ifdef __APPLE__ snprintf+ecore_exe_run boilerplate
with a single portable call. Existing user customization (apps with
their own configurable helper field) layers on top: pass-through if the
custom helper is set, fall back to elm_open_* otherwise.
---
src/lib/elementary/Elementary.h | 1 +
src/lib/elementary/elm_open.c | 166 ++++++++++++++++++++++++++++++++++++++++
src/lib/elementary/elm_open.h | 49 ++++++++++++
src/lib/elementary/meson.build | 6 +-
4 files changed, 220 insertions(+), 2 deletions(-)
diff --git a/src/lib/elementary/Elementary.h b/src/lib/elementary/Elementary.h
index a64406de43..e3e409598e 100644
--- a/src/lib/elementary/Elementary.h
+++ b/src/lib/elementary/Elementary.h
@@ -242,6 +242,7 @@ EAPI extern Elm_Version *elm_version;
#include <elm_need.h>
#include <elm_notify.h>
#include <elm_object.h>
+#include <elm_open.h>
#include <elm_panel.h>
#include <elm_panes.h>
diff --git a/src/lib/elementary/elm_open.c b/src/lib/elementary/elm_open.c
new file mode 100644
index 0000000000..ca40406387
--- /dev/null
+++ b/src/lib/elementary/elm_open.c
@@ -0,0 +1,166 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
+#include <Elementary.h>
+#include "elm_priv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# include <shellapi.h>
+#endif
+
+#ifndef _WIN32
+/* POSIX single-quote shell escaping. Wraps the argument in single quotes
+ * and escapes any embedded single-quote via the standard '"'"' dance.
+ *
+ * Returns a newly malloc'd string. Caller frees. Returns NULL on
+ * allocation failure.
+ *
+ * Single-quoted strings in POSIX sh are immune to ALL metacharacters
+ * (backtick, $, \, newline, etc.) — the only character that cannot
+ * appear literally is ' itself, which is why we close-quote, insert
+ * an escaped quote, and re-open. */
+static char *
+_shell_quote(const char *arg)
+{
+ const char *p;
+ char *quoted, *q;
+ size_t quoted_len;
+
+ /* Size the output:
+ * 2 chars for outer quotes
+ * each ' in arg expands to 5 chars: '"'"' (close-SQ, open-DQ,
+ * literal SQ, close-DQ, open-SQ)
+ * all other chars are 1 char each
+ * +1 for trailing NUL
+ */
+ quoted_len = 2;
+ for (p = arg; *p; p++)
+ quoted_len += (*p == '\'') ? 5 : 1;
+
+ quoted = malloc(quoted_len + 1);
+ if (!quoted) return NULL;
+
+ q = quoted;
+ *q++ = '\'';
+ for (p = arg; *p; p++)
+ {
+ if (*p == '\'')
+ {
+ /* Close current quote, insert escaped quote, reopen */
+ *q++ = '\'';
+ *q++ = '"';
+ *q++ = '\'';
+ *q++ = '"';
+ *q++ = '\'';
+ }
+ else
+ *q++ = *p;
+ }
+ *q++ = '\'';
+ *q = '\0';
+ return quoted;
+}
+
+static Eina_Bool
+_run_helper(const char *cmd, const char *arg)
+{
+ char *quoted = NULL;
+ char *line = NULL;
+ Eina_Bool ok = EINA_FALSE;
+
+ if (!arg || !arg[0]) return EINA_FALSE;
+
+ quoted = _shell_quote(arg);
+ if (!quoted) return EINA_FALSE;
+
+ if (asprintf(&line, "%s %s", cmd, quoted) < 0)
+ {
+ free(quoted);
+ return EINA_FALSE;
+ }
+
+ ok = (ecore_exe_run(line, NULL) != NULL);
+
+ free(line);
+ free(quoted);
+ return ok;
+}
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+static Eina_Bool
+_win_shellexecute(const wchar_t *verb, const char *arg_utf8)
+{
+ wchar_t *warg;
+ int n;
+ HINSTANCE rc;
+
+ if (!arg_utf8 || !arg_utf8[0]) return EINA_FALSE;
+
+ n = MultiByteToWideChar(CP_UTF8, 0, arg_utf8, -1, NULL, 0);
+ if (n <= 0) return EINA_FALSE;
+ warg = malloc(n * sizeof(wchar_t));
+ if (!warg) return EINA_FALSE;
+ if (MultiByteToWideChar(CP_UTF8, 0, arg_utf8, -1, warg, n) <= 0)
+ {
+ free(warg);
+ return EINA_FALSE;
+ }
+
+ rc = ShellExecuteW(NULL, verb, warg, NULL, NULL, SW_SHOWNORMAL);
+ free(warg);
+ /* ShellExecute returns >32 on success */
+ return ((INT_PTR)rc > 32);
+}
+#endif /* _WIN32 */
+
+EAPI Eina_Bool
+elm_open_url(const char *url)
+{
+#if defined(__APPLE__)
+ return _run_helper("open", url);
+#elif defined(_WIN32)
+ return _win_shellexecute(L"open", url);
+#else
+ return _run_helper("xdg-open", url);
+#endif
+}
+
+EAPI Eina_Bool
+elm_open_file(const char *path)
+{
+ /* On Unix-like platforms, the same command handles both files and URLs. */
+#if defined(_WIN32)
+ return _win_shellexecute(L"open", path);
+#else
+ return elm_open_url(path);
+#endif
+}
+
+EAPI Eina_Bool
+elm_open_email(const char *address)
+{
+ char buf[1024];
+ int n;
+
+ if (!address || !address[0]) return EINA_FALSE;
+
+#if defined(__APPLE__) || defined(_WIN32)
+ n = snprintf(buf, sizeof(buf), "mailto:%s", address);
+ if (n < 0 || n >= (int)sizeof(buf)) return EINA_FALSE;
+#endif
+
+#if defined(__APPLE__)
+ return _run_helper("open", buf);
+#elif defined(_WIN32)
+ return _win_shellexecute(L"open", buf);
+#else
+ (void)buf; (void)n;
+ return _run_helper("xdg-email", address);
+#endif
+}
diff --git a/src/lib/elementary/elm_open.h b/src/lib/elementary/elm_open.h
new file mode 100644
index 0000000000..209806cb30
--- /dev/null
+++ b/src/lib/elementary/elm_open.h
@@ -0,0 +1,49 @@
+/**
+ * @file elm_open.h
+ *
+ * Portable wrappers for opening URLs, files, and email recipients with
+ * the user's default external handler. Hides platform differences:
+ * - Linux/BSD: spawns xdg-open / xdg-email
+ * - macOS: spawns open
+ * - Windows: ShellExecute with verb "open"
+ */
+#ifndef ELM_OPEN_H
+#define ELM_OPEN_H
+
+/**
+ * Opens a URL in the user's default browser.
+ *
+ * @param url Absolute URL (http://, https://, ftp://, ...). UTF-8.
+ * @return EINA_TRUE on successful spawn, EINA_FALSE on failure
+ * (NULL/empty input, no handler available, exec failed).
+ *
+ * @since 1.28
+ * @ingroup Elm_App
+ */
+EAPI Eina_Bool elm_open_url(const char *url);
+
+/**
+ * Opens a local file with the user's default application for that
+ * file's MIME type.
+ *
+ * @param path Local filesystem path (absolute or relative). UTF-8.
+ * @return EINA_TRUE on success, EINA_FALSE on failure.
+ *
+ * @since 1.28
+ * @ingroup Elm_App
+ */
+EAPI Eina_Bool elm_open_file(const char *path);
+
+/**
+ * Composes a new email message addressed to the given recipient,
+ * using the user's default mail client.
+ *
+ * @param address Recipient email address (no "mailto:" prefix needed).
+ * @return EINA_TRUE on success, EINA_FALSE on failure.
+ *
+ * @since 1.28
+ * @ingroup Elm_App
+ */
+EAPI Eina_Bool elm_open_email(const char *address);
+
+#endif
diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build
index 0cf8436191..a7004ce742 100644
--- a/src/lib/elementary/meson.build
+++ b/src/lib/elementary/meson.build
@@ -514,6 +514,7 @@ elementary_pub_headers = [
'elm_notify_legacy.h',
'elm_object.h',
'elm_object_item.h',
+ 'elm_open.h',
'elm_panel.h',
'elm_panel_common.h',
'elm_panel_legacy.h',
@@ -806,8 +807,9 @@ elementary_src = files([
'elm_mapbuf.c',
'elm_menu.c',
'elm_module.c',
- 'elm_notify.c',
- 'elm_panel.c',
+ 'elm_notify.c',
+ 'elm_open.c',
+ 'elm_panel.c',
'efl_ui_panes.c',
'elm_photo.c',
'efl_ui_image_zoomable.c',
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.