From: "Juan José García-Ripoll" <juanjose.garciaripoll@gmail.com>
To: emacs-devel@gnu.org
Subject: GDI+ take 3
Date: Sat, 04 Apr 2020 23:25:09 +0200 [thread overview]
Message-ID: <86h7xzc5ai.fsf@csic.es> (raw)
[-- Attachment #1: Type: text/plain, Size: 1956 bytes --]
After some consideration and washing off the feeling that I am wasting
my time, I have reworked my previous patch to test at boot time whether
GDI+ is available (gdiplus_v3.diff). It implements a generic
"native-image-api" option which can be extended to OSX (I have a patch,
but I have no machines to test it so it is not attached).
It does not implement a new image type. Instead, it places the native
backends at the front and continues searching the image list when those
backends are not active. I find that treating the native backend as a
second class citizen, as ImageMagick, is not ok. But most important, I
find that the abstraction of backend = image-type is broken.
Regarding previous discussions, I believe the concerns about GDI+
becoming deprecated are irrelevant in comparison to the fact that Emacs
is using already a deprecated API, namely GDI, which is older and
equally prone to disappear in favor of Direct2d/WIC.
Regarding unicode, GDI+ requires WCHAR. Therefore the backend refuses to
initialize if w32-unicode-filenames is NIL, defaulting to other
backends. However, if the user changes this variable from T to NIL after
the backend has been initialized, the backend will remain active but
refuse to load images.
I have fixed the use of terminal hooks, due to insistance. However, that
meant one function declaration had to be added to w32term.h
I have not changed the use of SSDATA in the :data field of images. If
this is a problem, it is so in all of image.c.
I have touch my Emacs installation to avoid removing the double spaces
after a dot, but I am not 100% sure all is Kosher.
I go to bed. If you still find that this patch is not acceptable, I am
not going to work any more on it. I developed it to minimize the number
of dependencies of Emacs on the Windows platform, but that does not seem
to be a priority around here.
Best,
--
Juan José García Ripoll
http://juanjose.garciaripoll.com
http://quinfog.hbar.es
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gdiplus_v3.diff --]
[-- Type: text/x-patch, Size: 13958 bytes --]
diff --git a/src/image.c b/src/image.c
index 65d59254f0..2ef51a87f2 100644
--- a/src/image.c
+++ b/src/image.c
@@ -751,7 +751,7 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
/* Check that SPEC is a valid image specification for the given
image type. Value is true if SPEC is valid. */
- bool (*valid_p) (Lisp_Object spec);
+ bool (*valid_p) (Lisp_Object spec, Lisp_Object type);
/* Load IMG which is used on frame F from information contained in
IMG->spec. Value is true if successful. */
@@ -807,7 +807,7 @@ valid_image_p (Lisp_Object object)
{
struct image_type const *type = lookup_image_type (XCAR (tail));
if (type)
- return type->valid_p (object);
+ return type->valid_p (object, builtin_lisp_symbol (type->type));
}
break;
}
@@ -3144,12 +3144,12 @@ slurp_file (int fd, ptrdiff_t *size)
displayed is used. */
static bool
-xbm_image_p (Lisp_Object object)
+xbm_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword kw[XBM_LAST];
memcpy (kw, xbm_format, sizeof kw);
- if (!parse_image_spec (object, kw, XBM_LAST, Qxbm))
+ if (!parse_image_spec (object, kw, XBM_LAST, type))
return 0;
eassert (EQ (kw[XBM_TYPE].value, Qxbm));
@@ -3697,7 +3697,7 @@ xbm_load (struct frame *f, struct image *img)
bool success_p = 0;
Lisp_Object file_name;
- eassert (xbm_image_p (img->spec));
+ eassert (xbm_image_p (img->spec, Qxbm));
/* If IMG->spec specifies a file name, create a non-file spec from it. */
file_name = image_spec_value (img->spec, QCfile, NULL);
@@ -4155,11 +4155,11 @@ xpm_valid_color_symbols_p (Lisp_Object color_symbols)
/* Value is true if OBJECT is a valid XPM image specification. */
static bool
-xpm_image_p (Lisp_Object object)
+xpm_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[XPM_LAST];
memcpy (fmt, xpm_format, sizeof fmt);
- return (parse_image_spec (object, fmt, XPM_LAST, Qxpm)
+ return (parse_image_spec (object, fmt, XPM_LAST, type)
/* Either `:file' or `:data' must be present. */
&& fmt[XPM_FILE].count + fmt[XPM_DATA].count == 1
/* Either no `:color-symbols' or it's a list of conses
@@ -5882,13 +5882,13 @@ image_build_heuristic_mask (struct frame *f, struct image *img,
/* Return true if OBJECT is a valid PBM image specification. */
static bool
-pbm_image_p (Lisp_Object object)
+pbm_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[PBM_LAST];
memcpy (fmt, pbm_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, PBM_LAST, Qpbm))
+ if (!parse_image_spec (object, fmt, PBM_LAST, type))
return 0;
/* Must specify either :data or :file. */
@@ -6230,6 +6230,83 @@ pbm_load (struct frame *f, struct image *img)
return 1;
}
+\f
+/***********************************************************************
+ NATIVE IMAGE HANDLING
+ ***********************************************************************/
+#if defined(HAVE_NATIVE_IMAGE_API) && defined(HAVE_NTGUI)
+/*
+ * These functions are actually defined in the OS-native implementation
+ * file. Currently, for Windows GDI+ interface, w32image.c, but other
+ * operating systems can follow suit.
+ */
+
+static bool
+init_native_image_functions (void)
+{
+ return w32_gdiplus_startup ();
+}
+
+/* Indices of image specification fields in native format, below. */
+
+enum native_image_keyword_index
+{
+ NATIVE_IMAGE_TYPE,
+ NATIVE_IMAGE_DATA,
+ NATIVE_IMAGE_FILE,
+ NATIVE_IMAGE_ASCENT,
+ NATIVE_IMAGE_MARGIN,
+ NATIVE_IMAGE_RELIEF,
+ NATIVE_IMAGE_ALGORITHM,
+ NATIVE_IMAGE_HEURISTIC_MASK,
+ NATIVE_IMAGE_MASK,
+ NATIVE_IMAGE_BACKGROUND,
+ NATIVE_IMAGE_INDEX,
+ NATIVE_IMAGE_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+ of valid user-defined image specifications. */
+
+static const struct image_keyword native_image_format[] =
+{
+ {":type", IMAGE_SYMBOL_VALUE, 1},
+ {":data", IMAGE_STRING_VALUE, 0},
+ {":file", IMAGE_STRING_VALUE, 0},
+ {":ascent", IMAGE_ASCENT_VALUE, 0},
+ {":margin", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0},
+ {":relief", IMAGE_INTEGER_VALUE, 0},
+ {":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":background", IMAGE_STRING_OR_NIL_VALUE, 0},
+ {":index", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0}
+};
+
+/* Return true if OBJECT is a valid native API image specification. */
+
+static bool
+native_image_p (Lisp_Object object, Lisp_Object type)
+{
+ struct image_keyword fmt[NATIVE_IMAGE_LAST];
+ memcpy (fmt, native_image_format, sizeof fmt);
+
+ if (!parse_image_spec (object, fmt, 10, type))
+ return 0;
+
+ /* Must specify either the :data or :file keyword. */
+ return fmt[NATIVE_IMAGE_FILE].count + fmt[NATIVE_IMAGE_DATA].count == 1;
+}
+
+static bool
+native_image_load (struct frame *f, struct image *img)
+{
+ return w32_load_image (f, img,
+ image_spec_value (img->spec, QCfile, NULL),
+ image_spec_value (img->spec, QCdata, NULL));
+}
+#endif
+
\f
/***********************************************************************
PNG
@@ -6274,12 +6351,12 @@ pbm_load (struct frame *f, struct image *img)
/* Return true if OBJECT is a valid PNG image specification. */
static bool
-png_image_p (Lisp_Object object)
+png_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[PNG_LAST];
memcpy (fmt, png_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, PNG_LAST, Qpng))
+ if (!parse_image_spec (object, fmt, PNG_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
@@ -6889,7 +6966,6 @@ png_load (struct frame *f, struct image *img)
image_spec_value (img->spec, QCdata, NULL));
}
-
#endif /* HAVE_NS */
@@ -6937,13 +7013,13 @@ png_load (struct frame *f, struct image *img)
/* Return true if OBJECT is a valid JPEG image specification. */
static bool
-jpeg_image_p (Lisp_Object object)
+jpeg_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[JPEG_LAST];
memcpy (fmt, jpeg_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, JPEG_LAST, Qjpeg))
+ if (!parse_image_spec (object, fmt, JPEG_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
@@ -7513,12 +7589,12 @@ jpeg_load (struct frame *f, struct image *img)
/* Return true if OBJECT is a valid TIFF image specification. */
static bool
-tiff_image_p (Lisp_Object object)
+tiff_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[TIFF_LAST];
memcpy (fmt, tiff_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, TIFF_LAST, Qtiff))
+ if (!parse_image_spec (object, fmt, TIFF_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
@@ -7961,19 +8037,19 @@ gif_clear_image (struct frame *f, struct image *img)
/* Return true if OBJECT is a valid GIF image specification. */
static bool
-gif_image_p (Lisp_Object object)
+gif_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[GIF_LAST];
memcpy (fmt, gif_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, GIF_LAST, Qgif))
+ if (!parse_image_spec (object, fmt, GIF_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
return fmt[GIF_FILE].count + fmt[GIF_DATA].count == 1;
}
-#endif /* HAVE_GIF */
+#endif /* HAVE_GIF || HAVE_NS */
#ifdef HAVE_GIF
@@ -8573,12 +8649,12 @@ imagemagick_clear_image (struct frame *f,
identify the IMAGEMAGICK format. */
static bool
-imagemagick_image_p (Lisp_Object object)
+imagemagick_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[IMAGEMAGICK_LAST];
memcpy (fmt, imagemagick_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, Qimagemagick))
+ if (!parse_image_spec (object, fmt, IMAGEMAGICK_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
@@ -9368,12 +9444,12 @@ DEFUN ("imagemagick-types", Fimagemagick_types, Simagemagick_types, 0, 0, 0,
identify the SVG format. */
static bool
-svg_image_p (Lisp_Object object)
+svg_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[SVG_LAST];
memcpy (fmt, svg_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, SVG_LAST, Qsvg))
+ if (!parse_image_spec (object, fmt, SVG_LAST, type))
return 0;
/* Must specify either the :data or :file keyword. */
@@ -9836,7 +9912,7 @@ #define HAVE_GHOSTSCRIPT 1
specification. */
static bool
-gs_image_p (Lisp_Object object)
+gs_image_p (Lisp_Object object, Lisp_Object type)
{
struct image_keyword fmt[GS_LAST];
Lisp_Object tem;
@@ -9844,7 +9920,7 @@ gs_image_p (Lisp_Object object)
memcpy (fmt, gs_format, sizeof fmt);
- if (!parse_image_spec (object, fmt, GS_LAST, Qpostscript))
+ if (!parse_image_spec (object, fmt, GS_LAST, type))
return 0;
/* Bounding box must be a list or vector containing 4 integers. */
@@ -10131,13 +10207,20 @@ DEFUN ("init-image-library", Finit_image_library, Sinit_image_library, 1, 1, 0,
initialize_image_type (struct image_type const *type)
{
#ifdef WINDOWSNT
- Lisp_Object typesym = builtin_lisp_symbol (type->type);
- Lisp_Object tested = Fassq (typesym, Vlibrary_cache);
+ Lisp_Object typesym, tested;
+ bool (*init) (void) = type->init;
+
+#ifdef HAVE_NATIVE_IMAGE_API
+ if (init == init_native_image_functions)
+ return init();
+#endif
+
+ typesym = builtin_lisp_symbol (type->type);
+ tested = Fassq (typesym, Vlibrary_cache);
/* If we failed to load the library before, don't try again. */
if (CONSP (tested))
return !NILP (XCDR (tested)) ? true : false;
- bool (*init) (void) = type->init;
if (init)
{
bool type_valid = init ();
@@ -10164,6 +10247,16 @@ initialize_image_type (struct image_type const *type)
{ SYMBOL_INDEX (Qsvg), svg_image_p, svg_load, image_clear_image,
IMAGE_TYPE_INIT (init_svg_functions) },
#endif
+#if defined HAVE_NATIVE_IMAGE_API
+ { SYMBOL_INDEX (Qjpeg), native_image_p, native_image_load, image_clear_image,
+ IMAGE_TYPE_INIT (init_native_image_functions) },
+ { SYMBOL_INDEX (Qpng), native_image_p, native_image_load, image_clear_image,
+ IMAGE_TYPE_INIT (init_native_image_functions) },
+ { SYMBOL_INDEX (Qgif), native_image_p, native_image_load, image_clear_image,
+ IMAGE_TYPE_INIT (init_native_image_functions) },
+ { SYMBOL_INDEX (Qtiff), native_image_p, native_image_load, image_clear_image,
+ IMAGE_TYPE_INIT (init_native_image_functions) },
+#endif
#if defined HAVE_PNG || defined HAVE_NS
{ SYMBOL_INDEX (Qpng), png_image_p, png_load, image_clear_image,
IMAGE_TYPE_INIT (init_png_functions) },
@@ -10198,7 +10291,13 @@ lookup_image_type (Lisp_Object type)
{
struct image_type const *r = &image_types[i];
if (EQ (type, builtin_lisp_symbol (r->type)))
+#ifdef HAVE_NATIVE_IMAGE_API
+ /* We can have more than one backend for one image type. */
+ if (initialize_image_type (r))
+ return r;
+#else
return initialize_image_type (r) ? r : NULL;
+#endif
}
return NULL;
}
@@ -10315,22 +10414,22 @@ syms_of_image (void)
add_image_type (Qxpm);
#endif
-#if defined (HAVE_JPEG) || defined (HAVE_NS)
+#if defined (HAVE_JPEG) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
DEFSYM (Qjpeg, "jpeg");
add_image_type (Qjpeg);
#endif
-#if defined (HAVE_TIFF) || defined (HAVE_NS)
+#if defined (HAVE_TIFF) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
DEFSYM (Qtiff, "tiff");
add_image_type (Qtiff);
#endif
-#if defined (HAVE_GIF) || defined (HAVE_NS)
+#if defined (HAVE_GIF) || defined (HAVE_NS) || defined (HAVE_NATIVE_IMAGE_API)
DEFSYM (Qgif, "gif");
add_image_type (Qgif);
#endif
-#if defined (HAVE_PNG) || defined (HAVE_NS)
+#if defined (HAVE_PNG) || defined (HAVE_NS) || defined(HAVE_NATIVE_IMAGE_API)
DEFSYM (Qpng, "png");
add_image_type (Qpng);
#endif
diff --git a/src/w32.c b/src/w32.c
index 698e10e234..1d2a52b6df 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10225,6 +10225,10 @@ term_ntproc (int ignored)
term_winsock ();
term_w32select ();
+
+#ifdef HAVE_GDIPLUS
+ w32_gdiplus_shutdown ();
+#endif
}
void
diff --git a/src/w32term.c b/src/w32term.c
index 5fa77d58e1..f19754df02 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -1529,7 +1529,7 @@ w32_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
/* Store F's background color into *BGCOLOR. */
-static void
+void
w32_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
{
bgcolor->pixel = FRAME_BACKGROUND_PIXEL (f);
diff --git a/src/w32term.h b/src/w32term.h
index f8a8a727e8..d44c6f9b83 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -75,7 +75,10 @@ #define CP_DEFAULT 1004
extern void w32_regenerate_palette (struct frame *f);
extern void w32_fullscreen_rect (HWND hwnd, int fsmode, RECT normal,
RECT *rect);
-
+extern int w32_load_image (struct frame *f, struct image *img,
+ Lisp_Object spec_file, Lisp_Object spec_data);
+extern bool w32_gdiplus_startup (void);
+extern void w32_gdiplus_shutdown (void);
\f
/* For each display (currently only one on w32), we have a structure that
records information about it. */
@@ -248,6 +251,8 @@ #define CP_DEFAULT 1004
extern int w32_display_pixel_width (struct w32_display_info *);
extern void initialize_frame_menubar (struct frame *);
extern void w32_dialog_in_progress (Lisp_Object in_progress);
+extern void w32_query_frame_background_color (struct frame *f,
+ Emacs_Color *bgcolor);
extern void w32_make_frame_visible (struct frame *f);
extern void w32_make_frame_invisible (struct frame *f);
next reply other threads:[~2020-04-04 21:25 UTC|newest]
Thread overview: 86+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-04 21:25 Juan José García-Ripoll [this message]
2020-04-05 12:58 ` GDI+ take 3 Eli Zaretskii
2020-04-13 6:19 ` Eli Zaretskii
2020-04-13 10:04 ` Juan José García-Ripoll
2020-04-14 15:33 ` Eli Zaretskii
2020-04-14 17:43 ` Eli Zaretskii
2020-04-14 22:19 ` Alan Third
2020-04-16 18:39 ` Eli Zaretskii
2020-04-14 18:38 ` Dmitry Gutov
2020-04-14 18:43 ` Eli Zaretskii
2020-04-14 19:38 ` Dmitry Gutov
2020-04-14 19:08 ` Basil L. Contovounesios
2020-04-14 19:24 ` Eli Zaretskii
2020-04-14 21:57 ` Basil L. Contovounesios
2020-04-15 6:18 ` Eli Zaretskii
2020-04-15 13:40 ` Juanma Barranquero
2020-04-15 14:00 ` Eli Zaretskii
2020-04-15 14:12 ` Juanma Barranquero
2020-04-15 14:17 ` Juanma Barranquero
2020-04-15 14:28 ` Eli Zaretskii
2020-04-15 14:35 ` Juanma Barranquero
2020-04-15 14:43 ` Eli Zaretskii
2020-04-15 14:47 ` Juanma Barranquero
2020-04-15 15:00 ` Juanma Barranquero
2020-04-15 15:02 ` Juanma Barranquero
2020-04-15 15:10 ` Eli Zaretskii
2020-04-15 15:31 ` Juanma Barranquero
2020-04-15 15:46 ` Eli Zaretskii
2020-04-15 15:56 ` Eli Zaretskii
2020-04-15 16:08 ` Eli Zaretskii
2020-04-15 16:50 ` Juanma Barranquero
2020-04-15 16:59 ` Eli Zaretskii
2020-04-15 17:24 ` Juanma Barranquero
2020-04-15 17:34 ` Eli Zaretskii
2020-04-15 17:49 ` Juanma Barranquero
2020-04-15 18:13 ` Eli Zaretskii
2020-04-15 18:45 ` Juanma Barranquero
2020-04-15 20:21 ` Eli Zaretskii
2020-04-15 20:31 ` Juanma Barranquero
2020-04-16 10:04 ` Eli Zaretskii
2020-04-16 23:49 ` Juanma Barranquero
2020-04-17 6:55 ` Eli Zaretskii
2020-04-17 7:27 ` Juan José García-Ripoll
2020-04-17 8:36 ` Juanma Barranquero
2020-04-17 9:52 ` Eli Zaretskii
2020-04-18 8:41 ` Juan José García-Ripoll
2020-04-18 10:00 ` Eli Zaretskii
2020-04-18 10:09 ` Juanma Barranquero
2020-04-18 12:38 ` Juan José García-Ripoll
2020-04-18 13:38 ` Eli Zaretskii
2020-04-18 15:56 ` Juanma Barranquero
2020-04-18 16:15 ` Eli Zaretskii
2020-04-18 17:51 ` Juan José García-Ripoll
2020-04-18 18:01 ` Eli Zaretskii
2020-04-18 18:04 ` Eli Zaretskii
2020-04-18 18:49 ` Juanma Barranquero
2020-04-18 19:15 ` Eli Zaretskii
2020-04-18 20:19 ` Alan Third
2020-04-19 10:20 ` Juan José García-Ripoll
2020-04-19 20:08 ` Juan José García-Ripoll
2020-04-20 13:37 ` Eli Zaretskii
2020-04-21 7:35 ` Juan José García-Ripoll
2020-04-21 14:15 ` Eli Zaretskii
2020-04-21 18:17 ` Alan Third
2020-04-21 18:34 ` Eli Zaretskii
2020-04-25 16:51 ` Alan Third
2020-04-20 20:16 ` Alan Third
2020-04-21 6:25 ` Juan José García-Ripoll
2020-04-25 16:23 ` Alan Third
2020-04-25 13:42 ` Eli Zaretskii
2020-04-26 15:14 ` Juan José García-Ripoll
2020-04-19 18:16 ` Eli Zaretskii
2020-04-19 20:28 ` Juan José García-Ripoll
2020-04-20 13:54 ` Eli Zaretskii
2020-04-21 6:44 ` Juan José García-Ripoll
2020-04-21 14:13 ` Eli Zaretskii
2020-04-21 16:20 ` Juan José García-Ripoll
2020-04-15 16:50 ` Eli Zaretskii
2020-04-15 14:27 ` Eli Zaretskii
[not found] <617217672.240027.1586079490291@mail1.libero.it>
2020-04-15 14:07 ` Angelo Graziosi
2020-04-15 14:15 ` Eli Zaretskii
2020-04-15 14:22 ` Angelo Graziosi
2020-04-15 14:26 ` Eli Zaretskii
2020-04-15 15:25 ` Angelo Graziosi
2020-04-15 15:27 ` Eli Zaretskii
2020-04-15 15:46 ` Angelo Graziosi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=86h7xzc5ai.fsf@csic.es \
--to=juanjose.garciaripoll@gmail.com \
--cc=emacs-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.