unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Cecilio Pardo <cpardo@imayhem.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 71909@debbugs.gnu.org
Subject: bug#71909: 30.0.60;
Date: Sat, 2 Nov 2024 21:30:38 +0100	[thread overview]
Message-ID: <d3e17e4d-1f25-4a72-84aa-03ff1e9566ce@imayhem.com> (raw)
In-Reply-To: <86plndygyx.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 944 bytes --]

On 02/11/2024 13:09, Eli Zaretskii wrote:
>> Date: Sat, 2 Nov 2024 12:24:58 +0100
>> Cc: 71909@debbugs.gnu.org
>> From: Cecilio Pardo <cpardo@imayhem.com>
>>
>>>
>>>> +Elements in FORMATS are symbols naming a format, such a image/png, or
>>>> +image/jpeg.  They don't need to be MIME types, any format available can
>>>> +be retrieved.  For compatibility with X systems, some conventional
>>>> +format names are translated to equivalent MIME types.
>>>
>>> Should this mention 'w32--selection-target-translations'?
>>>
>>> And I don't understand what you mean by the second sentence above.
>>> Surely, "any format" can be retrieved only if there's a handler for
>>> it?
>>
>> Someone may use this function outside of yank-media to get data from the
>> clipboard, and handle it herself.
> 
> Then maybe this sentence should be removed?  What useful information
> does it provide?

I did that. New patch attached with all corrections.

Thank you.

[-- Attachment #2: 0001-Add-support-for-yank-media-on-MS-Windows.patch --]
[-- Type: text/plain, Size: 28317 bytes --]

From 4f4bc35f64c8b4e25574a2e396066e2e5fa7b307 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Mon, 28 Oct 2024 22:18:13 +0100
Subject: [PATCH] Add support for yank-media on MS-Windows

Adds the capacity to handle types different from strings to the
clipboard management functions on MS-Windows, and some logic
required to convert media types names and content to be what
yank-media and the modes that use it expect (bug#71909).

* lisp/term/w32-win.el (w32--selection-target-translations): New
variable that holds the name translations for media types.
(w32--translate-selection-target): New function, translate the
name of a media type.
(w32--translate-reverse-selection-target): New function, reverse
translation.
(w32--get-selection): Modified to translate target names when
asked for targets, and retrieve media types when asked for them.
(w32--mime-type-textual-p): New function, checks if a MIME type
is textual.
* lisp/textmodes/sgml-mode.el (html-mode--image-yank-handler):
Fixed the image save mechanism, that added line feed characters
on MS-Windows, breaking binary formats.
* src/w32image.c (gdiplus_init): Modified to fetch more
functions fromm gdiplus.
(get_encoder_clsid): Renamed to w32_gdip_get_encoder_clsid and
made nonstatic.
(gdiplus_startup): Renamed to w32_gdiplus_startup and
made nonstatic.
* src/w32select.c (stdfmt_name): Made global, was static
function.
(convert_dibv5_to_png): New function to convert DIBV5 clipboard
format to PNG.
(get_clipboard_format_name): New function get the name of a
format given its index.
(Fw32__get_clipboard_data_media): New function, retrieves and
converts media content.
(syms_of_w32select): Export new lisp functions.
* src/w32gdiplus.h: New file, for definitions in w32image.c
* doc/lispref/frames.texi: Updated with MS-Windows support.
* etc/NEWS: Added entry about new feature.
---
 doc/lispref/frames.texi     |  16 +--
 etc/NEWS                    |   6 ++
 lisp/term/w32-win.el        |  71 ++++++++++++-
 lisp/textmodes/sgml-mode.el |   7 +-
 src/w32gdiplus.h            | 112 +++++++++++++++++++++
 src/w32gui.h                |   2 +
 src/w32image.c              | 104 ++++---------------
 src/w32select.c             | 194 +++++++++++++++++++++++++++++++-----
 8 files changed, 389 insertions(+), 123 deletions(-)
 create mode 100644 src/w32gdiplus.h

diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index edeba3288fc..a3538c8ac4b 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -4758,14 +4758,14 @@ Other Selections
 @node Yanking Media
 @section Yanking Media
 
-  Data saved within window system selections is not restricted to
-plain text.  It is possible for selection data to encompass images or
-other binary data of the like, as well as rich text content instanced
-by HTML, and also PostScript.  Since the selection data types incident
-to this data are at variance with those for plain text, the insertion
-of such data is facilitated by a set of functions dubbed
-@dfn{yank-media handlers}, which are registered by each major mode
-undertaking its insertion and called where warranted upon the
+  Data saved within window system selections and the MS-Windows
+clipboard is not restricted to plain text.  It is possible for selection
+data to encompass images or other binary data of the like, as well as
+rich text content instanced by HTML, and also PostScript.  Since the
+selection data types incident to this data are at variance with those
+for plain text, the insertion of such data is facilitated by a set of
+functions dubbed @dfn{yank-media handlers}, which are registered by each
+major mode undertaking its insertion and called where warranted upon the
 execution of the @code{yank-media} command.
 
 @defun yank-media-handler types handler
diff --git a/etc/NEWS b/etc/NEWS
index 4aba4b17055..900073bf80e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -815,6 +815,12 @@ DirectWrite rendering parameters.
 To show color Emoji in Emacs, customize the default fontset to use a
 color Emoji font installed on your system for the 'emoji' script.
 
++++
+** Emacs on MS-Windows now supports 'yank-media'.
+This command inserts clipboard data of different formats into the
+current buffer, if the major mode supports it.
+
+
 \f
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index 75f8530010c..b5c909f4a4e 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -442,15 +442,82 @@ w32--set-selection
       (w32-set-clipboard-data (string-replace "\0" "\\0" value))
     (put 'x-selections (or type 'PRIMARY) value)))
 
