Mercurial > hg > xemacs-beta
diff src/select-msw.c @ 442:abe6d1db359e r21-2-36
Import from CVS: tag r21-2-36
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:35:02 +0200 |
parents | a5df635868b2 |
children | e7ef97881643 |
line wrap: on
line diff
--- a/src/select-msw.c Mon Aug 13 11:33:40 2007 +0200 +++ b/src/select-msw.c Mon Aug 13 11:35:02 2007 +0200 @@ -24,185 +24,549 @@ Written by Kevin Gallo for FSF Emacs. Rewritten for mswindows by Jonathan Harris, December 1997 for 21.0. - */ - + Hacked by Alastair Houghton, July 2000 for enhanced clipboard support. +*/ #include <config.h> #include "lisp.h" #include "frame.h" #include "select.h" +#include "opaque.h" +#include "file-coding.h" +#include "buffer.h" #include "console-msw.h" -DEFUN ("mswindows-set-clipboard", Fmswindows_set_clipboard, 1, 1, 0, /* -Copy STRING to the mswindows clipboard. -*/ - (string)) +/* A list of handles that we must release. Not accessible from Lisp. */ +static Lisp_Object Vhandle_alist; + +/* Test if this is an X symbol that we understand */ +static int +x_sym_p (Lisp_Object value) +{ + if (NILP (value) || INTP (value)) + return 0; + + /* Check for some of the X symbols */ + if (EQ (value, QSTRING)) return 1; + if (EQ (value, QTEXT)) return 1; + if (EQ (value, QCOMPOUND_TEXT)) return 1; + + return 0; +} + +/* This converts a Lisp symbol to an MS-Windows clipboard format. + We have symbols for all predefined clipboard formats, but that + doesn't mean we support them all ;-) + The name of this function is actually a lie - it also knows about + integers and strings... */ +static UINT +symbol_to_ms_cf (Lisp_Object value) { - int rawsize, size, i; - unsigned char *src, *dst, *next; - HGLOBAL h = NULL; - struct frame *f = NULL; + /* If it's NIL, we're in trouble. */ + if (NILP (value)) return 0; + + /* If it's an integer, assume it's a format ID */ + if (INTP (value)) return (UINT) (XINT (value)); + + /* If it's a string, register the format(!) */ + if (STRINGP (value)) + return RegisterClipboardFormat (XSTRING_DATA (value)); + + /* Check for Windows clipboard format symbols */ + if (EQ (value, QCF_TEXT)) return CF_TEXT; + if (EQ (value, QCF_BITMAP)) return CF_BITMAP; + if (EQ (value, QCF_METAFILEPICT)) return CF_METAFILEPICT; + if (EQ (value, QCF_SYLK)) return CF_SYLK; + if (EQ (value, QCF_DIF)) return CF_DIF; + if (EQ (value, QCF_TIFF)) return CF_TIFF; + if (EQ (value, QCF_OEMTEXT)) return CF_OEMTEXT; + if (EQ (value, QCF_DIB)) return CF_DIB; +#ifdef CF_DIBV5 + if (EQ (value, QCF_DIBV5)) return CF_DIBV5; +#endif + if (EQ (value, QCF_PALETTE)) return CF_PALETTE; + if (EQ (value, QCF_PENDATA)) return CF_PENDATA; + if (EQ (value, QCF_RIFF)) return CF_RIFF; + if (EQ (value, QCF_WAVE)) return CF_WAVE; + if (EQ (value, QCF_UNICODETEXT)) return CF_UNICODETEXT; + if (EQ (value, QCF_ENHMETAFILE)) return CF_ENHMETAFILE; + if (EQ (value, QCF_HDROP)) return CF_HDROP; + if (EQ (value, QCF_LOCALE)) return CF_LOCALE; + if (EQ (value, QCF_OWNERDISPLAY)) return CF_OWNERDISPLAY; + if (EQ (value, QCF_DSPTEXT)) return CF_DSPTEXT; + if (EQ (value, QCF_DSPBITMAP)) return CF_DSPBITMAP; + if (EQ (value, QCF_DSPMETAFILEPICT)) return CF_DSPMETAFILEPICT; + if (EQ (value, QCF_DSPENHMETAFILE)) return CF_DSPENHMETAFILE; + + return 0; +} + +/* This converts an MS-Windows clipboard format to its corresponding + Lisp symbol, or a Lisp integer otherwise. */ +static Lisp_Object +ms_cf_to_symbol (UINT format) +{ + switch (format) + { + case CF_TEXT: return QCF_TEXT; + case CF_BITMAP: return QCF_BITMAP; + case CF_METAFILEPICT: return QCF_METAFILEPICT; + case CF_SYLK: return QCF_SYLK; + case CF_DIF: return QCF_DIF; + case CF_TIFF: return QCF_TIFF; + case CF_OEMTEXT: return QCF_OEMTEXT; + case CF_DIB: return QCF_DIB; +#ifdef CF_DIBV5 + case CF_DIBV5: return QCF_DIBV5; +#endif + case CF_PALETTE: return QCF_PALETTE; + case CF_PENDATA: return QCF_PENDATA; + case CF_RIFF: return QCF_RIFF; + case CF_WAVE: return QCF_WAVE; + case CF_UNICODETEXT: return QCF_UNICODETEXT; + case CF_ENHMETAFILE: return QCF_ENHMETAFILE; + case CF_HDROP: return QCF_HDROP; + case CF_LOCALE: return QCF_LOCALE; + case CF_OWNERDISPLAY: return QCF_OWNERDISPLAY; + case CF_DSPTEXT: return QCF_DSPTEXT; + case CF_DSPBITMAP: return QCF_DSPBITMAP; + case CF_DSPMETAFILEPICT: return QCF_DSPMETAFILEPICT; + case CF_DSPENHMETAFILE: return QCF_DSPENHMETAFILE; + default: return make_int ((int) format); + } +} - CHECK_STRING (string); +/* Test if the specified clipboard format is auto-released by the OS. If + not, we must remember the handle on Vhandle_alist, and free it if + the clipboard is emptied or if we set data with the same format. */ +static int +cf_is_autofreed (UINT format) +{ + switch (format) + { + /* This list comes from the SDK documentation */ + case CF_DSPENHMETAFILE: + case CF_DSPMETAFILEPICT: + case CF_ENHMETAFILE: + case CF_METAFILEPICT: + case CF_BITMAP: + case CF_DSPBITMAP: + case CF_PALETTE: + case CF_DIB: +#ifdef CF_DIBV5 + case CF_DIBV5: +#endif + case CF_DSPTEXT: + case CF_OEMTEXT: + case CF_TEXT: + case CF_UNICODETEXT: + return TRUE; + + default: + return FALSE; + } +} + +/* Do protocol to assert ourself as a selection owner. + + Under mswindows, we: + + * Only set the clipboard if (eq selection-name 'CLIPBOARD) + + * Check if an X atom name has been passed. If so, convert to CF_TEXT + (or CF_UNICODETEXT) remembering to perform LF -> CR-LF conversion. + + * Otherwise assume the data is formatted appropriately for the data type + that was passed. + + Then set the clipboard as necessary. +*/ +static Lisp_Object +mswindows_own_selection (Lisp_Object selection_name, + Lisp_Object selection_value, + Lisp_Object how_to_add, + Lisp_Object selection_type) +{ + HGLOBAL hValue = NULL; + UINT cfType; + int is_X_type = FALSE; + Lisp_Object cfObject; + Lisp_Object data = Qnil; + int size; + void *src, *dst; + struct frame *f = NULL; - /* Calculate size with LFs converted to CRLFs because - * CF_TEXT format uses CRLF delimited ASCIIZ */ - src = XSTRING_DATA (string); - size = rawsize = XSTRING_LENGTH (string) + 1; - for (i=0; i<rawsize; i++) - if (src[i] == '\n') - size++; + /* Only continue if we're trying to set the clipboard - mswindows doesn't + use the same selection model as X */ + if (!EQ (selection_name, QCLIPBOARD)) + return Qnil; + + /* If this is one of the X-style atom name symbols, or NIL, convert it + as appropriate */ + if (NILP (selection_type) || x_sym_p (selection_type)) + { + /* Should COMPOUND_TEXT map to CF_UNICODETEXT? */ + cfType = CF_TEXT; + cfObject = QCF_TEXT; + is_X_type = TRUE; + } + else + { + cfType = symbol_to_ms_cf (selection_type); + + /* Only continue if we can figure out a clipboard type */ + if (!cfType) + return Qnil; + + cfObject = selection_type; + } + + /* Convert things appropriately */ + data = select_convert_out (selection_name, + cfObject, + selection_value); + if (NILP (data)) + return Qnil; + + if (CONSP (data)) + { + if (!EQ (XCAR (data), cfObject)) + cfType = symbol_to_ms_cf (XCAR (data)); + + if (!cfType) + return Qnil; + + data = XCDR (data); + } + + /* We support opaque or string values, but we only mention string + values for now... */ + if (!OPAQUEP (data) + && !STRINGP (data)) + return Qnil; + + /* Compute the data length */ + if (OPAQUEP (data)) + size = XOPAQUE_SIZE (data); + else + size = XSTRING_LENGTH (data) + 1; + + /* Find the frame */ f = selected_frame (); + + /* Open the clipboard */ if (!OpenClipboard (FRAME_MSWINDOWS_HANDLE (f))) return Qnil; - if (!EmptyClipboard () || - (h = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, size)) == NULL || - (dst = (unsigned char *) GlobalLock (h)) == NULL) + /* Allocate memory */ + hValue = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, size); + + if (!hValue) { - if (h != NULL) GlobalFree (h); CloseClipboard (); + + return Qnil; + } + + /* Copy the data */ + if (OPAQUEP (data)) + src = XOPAQUE_DATA (data); + else + src = XSTRING_DATA (data); + + dst = GlobalLock (hValue); + + if (!dst) + { + GlobalFree (hValue); + CloseClipboard (); + return Qnil; } - - /* Convert LFs to CRLFs */ - do + + memcpy (dst, src, size); + + GlobalUnlock (hValue); + + /* Empty the clipboard if we're replacing everything */ + if (NILP (how_to_add) || EQ (how_to_add, Qreplace_all)) { - /* copy next line or remaining bytes including '\0' */ - next = memccpy (dst, src, '\n', rawsize); - if (next) + if (!EmptyClipboard ()) { - /* copied one line ending with '\n' */ - int copied = next - dst; - rawsize -= copied; - src += copied; - /* insert '\r' before '\n' */ - next[-1] = '\r'; - next[0] = '\n'; - dst = next+1; - } + CloseClipboard (); + GlobalFree (hValue); + + return Qnil; + } } - while (next); - - GlobalUnlock (h); - - i = (SetClipboardData (CF_TEXT, h) != NULL); - + + /* Append is currently handled in select.el; perhaps this should change, + but it only really makes sense for ordinary text in any case... */ + + SetClipboardData (cfType, hValue); + + if (!cf_is_autofreed (cfType)) + { + Lisp_Object alist_elt = Qnil, rest; + Lisp_Object cfType_int = make_int (cfType); + + /* First check if there's an element in the alist for this type + already. */ + alist_elt = assq_no_quit (cfType_int, Vhandle_alist); + + /* Add an element to the alist */ + Vhandle_alist = Fcons (Fcons (cfType_int, make_opaque_ptr (hValue)), + Vhandle_alist); + + if (!NILP (alist_elt)) + { + /* Free the original handle */ + GlobalFree ((HGLOBAL) get_opaque_ptr (XCDR (alist_elt))); + + /* Remove the original one (adding first makes life easier, because + we don't have to special case this being the first element) */ + for (rest = Vhandle_alist; !NILP (rest); rest = Fcdr (rest)) + if (EQ (cfType_int, Fcar (XCDR (rest)))) + { + XCDR (rest) = Fcdr (XCDR (rest)); + break; + } + } + } + CloseClipboard (); - - return i ? Qt : Qnil; + + /* #### Should really return a time, though this is because of the + X model (by the looks of things) */ + return Qnil; } -/* Do protocol to assert ourself as a selection owner. Under mswindows -this is easy, we just set the clipboard. */ static Lisp_Object -mswindows_own_selection (Lisp_Object selection_name, Lisp_Object selection_value) +mswindows_available_selection_types (Lisp_Object selection_name) +{ + Lisp_Object types = Qnil; + UINT format = 0; + struct frame *f = NULL; + + if (!EQ (selection_name, QCLIPBOARD)) + return Qnil; + + /* Find the frame */ + f = selected_frame (); + + /* Open the clipboard */ + if (!OpenClipboard (FRAME_MSWINDOWS_HANDLE (f))) + return Qnil; + + /* #### ajh - Should there be an unwind-protect handler around this? + It could (well it probably won't, but it's always better to + be safe) run out of memory and leave the clipboard open... */ + + while ((format = EnumClipboardFormats (format))) + types = Fcons (ms_cf_to_symbol (format), types); + + /* Close it */ + CloseClipboard (); + + return types; +} + +static Lisp_Object +mswindows_register_selection_data_type (Lisp_Object type_name) { - Lisp_Object converted_value = get_local_selection (selection_name, QSTRING); - if (!NILP (converted_value) && - CONSP (converted_value) && - EQ (XCAR (converted_value), QSTRING) && - /* pure mswindows behaviour only says we can own the selection - if it is the clipboard */ - EQ (selection_name, QCLIPBOARD)) - Fmswindows_set_clipboard (XCDR (converted_value)); + /* Type already checked in select.c */ + const char *name = XSTRING_DATA (type_name); + UINT format; + + format = RegisterClipboardFormat (name); + + if (format) + return make_int ((int) format); + else + return Qnil; +} + +static Lisp_Object +mswindows_selection_data_type_name (Lisp_Object type_id) +{ + UINT format; + int numchars; + char name_buf[128]; + + /* If it's an integer, convert to a symbol if appropriate */ + if (INTP (type_id)) + type_id = ms_cf_to_symbol (XINT (type_id)); + + /* If this is a symbol, return it */ + if (SYMBOLP (type_id)) + return type_id; + + /* Find the format code */ + format = symbol_to_ms_cf (type_id); + + if (!format) + return Qnil; + + /* Microsoft, stupid Microsoft */ + numchars = GetClipboardFormatName (format, name_buf, 128); + + if (numchars) + { + Lisp_Object name; + + /* Do this properly - though we could support UNICODE (UCS-2) if + MULE could hack it. */ + name = make_ext_string (name_buf, numchars, + Fget_coding_system (Qraw_text)); + + return name; + } return Qnil; } -DEFUN ("mswindows-get-clipboard", Fmswindows_get_clipboard, 0, 0, 0, /* -Return the contents of the mswindows clipboard. -*/ - ()) +static Lisp_Object +mswindows_get_foreign_selection (Lisp_Object selection_symbol, + Lisp_Object target_type) { - HANDLE h; - unsigned char *src, *dst, *next; - Lisp_Object ret = Qnil; + HGLOBAL hValue = NULL; + UINT cfType; + Lisp_Object cfObject = Qnil, ret = Qnil, value = Qnil; + int is_X_type = FALSE; + int size; + void *data; + struct frame *f = NULL; + struct gcpro gcpro1; + + /* Only continue if we're trying to read the clipboard - mswindows doesn't + use the same selection model as X */ + if (!EQ (selection_symbol, QCLIPBOARD)) + return Qnil; - if (!OpenClipboard (NULL)) + /* If this is one of the X-style atom name symbols, or NIL, convert it + as appropriate */ + if (NILP (target_type) || x_sym_p (target_type)) + { + /* Should COMPOUND_TEXT map to CF_UNICODETEXT? */ + cfType = CF_TEXT; + cfObject = QCF_TEXT; + is_X_type = TRUE; + } + else + { + cfType = symbol_to_ms_cf (target_type); + + /* Only continue if we can figure out a clipboard type */ + if (!cfType) + return Qnil; + + cfObject = ms_cf_to_symbol (cfType); + } + + /* Find the frame */ + f = selected_frame (); + + /* Open the clipboard */ + if (!OpenClipboard (FRAME_MSWINDOWS_HANDLE (f))) return Qnil; - if ((h = GetClipboardData (CF_TEXT)) != NULL && - (src = (unsigned char *) GlobalLock (h)) != NULL) + /* Read the clipboard */ + hValue = GetClipboardData (cfType); + + if (!hValue) { - int i; - int size, rawsize; - size = rawsize = strlen (src); + CloseClipboard (); - for (i=0; i<rawsize; i++) - if (src[i] == '\r' && src[i+1] == '\n') - size--; + return Qnil; + } - /* Convert CRLFs to LFs */ - ret = make_uninit_string (size); - dst = XSTRING_DATA (ret); - do - { - /* copy next line or remaining bytes excluding '\0' */ - next = memccpy (dst, src, '\r', rawsize); - if (next) - { - /* copied one line ending with '\r' */ - int copied = next - dst; - rawsize -= copied; - src += copied; - if (*src == '\n') - dst += copied - 1; /* overwrite '\r' */ - else - dst += copied; - } - } - while (next); + /* Find the data */ + size = GlobalSize (hValue); + data = GlobalLock (hValue); - GlobalUnlock (h); + if (!data) + { + CloseClipboard (); + + return Qnil; } + /* Place it in a Lisp string */ + TO_INTERNAL_FORMAT (DATA, (data, size), + LISP_STRING, ret, + Qbinary); + + GlobalUnlock (data); CloseClipboard (); - return ret; -} - -static Lisp_Object -mswindows_get_foreign_selection (Lisp_Object selection_symbol, Lisp_Object target_type) -{ - if (EQ (selection_symbol, QCLIPBOARD)) - return Fmswindows_get_clipboard (); - else - return Qnil; -} + GCPRO1 (ret); -DEFUN ("mswindows-selection-exists-p", Fmswindows_selection_exists_p, 0, 0, 0, /* -Whether there is an MS-Windows selection. -*/ - ()) -{ - return IsClipboardFormatAvailable (CF_TEXT) ? Qt : Qnil; -} + /* Convert this to the appropriate type. If we can't find anything, + then we return a cons of the form (DATA-TYPE . STRING), where the + string contains the raw binary data. */ + value = select_convert_in (selection_symbol, + cfObject, + ret); -DEFUN ("mswindows-delete-selection", Fmswindows_delete_selection, 0, 0, 0, /* -Remove the current MS-Windows selection from the clipboard. -*/ - ()) -{ - BOOL success = OpenClipboard (NULL); - if (success) - { - success = EmptyClipboard (); - /* Close it regardless of whether empty worked. */ - if (!CloseClipboard ()) - success = FALSE; - } + UNGCPRO; - return success ? Qt : Qnil; + if (NILP (value)) + return Fcons (cfObject, ret); + else + return value; } static void mswindows_disown_selection (Lisp_Object selection, Lisp_Object timeval) { if (EQ (selection, QCLIPBOARD)) - Fmswindows_delete_selection (); + { + BOOL success = OpenClipboard (NULL); + if (success) + { + success = EmptyClipboard (); + /* Close it regardless of whether empty worked. */ + if (!CloseClipboard ()) + success = FALSE; + } + + /* #### return success ? Qt : Qnil; */ + } +} + +void +mswindows_destroy_selection (Lisp_Object selection) +{ + /* Do nothing if this isn't for the clipboard. */ + if (!EQ (selection, QCLIPBOARD)) + return; + + /* Right. We need to delete everything in Vhandle_alist. */ + { + LIST_LOOP_2 (elt, Vhandle_alist) + GlobalFree ((HGLOBAL) get_opaque_ptr (XCDR (elt))); + } + + Vhandle_alist = Qnil; +} + +static Lisp_Object +mswindows_selection_exists_p (Lisp_Object selection, + Lisp_Object selection_type) +{ + /* We used to be picky about the format, but now we support anything. */ + if (EQ (selection, QCLIPBOARD)) + { + if (NILP (selection_type)) + return CountClipboardFormats () ? Qt : Qnil; + else + return IsClipboardFormatAvailable (symbol_to_ms_cf (selection_type)) + ? Qt : Qnil; + } + else + return Qnil; } @@ -215,19 +579,22 @@ { CONSOLE_HAS_METHOD (mswindows, own_selection); CONSOLE_HAS_METHOD (mswindows, disown_selection); + CONSOLE_HAS_METHOD (mswindows, selection_exists_p); CONSOLE_HAS_METHOD (mswindows, get_foreign_selection); + CONSOLE_HAS_METHOD (mswindows, available_selection_types); + CONSOLE_HAS_METHOD (mswindows, register_selection_data_type); + CONSOLE_HAS_METHOD (mswindows, selection_data_type_name); } void syms_of_select_mswindows (void) { - DEFSUBR (Fmswindows_set_clipboard); - DEFSUBR (Fmswindows_get_clipboard); - DEFSUBR (Fmswindows_selection_exists_p); - DEFSUBR (Fmswindows_delete_selection); } void vars_of_select_mswindows (void) { + /* Initialise Vhandle_alist */ + Vhandle_alist = Qnil; + staticpro (&Vhandle_alist); }