From 4f4bc35f64c8b4e25574a2e396066e2e5fa7b307 Mon Sep 17 00:00:00 2001 From: Cecilio Pardo 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. + + ---------------------------------------------------------------------- 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 "\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 +#include +#include +#include +#include +#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 @@ -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