-(defun w32--get-selection  (&optional type data-type)
+(defvar w32--selection-target-translations
+  '((PNG . image/png)
+    (DIBV5 . image/png)
+    (HTML\ Format . text/html)))
+
+(defun w32--translate-selection-target (target)
+  (let ((xlat (assoc target w32--selection-target-translations)))
+    (if xlat
+        (cdr xlat)
+      target)))
+
+(defun w32--translate-reverse-selection-target (target)
+  (append
+   (mapcar #'car
+           (seq-filter
+            (lambda (x)
+              (eq target
+                  (w32--translate-selection-target (car x))))
+            w32--selection-target-translations))
+   (list target)))
+
+(defvar w32--textual-mime-types
+  '("application/xml"
+    "application/json"
+    "application/yaml"
+    "application/json-seq"
+    "\\`text/"
+    "+xml\\'"
+    "+json\\'"
+    "+yaml\\'"
+    "+json-seq\\'"))
+
+(defun w32--mime-type-textual-p (mime-type)
+  "Returns t if MIME-TYPE, a symbol, names a textual MIME type.
+
+This function is intended to classify clipboard data.  All MIME subtypes
+of text/ are considered textual.  Also those with suffixes +xml, +json,
++yaml, +json-seq.  And application/xml, application/json,
+application/yaml, application/json-seq.
+
+This classification is not exhaustive.  Some MIME types not listed may
+also be textual."
+  (string-match-p
+   (mapconcat #'identity w32--textual-mime-types "\\|")
+        (symbol-name mime-type)))
+
+(defun w32--get-selection (&optional type data-type)
   (cond ((and (eq type 'CLIPBOARD)
               (eq data-type 'STRING))
          (with-demoted-errors "w32-get-clipboard-data:%S"
            (w32-get-clipboard-data)))
         ((eq data-type 'TARGETS)
          (if (eq type 'CLIPBOARD)
-             (w32-selection-targets type)
+             (vconcat
+              (delete-dups
+               (seq-map #'w32--translate-selection-target
+                        (w32-selection-targets type))))
            (if (get 'x-selections (or type 'PRIMARY)) '[STRING])))
+        ((eq type 'CLIPBOARD)
+         (let ((tmp-file (make-temp-file "emacs-clipboard"))
+               (is-textual (w32--mime-type-textual-p data-type)))
+           (unwind-protect
+               (let* ((data-types (w32--translate-reverse-selection-target data-type))
+                      (data (w32--get-clipboard-data-media data-types tmp-file is-textual)))
+                 (cond
+                  ;; data is in the file
+                  ((eq data t)
+                   (with-temp-buffer
+                     (set-buffer-multibyte nil)
+                     (insert-file-contents-literally tmp-file)
+                     (buffer-string)))
+                  ;; data is in data var
+                  ((stringp data) data)
+                  ;; No data
+                  (t nil)))
+             (delete-file tmp-file))))
         (t (get 'x-selections (or type 'PRIMARY)))))
 
 (defun w32--selection-owner-p (selection)
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index cc86294df09..fad7008adc0 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -2476,10 +2476,9 @@ html-mode--image-yank-handler
     (when (and (file-exists-p file)
                (not (yes-or-no-p (format "%s exists; overwrite?" file))))
       (user-error "%s exists" file))
-    (with-temp-buffer
-      (set-buffer-multibyte nil)
-      (insert image)
-      (write-region (point-min) (point-max) file))
+    (let ((coding-system-for-write 'emacs-internal))
+      (with-temp-file file
+        (insert image)))
     (insert (format "<img src=%S>\n" (file-relative-name file)))
     (insert-image
      (create-image file (mailcap-mime-type-to-extension type) nil
diff --git a/src/w32gdiplus.h b/src/w32gdiplus.h
new file mode 100644
index 00000000000..9d05ae6c190
--- /dev/null
+++ b/src/w32gdiplus.h
@@ -0,0 +1,112 @@
+#ifdef WINDOWSNT
+typedef GpStatus (WINGDIPAPI *GdiplusStartup_Proc)
+  (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *);
+typedef VOID (WINGDIPAPI *GdiplusShutdown_Proc) (ULONG_PTR);
+typedef GpStatus (WINGDIPAPI *GdipGetPropertyItemSize_Proc)
+  (GpImage *, PROPID, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipGetPropertyItem_Proc)
+  (GpImage *, PROPID, UINT, PropertyItem *);
+typedef GpStatus (WINGDIPAPI *GdipImageGetFrameDimensionsCount_Proc)
+  (GpImage *, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipImageGetFrameDimensionsList_Proc)
+  (GpImage *, GUID *, UINT);
+typedef GpStatus (WINGDIPAPI *GdipImageGetFrameCount_Proc)
+  (GpImage *, GDIPCONST GUID *, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipImageSelectActiveFrame_Proc)
+  (GpImage*, GDIPCONST GUID *, UINT);
+typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromFile_Proc)
+  (WCHAR *, GpBitmap **);
+typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromStream_Proc)
+  (IStream *, GpBitmap **);
+typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromScan0_Proc)
+  (INT, INT, INT, PixelFormat, BYTE*, GpBitmap**);
+typedef IStream * (WINAPI *SHCreateMemStream_Proc) (const BYTE *, UINT);
+typedef GpStatus (WINGDIPAPI *GdipCreateHBITMAPFromBitmap_Proc)
+  (GpBitmap *, HBITMAP *, ARGB);
+typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *);
+typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipGetImageEncodersSize_Proc) (UINT *, UINT *);
+typedef GpStatus (WINGDIPAPI *GdipGetImageEncoders_Proc)
+ (UINT, UINT, ImageCodecInfo *);
+typedef GpStatus (WINGDIPAPI *GdipLoadImageFromFile_Proc)
+ (GDIPCONST WCHAR *,GpImage **);
+typedef GpStatus (WINGDIPAPI *GdipGetImageThumbnail_Proc)
+ (GpImage *, UINT, UINT, GpImage**, GetThumbnailImageAbort, VOID *);
+typedef GpStatus (WINGDIPAPI *GdipSaveImageToFile_Proc)
+ (GpImage *, GDIPCONST WCHAR *, GDIPCONST CLSID *,
+ GDIPCONST EncoderParameters *);
+typedef GpStatus (WINGDIPAPI *GdipImageRotateFlip_Proc)
+  (GpImage *image, RotateFlipType rfType);
+
+extern GdiplusStartup_Proc fn_GdiplusStartup;
+extern GdiplusShutdown_Proc fn_GdiplusShutdown;
+extern GdipGetPropertyItemSize_Proc fn_GdipGetPropertyItemSize;
+extern GdipGetPropertyItem_Proc fn_GdipGetPropertyItem;
+extern GdipImageGetFrameDimensionsCount_Proc fn_GdipImageGetFrameDimensionsCount;
+extern GdipImageGetFrameDimensionsList_Proc fn_GdipImageGetFrameDimensionsList;
+extern GdipImageGetFrameCount_Proc fn_GdipImageGetFrameCount;
+extern GdipImageSelectActiveFrame_Proc fn_GdipImageSelectActiveFrame;
+extern GdipCreateBitmapFromFile_Proc fn_GdipCreateBitmapFromFile;
+extern GdipCreateBitmapFromStream_Proc fn_GdipCreateBitmapFromStream;
+extern GdipCreateBitmapFromScan0_Proc fn_GdipCreateBitmapFromScan0;
+extern SHCreateMemStream_Proc fn_SHCreateMemStream;
+extern GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap;
+extern GdipDisposeImage_Proc fn_GdipDisposeImage;
+extern GdipGetImageHeight_Proc fn_GdipGetImageHeight;
+extern GdipGetImageWidth_Proc fn_GdipGetImageWidth;
+extern GdipGetImageEncodersSize_Proc fn_GdipGetImageEncodersSize;
+extern GdipGetImageEncoders_Proc fn_GdipGetImageEncoders;
+extern GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile;
+extern GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail;
+extern GdipSaveImageToFile_Proc fn_GdipSaveImageToFile;
+extern GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
+
+# undef GdiplusStartup
+# undef GdiplusShutdown
+# undef GdipGetPropertyItemSize
+# undef GdipGetPropertyItem
+# undef GdipImageGetFrameDimensionsCount
+# undef GdipImageGetFrameDimensionsList
+# undef GdipImageGetFrameCount
+# undef GdipImageSelectActiveFrame
+# undef GdipCreateBitmapFromFile
+# undef GdipCreateBitmapFromStream
+# undef GdipCreateBitmapFromScan0
+# undef SHCreateMemStream
+# undef GdipCreateHBITMAPFromBitmap
+# undef GdipDisposeImage
+# undef GdipGetImageHeight
+# undef GdipGetImageWidth
+# undef GdipGetImageEncodersSize
+# undef GdipGetImageEncoders
+# undef GdipLoadImageFromFile
+# undef GdipGetImageThumbnail
+# undef GdipSaveImageToFile
+# undef GdipSaveImageRotateFlip
+
+# define GdiplusStartup fn_GdiplusStartup
+# define GdiplusShutdown fn_GdiplusShutdown
+# define GdipGetPropertyItemSize fn_GdipGetPropertyItemSize
+# define GdipGetPropertyItem fn_GdipGetPropertyItem
+# define GdipImageGetFrameDimensionsCount fn_GdipImageGetFrameDimensionsCount
+# define GdipImageGetFrameDimensionsList fn_GdipImageGetFrameDimensionsList
+# define GdipImageGetFrameCount fn_GdipImageGetFrameCount
+# define GdipImageSelectActiveFrame fn_GdipImageSelectActiveFrame
+# define GdipCreateBitmapFromFile fn_GdipCreateBitmapFromFile
+# define GdipCreateBitmapFromStream fn_GdipCreateBitmapFromStream
+# define GdipCreateBitmapFromScan0 fn_GdipCreateBitmapFromScan0
+# define SHCreateMemStream fn_SHCreateMemStream
+# define GdipCreateHBITMAPFromBitmap fn_GdipCreateHBITMAPFromBitmap
+# define GdipDisposeImage fn_GdipDisposeImage
+# define GdipGetImageHeight fn_GdipGetImageHeight
+# define GdipGetImageWidth fn_GdipGetImageWidth
+# define GdipGetImageEncodersSize fn_GdipGetImageEncodersSize
+# define GdipGetImageEncoders fn_GdipGetImageEncoders
+# define GdipLoadImageFromFile fn_GdipLoadImageFromFile
+# define GdipGetImageThumbnail fn_GdipGetImageThumbnail
+# define GdipSaveImageToFile fn_GdipSaveImageToFile
+# define GdipImageRotateFlip fn_GdipImageRotateFlip
+#endif
+
+int w32_gdip_get_encoder_clsid (const char *type, CLSID *clsid);
diff --git a/src/w32gui.h b/src/w32gui.h
index 739a790911e..26565dcae6b 100644
--- a/src/w32gui.h
+++ b/src/w32gui.h
@@ -45,7 +45,9 @@ #define local_free(p) (HeapFree (local_heap, 0, ((LPVOID) (p))))
 extern int w32_load_image (struct frame *f, struct image *img,
                            Lisp_Object spec_file, Lisp_Object spec_data);
 extern bool w32_can_use_native_image_api (Lisp_Object);
+extern bool w32_gdiplus_startup (void);
 extern void w32_gdiplus_shutdown (void);
+
 extern size_t w32_image_size (Emacs_Pixmap);
 
 #define FACE_DEFAULT (~0)
diff --git a/src/w32image.c b/src/w32image.c
index 359a4fa3a72..44eed087528 100644
--- a/src/w32image.c
+++ b/src/w32image.c
@@ -38,44 +38,8 @@ #define COBJMACROS
 #include "frame.h"
 #include "coding.h"
 
+#include "w32gdiplus.h"
 #ifdef WINDOWSNT
-
-typedef GpStatus (WINGDIPAPI *GdiplusStartup_Proc)
-  (ULONG_PTR *, GdiplusStartupInput *, GdiplusStartupOutput *);
-typedef VOID (WINGDIPAPI *GdiplusShutdown_Proc) (ULONG_PTR);
-typedef GpStatus (WINGDIPAPI *GdipGetPropertyItemSize_Proc)
-  (GpImage *, PROPID, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipGetPropertyItem_Proc)
-  (GpImage *, PROPID, UINT, PropertyItem *);
-typedef GpStatus (WINGDIPAPI *GdipImageGetFrameDimensionsCount_Proc)
-  (GpImage *, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipImageGetFrameDimensionsList_Proc)
-  (GpImage *, GUID *, UINT);
-typedef GpStatus (WINGDIPAPI *GdipImageGetFrameCount_Proc)
-  (GpImage *, GDIPCONST GUID *, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipImageSelectActiveFrame_Proc)
-  (GpImage*, GDIPCONST GUID *, UINT);
-typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromFile_Proc)
-  (WCHAR *, GpBitmap **);
-typedef GpStatus (WINGDIPAPI *GdipCreateBitmapFromStream_Proc)
-  (IStream *, GpBitmap **);
-typedef IStream * (WINAPI *SHCreateMemStream_Proc) (const BYTE *, UINT);
-typedef GpStatus (WINGDIPAPI *GdipCreateHBITMAPFromBitmap_Proc)
-  (GpBitmap *, HBITMAP *, ARGB);
-typedef GpStatus (WINGDIPAPI *GdipDisposeImage_Proc) (GpImage *);
-typedef GpStatus (WINGDIPAPI *GdipGetImageHeight_Proc) (GpImage *, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipGetImageWidth_Proc) (GpImage *, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipGetImageEncodersSize_Proc) (UINT *, UINT *);
-typedef GpStatus (WINGDIPAPI *GdipGetImageEncoders_Proc)
- (UINT, UINT, ImageCodecInfo *);
-typedef GpStatus (WINGDIPAPI *GdipLoadImageFromFile_Proc)
- (GDIPCONST WCHAR *,GpImage **);
-typedef GpStatus (WINGDIPAPI *GdipGetImageThumbnail_Proc)
- (GpImage *, UINT, UINT, GpImage**, GetThumbnailImageAbort, VOID *);
-typedef GpStatus (WINGDIPAPI *GdipSaveImageToFile_Proc)
- (GpImage *, GDIPCONST WCHAR *, GDIPCONST CLSID *,
- GDIPCONST EncoderParameters *);
-
 GdiplusStartup_Proc fn_GdiplusStartup;
 GdiplusShutdown_Proc fn_GdiplusShutdown;
 GdipGetPropertyItemSize_Proc fn_GdipGetPropertyItemSize;
@@ -86,6 +50,7 @@ #define COBJMACROS
 GdipImageSelectActiveFrame_Proc fn_GdipImageSelectActiveFrame;
 GdipCreateBitmapFromFile_Proc fn_GdipCreateBitmapFromFile;
 GdipCreateBitmapFromStream_Proc fn_GdipCreateBitmapFromStream;
+GdipCreateBitmapFromScan0_Proc fn_GdipCreateBitmapFromScan0;
 SHCreateMemStream_Proc fn_SHCreateMemStream;
 GdipCreateHBITMAPFromBitmap_Proc fn_GdipCreateHBITMAPFromBitmap;
 GdipDisposeImage_Proc fn_GdipDisposeImage;
@@ -96,6 +61,7 @@ #define COBJMACROS
 GdipLoadImageFromFile_Proc fn_GdipLoadImageFromFile;
 GdipGetImageThumbnail_Proc fn_GdipGetImageThumbnail;
 GdipSaveImageToFile_Proc fn_GdipSaveImageToFile;
+GdipImageRotateFlip_Proc fn_GdipImageRotateFlip;
 
 static bool
 gdiplus_init (void)
@@ -146,6 +112,10 @@ gdiplus_init (void)
     get_proc_addr (gdiplus_lib, "GdipCreateBitmapFromStream");
   if (!fn_GdipCreateBitmapFromStream)
     return false;
+  fn_GdipCreateBitmapFromScan0 = (GdipCreateBitmapFromScan0_Proc)
+    get_proc_addr (gdiplus_lib, "GdipCreateBitmapFromScan0");
+  if (!fn_GdipCreateBitmapFromScan0)
+    return false;
   fn_GdipCreateHBITMAPFromBitmap = (GdipCreateHBITMAPFromBitmap_Proc)
     get_proc_addr (gdiplus_lib, "GdipCreateHBITMAPFromBitmap");
   if (!fn_GdipCreateHBITMAPFromBitmap)
@@ -196,52 +166,14 @@ gdiplus_init (void)
     get_proc_addr (gdiplus_lib, "GdipSaveImageToFile");
   if (!fn_GdipSaveImageToFile)
     return false;
+  fn_GdipImageRotateFlip = (GdipImageRotateFlip_Proc)
+    get_proc_addr (gdiplus_lib, "GdipImageRotateFlip");
+  if (!fn_GdipImageRotateFlip)
+    return false;
 
   return true;
 }
 
-# undef GdiplusStartup
-# undef GdiplusShutdown
-# undef GdipGetPropertyItemSize
-# undef GdipGetPropertyItem
-# undef GdipImageGetFrameDimensionsCount
-# undef GdipImageGetFrameDimensionsList
-# undef GdipImageGetFrameCount
-# undef GdipImageSelectActiveFrame
-# undef GdipCreateBitmapFromFile
-# undef GdipCreateBitmapFromStream
-# undef SHCreateMemStream
-# undef GdipCreateHBITMAPFromBitmap
-# undef GdipDisposeImage
-# undef GdipGetImageHeight
-# undef GdipGetImageWidth
-# undef GdipGetImageEncodersSize
-# undef GdipGetImageEncoders
-# undef GdipLoadImageFromFile
-# undef GdipGetImageThumbnail
-# undef GdipSaveImageToFile
-
-# define GdiplusStartup fn_GdiplusStartup
-# define GdiplusShutdown fn_GdiplusShutdown
-# define GdipGetPropertyItemSize fn_GdipGetPropertyItemSize
-# define GdipGetPropertyItem fn_GdipGetPropertyItem
-# define GdipImageGetFrameDimensionsCount fn_GdipImageGetFrameDimensionsCount
-# define GdipImageGetFrameDimensionsList fn_GdipImageGetFrameDimensionsList
-# define GdipImageGetFrameCount fn_GdipImageGetFrameCount
-# define GdipImageSelectActiveFrame fn_GdipImageSelectActiveFrame
-# define GdipCreateBitmapFromFile fn_GdipCreateBitmapFromFile
-# define GdipCreateBitmapFromStream fn_GdipCreateBitmapFromStream
-# define SHCreateMemStream fn_SHCreateMemStream
-# define GdipCreateHBITMAPFromBitmap fn_GdipCreateHBITMAPFromBitmap
-# define GdipDisposeImage fn_GdipDisposeImage
-# define GdipGetImageHeight fn_GdipGetImageHeight
-# define GdipGetImageWidth fn_GdipGetImageWidth
-# define GdipGetImageEncodersSize fn_GdipGetImageEncodersSize
-# define GdipGetImageEncoders fn_GdipGetImageEncoders
-# define GdipLoadImageFromFile fn_GdipLoadImageFromFile
-# define GdipGetImageThumbnail fn_GdipGetImageThumbnail
-# define GdipSaveImageToFile fn_GdipSaveImageToFile
-
 #endif	/* WINDOWSNT */
 
 static int gdip_initialized;
@@ -252,8 +184,8 @@ gdiplus_init (void)
 
 
 /* Initialize GDI+, return true if successful.  */
-static bool
-gdiplus_startup (void)
+bool
+w32_gdiplus_startup (void)
 {
   GpStatus status;
 
@@ -305,7 +237,7 @@ w32_can_use_native_image_api (Lisp_Object type)
 	 But we don't yet support these in image.c.  */
       return false;
     }
-  return gdiplus_startup ();
+  return w32_gdiplus_startup ();
 }
 
 enum PropertyItem_type {
@@ -549,8 +481,8 @@ w32_load_image (struct frame *f, struct image *img,
   };
 
 
-static int
-get_encoder_clsid (const char *type, CLSID *clsid)
+int
+w32_gdip_get_encoder_clsid (const char *type, CLSID *clsid)
 {
   /* A simple cache based on the assumptions that many thumbnails will
      be generated using the same TYPE.  */
@@ -625,7 +557,7 @@ DEFUN ("w32image-create-thumbnail", Fw32image_create_thumbnail,
 
   if (!gdiplus_started)
     {
-      if (!gdiplus_startup ())
+      if (!w32_gdiplus_startup ())
 	return Qnil;
     }
 
@@ -649,7 +581,7 @@ DEFUN ("w32image-create-thumbnail", Fw32image_create_thumbnail,
       CLSID thumb_clsid;
       if (status == Ok
 	  /* Get the GUID of the TYPE's encoder. */
-	  && get_encoder_clsid (SSDATA (type), &thumb_clsid) >= 0)
+	  && w32_gdip_get_encoder_clsid (SSDATA (type), &thumb_clsid) >= 0)
 	{
 	  /* Save the thumbnail image to a file of specified TYPE.  */
 	  wchar_t thumb_file_w[MAX_PATH];
diff --git a/src/w32select.c b/src/w32select.c
index 006bf408b47..7e8dc3f0702 100644
--- a/src/w32select.c
+++ b/src/w32select.c
@@ -73,12 +73,22 @@ Copyright (C) 1993-1994, 2001-2024 Free Software Foundation, Inc.
  */
 
 #include <config.h>
+#include <windows.h>
+#include <wingdi.h>
+#include <wtypes.h>
+#include <gdiplus.h>
+#ifndef CF_DIBV5
+# define CF_DIBV5 17
+# undef CF_MAX
+# define CF_MAX 18
+#endif
 #include "lisp.h"
 #include "w32common.h"	/* os_subtype */
 #include "w32term.h"	/* for all of the w32 includes */
 #include "w32select.h"
 #include "blockinput.h"
 #include "coding.h"
+#include "w32gdiplus.h"
 
 #ifdef CYGWIN
 #include <string.h>
@@ -787,6 +797,166 @@ DEFUN ("w32-set-clipboard-data", Fw32_set_clipboard_data,
   return (ok ? string : Qnil);
 }
 
+/* Xlib-like names for standard Windows clipboard data formats.
+   They are in upper-case to mimic xselect.c.  A couple of the names
+   were changed to be more like their X counterparts.  */
+static const char *stdfmt_name[] = {
+  "UNDEFINED",
+  "STRING",
+  "BITMAP",
+  "METAFILE",
+  "SYMLINK",
+  "DIF",
+  "TIFF",
+  "OEM_STRING",
+  "DIB",
+  "PALETTE",
+  "PENDATA",
+  "RIFF",
+  "WAVE",
+  "UTF8_STRING",
+  "ENHMETAFILE",
+  "FILE_NAMES", /* DND */
+  "LOCALE", /* not used */
+  "DIBV5"
+};
+
+/* Must be called with block_input() active.  */
+static bool
+convert_dibv5_to_png (char *data, int size, char *temp_file)
+{
+  CLSID clsid_png;
+
+  if (!w32_gdiplus_startup ()
+      || !w32_gdip_get_encoder_clsid ("png", &clsid_png))
+    return false;
+
+  BITMAPV5HEADER *bmi = (void *) data;
+  int stride = bmi->bV5SizeImage / bmi->bV5Height;
+  long offset = bmi->bV5Size + bmi->bV5ClrUsed * sizeof (RGBQUAD);
+  if (bmi->bV5Compression == BI_BITFIELDS)
+    offset += 12;
+  BYTE *scan0 = data + offset;
+
+  GpBitmap *bitmap = NULL;
+
+  GpStatus status
+    = GdipCreateBitmapFromScan0 (bmi->bV5Width, bmi->bV5Height, stride,
+				 PixelFormat32bppARGB, scan0, &bitmap);
+
+  if (status != Ok)
+    return false;
+
+  /* The bitmap comes upside down.  */
+  GdipImageRotateFlip (bitmap, RotateNoneFlipY);
+
+  WCHAR wide_filename[MAX_PATH];
+  filename_to_utf16 (temp_file, wide_filename);
+
+  status = GdipSaveImageToFile (bitmap, wide_filename, &clsid_png, NULL);
+  GdipDisposeImage (bitmap);
+  if (status != Ok)
+    return false;
+  return true;
+}
+
+static int
+get_clipboard_format_name (int format_index, char *name)
+{
+  *name = 0;
+  format_index = EnumClipboardFormats (format_index);
+  if (format_index == 0)
+    return 0;
+  if (format_index < CF_MAX)
+    strcpy (name, stdfmt_name[format_index]);
+  GetClipboardFormatName (format_index, name, 256);
+  return format_index;
+}
+
+DEFUN ("w32--get-clipboard-data-media", Fw32__get_clipboard_data_media,
+       Sw32__get_clipboard_data_media, 3, 3, 0,
+       doc: /* Gets media (not plain text) clipboard data in one of the given formats.
+
+FORMATS is a list of formats.
+TEMP-FILE-IN is the name of the file to store the data.
+
+Elements in FORMATS are symbols naming a format, such a image/png, or
+image/jpeg.  For compatibility with X systems, some conventional
+format names are translated to equivalent MIME types, as configured with
+the variable 'w32--selection-target-translations'.
+
+The file named in TEMP-FILE-IN must be created by the caller, and also
+deleted if required.
+
+Returns nil it there is no such format, or something failed.
+If it returns t, then the caller should read the file to get the data.
+If it returns a string, then that is the data and the file is not used.
+
+When returning a string, it will be unibyte if IS-TEXTUAL is nil (the
+content is binary data).  */)
+  (Lisp_Object formats, Lisp_Object temp_file_in, Lisp_Object is_textual)
+{
+  CHECK_LIST (formats);
+  CHECK_STRING (temp_file_in);
+
+  temp_file_in = Fexpand_file_name (temp_file_in, Qnil);
+  char *temp_file = SSDATA (ENCODE_FILE (temp_file_in));
+
+  Lisp_Object result = Qnil;
+
+  block_input();
+  if (!OpenClipboard (NULL))
+    {
+      unblock_input();
+      return Qnil;
+    }
+
+  for (int format_index = 0;;)
+    {
+      static char name[256];
+      format_index = get_clipboard_format_name (format_index, name);
+      if (format_index == 0)
+	  break;
+
+      /* If name doesn't match any of the formats, try the next format.  */
+      bool match = false;
+      for (Lisp_Object tail = formats; CONSP (tail); tail = XCDR (tail))
+	if (strcmp (name, SSDATA (SYMBOL_NAME (XCAR (tail)))) == 0)
+	    match = true;
+      if (!match)
+	  continue;
+
+      /* Of the standard formats, only DIBV5 is supported.  */
+      if (format_index < CF_MAX && format_index != CF_DIBV5)
+	continue;
+
+      /* Found the format.  */
+      HANDLE d = GetClipboardData (format_index);
+      if (!d)
+	break;
+      int size = GlobalSize (d);
+      char *data = GlobalLock (d);
+      if (!data)
+	break;
+      if (strcmp (name, "DIBV5") == 0)
+	{
+	  if (convert_dibv5_to_png (data, size, temp_file))
+	    result = Qt;
+	}
+      else
+	{
+	  if (NILP (is_textual))
+	    result = make_unibyte_string (data, size);
+	  else
+	    result = make_string (data, size);
+	}
+      GlobalUnlock (d);
+      break;
+    }
+  CloseClipboard ();
+  unblock_input ();
+  return result;
+}
 
 DEFUN ("w32-get-clipboard-data", Fw32_get_clipboard_data,
        Sw32_get_clipboard_data, 0, 1, 0,
@@ -1069,29 +1239,6 @@ DEFUN ("w32-selection-targets", Fw32_selection_targets, Sw32_selection_targets,
 representing a data format that is currently available in the clipboard.  */)
   (Lisp_Object selection, Lisp_Object terminal)
 {
-  /* Xlib-like names for standard Windows clipboard data formats.
-     They are in upper-case to mimic xselect.c.  A couple of the names
-     were changed to be more like their X counterparts.  */
-  static const char *stdfmt_name[] = {
-    "UNDEFINED",
-    "STRING",
-    "BITMAP",
-    "METAFILE",
-    "SYMLINK",
-    "DIF",
-    "TIFF",
-    "OEM_STRING",
-    "DIB",
-    "PALETTE",
-    "PENDATA",
-    "RIFF",
-    "WAVE",
-    "UTF8_STRING",
-    "ENHMETAFILE",
-    "FILE_NAMES", /* DND */
-    "LOCALE", /* not used */
-    "DIBV5"
-  };
   CHECK_SYMBOL (selection);
 
   /* Return nil for PRIMARY and SECONDARY selections; for CLIPBOARD, check
@@ -1166,6 +1313,7 @@ syms_of_w32select (void)
 {
   defsubr (&Sw32_set_clipboard_data);
   defsubr (&Sw32_get_clipboard_data);
+  defsubr (&Sw32__get_clipboard_data_media);
   defsubr (&Sw32_selection_exists_p);
   defsubr (&Sw32_selection_targets);
 
-- 
2.35.1.windows.2


  reply	other threads:[~2024-11-02 20:30 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <865xtnhyn6.fsf@foxmail.com>
2024-10-05 12:28 ` bug#71909: 30.0.60; Cecilio Pardo
2024-10-05 12:33   ` Eli Zaretskii
2024-10-05 12:42     ` Eli Zaretskii
2024-10-05 17:14       ` Cecilio Pardo
2024-10-05 19:31         ` Eli Zaretskii
2024-10-05 21:24           ` Cecilio Pardo
2024-10-06  5:59             ` Eli Zaretskii
     [not found]               ` <87ldz1h5s4.fsf@gmail.com>
2024-10-06 11:50                 ` Eli Zaretskii
2024-10-06 12:15                   ` Visuwesh
2024-10-20 13:09                     ` Ihor Radchenko
2024-10-20 13:51                       ` Eli Zaretskii
2024-10-20 13:59                         ` Ihor Radchenko
2024-10-20 14:22                           ` Eli Zaretskii
2024-10-20 15:02                             ` Ihor Radchenko
2024-10-20 15:34                               ` Eli Zaretskii
2024-10-20 15:57                                 ` Ihor Radchenko
2024-10-20 17:50                                   ` Visuwesh
2024-10-20 17:59                                     ` Eli Zaretskii
2024-10-20 17:16                       ` Cecilio Pardo
2024-10-20 17:58                         ` Eli Zaretskii
2024-10-07 10:24               ` Cecilio Pardo
2024-10-07 11:58                 ` Eli Zaretskii
2024-10-09 12:52                   ` Cecilio Pardo
2024-10-09 13:40                     ` Eli Zaretskii
2024-10-23 23:13                       ` bug#71909: 30.0.60; yank-media on MS-Windows Cecilio Pardo
2024-10-24  7:18                         ` Eli Zaretskii
2024-10-24  8:39                           ` Cecilio Pardo
2024-10-24  9:38                             ` Eli Zaretskii
2024-10-24 10:43                               ` Cecilio Pardo
2024-10-10 10:04     ` bug#71909: 30.0.60; Cecilio Pardo
2024-10-10 10:49       ` Eli Zaretskii
2024-10-28 21:46   ` Cecilio Pardo
2024-10-29 14:25     ` Eli Zaretskii
2024-10-29 14:55       ` Eli Zaretskii
2024-10-30  9:05       ` Cecilio Pardo
2024-10-30 15:35         ` Eli Zaretskii
2024-10-30 15:49           ` Cecilio Pardo
2024-11-02  0:23           ` Cecilio Pardo
2024-11-02 10:44             ` Eli Zaretskii
2024-11-02 11:24               ` Cecilio Pardo
2024-11-02 12:09                 ` Eli Zaretskii
2024-11-02 20:30                   ` Cecilio Pardo [this message]
2024-11-03 13:14                     ` Eli Zaretskii
2024-11-04 22:19                       ` Cecilio Pardo
2024-11-05 12:30                         ` Eli Zaretskii

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

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d3e17e4d-1f25-4a72-84aa-03ff1e9566ce@imayhem.com \
    --to=cpardo@imayhem.com \
    --cc=71909@debbugs.gnu.org \
    --cc=eliz@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 public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).