Index: w32select.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/w32select.c,v retrieving revision 1.32 diff -u -p -r1.32 w32select.c --- w32select.c 18 Apr 2004 18:34:03 -0000 1.32 +++ w32select.c 2 Jun 2004 15:17:41 -0000 @@ -41,6 +41,16 @@ static Lisp_Object Vselection_coding_sys /* Coding system for the next communicating with other Windows programs. */ static Lisp_Object Vnext_selection_coding_system; +/* Cached transfer parameters. */ +static Lisp_Object cached_coding_system; +static UINT codepage; +static LCID lcid; +static UINT clipboard_type; + +/* Constants, initialized dynamically later. */ +static LCID DEFAULT_LCID = LOCALE_NEUTRAL; +static UINT ANSICP, OEMCP; + /* Sequence number, used where possible to detect when we are pasting our own text. */ static DWORD last_clipboard_sequence_number; @@ -51,7 +61,17 @@ extern ClipboardSequence_Proc clipboard_ use data put on the clipboard by Emacs because the clipboard data could be MULEtilated by inappropriately chosen (next-)selection-coding-system. For this reason, we must store the - text *after* it was encoded/Unix-to-DOS-converted. */ + text *after* it was encoded/Unix-to-DOS-converted. + + FIXME: This doesn't work quite right, in the situation when the + last stored text is in CF_TEXT format and the format that we want + according to `selection-coding-system' is CF_UNICODETEXT. We'd + have to retrieve the CF_TEXT just for the comparison, which is + wastefull with delayed rendering. A better method is to check the + clipboard owner, but that requires that we *always* pass a HWND to + OpenClipboard(), and than preferably always the same HWND. This + will probably be easier once we implement delayed rendering + (WM_RENDERFORMAT) ourself, where this is also a requirement. */ static unsigned char *last_clipboard_text = NULL; static size_t clipboard_storage_size = 0; @@ -110,6 +130,136 @@ DEFUN ("w32-close-clipboard", Fw32_close #endif +static UINT +cp_from_locale (LCID lcid, UINT variant) +{ + char buffer[20] = ""; + UINT cp; + + GetLocaleInfo (lcid, variant, buffer, sizeof (buffer)); + cp = strtoul (buffer, NULL, 10); + + if (cp == CP_ACP) + return ANSICP; + else if (cp == CP_OEMCP) + return OEMCP; + else + return cp; +} + +static BOOL WINAPI +enum_locale_callback (/*const*/ char* loc_string) +{ + LCID this_lcid; + UINT this_codepage; + + this_lcid = strtoul (loc_string, NULL, 16); + + /* Is the wanted codepage the "ANSI" codepage for this locale? */ + this_codepage = cp_from_locale (this_lcid, LOCALE_IDEFAULTANSICODEPAGE); + if (this_codepage == codepage) + { + lcid = this_lcid; + clipboard_type = CF_TEXT; + return FALSE; /* Stop enumeration */ + } + + /* Is the wanted codepage the OEM codepage for this locale? */ + this_codepage = cp_from_locale (this_lcid, LOCALE_IDEFAULTCODEPAGE); + if (this_codepage == codepage) + { + lcid = this_lcid; + clipboard_type = CF_OEMTEXT; + return FALSE; /* Stop enumeration */ + } + + return TRUE; /* Continue enumeration */ +} + +static void +setup_parameters (void) +{ + const char *coding_name; + const char *cp; + char *end; + int slen; + Lisp_Object current_coding_system; + + CHECK_SYMBOL (Vselection_coding_system); + + /* One-time init. */ + if (DEFAULT_LCID == LOCALE_NEUTRAL) + { + DEFAULT_LCID = GetUserDefaultLCID (); + + /* Drop the sort order, so we compare this to CF_LOCALE objects + with the same fix on W'9x. */ + DEFAULT_LCID = MAKELCID (LANGIDFROMLCID (DEFAULT_LCID), SORT_DEFAULT); + + ANSICP = GetACP (); + OEMCP = GetOEMCP (); + } + + /* Check if we have it cached */ + current_coding_system = NILP (Vnext_selection_coding_system) ? + Vselection_coding_system : Vnext_selection_coding_system; + if (!NILP (cached_coding_system) + && EQ (cached_coding_system, current_coding_system)) + return; + cached_coding_system = current_coding_system; + + /* Set some sensible fallbacks */ + codepage = ANSICP; + lcid = LOCALE_NEUTRAL; + clipboard_type = CF_TEXT; + + /* Interpret the coding system symbol name */ + coding_name = SDATA (SYMBOL_NAME (current_coding_system)); + + /* "(.*-)?utf-16.*" -> CF_UNICODETEXT */ + cp = strstr (coding_name, "utf-16"); + if (cp != NULL && (cp == coding_name || cp[-1] == '-')) + { + clipboard_type = CF_UNICODETEXT; + return; + } + + /* "cp[0-9]+.*" or "windows-[0-9]+.*" -> CF_TEXT or CF_OEMTEXT */ + slen = strlen (coding_name); + if (slen >= 4 && coding_name[0] == 'c' && coding_name[1] == 'p') + cp = coding_name + 2; + else if (slen >= 10 && memcmp (coding_name, "windows-", 8) == 0) + cp = coding_name + 8; + else + return; + + end = (char*)cp; + codepage = strtol (cp, &end, 10); + + /* Error return from strtol() or number of digits < 2 -> Restore the + default and drop it. */ + if (codepage == 0 || (end-cp) < 2 ) + { + codepage = ANSICP; + return; + } + + /* Is it the current system default? */ + if (codepage == ANSICP) + { + /* clipboard_type = CF_TEXT; */ + return; + } + if (codepage == OEMCP) + { + clipboard_type = CF_OEMTEXT; + return; + } + + /* Else determine a suitable locale the hard way. */ + EnumSystemLocales (enum_locale_callback, LCID_INSTALLED); +} + DEFUN ("w32-set-clipboard-data", Fw32_set_clipboard_data, Sw32_set_clipboard_data, 1, 2, 0, doc: /* This sets the clipboard data to the given text. */) @@ -117,147 +267,176 @@ DEFUN ("w32-set-clipboard-data", Fw32_se Lisp_Object string, frame; { BOOL ok = TRUE; - HANDLE htext; + HANDLE htext, hlocale = NULL; int nbytes; int truelen, nlines = 0; + int require_encoding = 0; unsigned char *src; unsigned char *dst; + unsigned char *end; + UINT actual_clipboard_type; CHECK_STRING (string); if (!NILP (frame)) CHECK_LIVE_FRAME (frame); + setup_parameters (); + actual_clipboard_type = clipboard_type; + BLOCK_INPUT; nbytes = SBYTES (string) + 1; src = SDATA (string); - dst = src; - /* We need to know how many lines there are, since we need CRLF line - termination for compatibility with other Windows Programs. - avoid using strchr because it recomputes the length every time */ - while ((dst = memchr (dst, '\n', nbytes - (dst - src))) != NULL) + /* Check for non-ASCII characters. While we are at it, count the + number of LFs, so we know how many CRs we have to add later + on. */ + for (dst = src, end = src+nbytes; dst < end; dst++) { - nlines++; - dst++; + if (*dst == '\n') + nlines++; + else if (*dst >= 0x80 || *dst == 0) + { + require_encoding = 1; + break; + } } - { - /* Since we are now handling multilingual text, we must consider - encoding text for the clipboard. */ - int charset_info = find_charset_in_text (src, SCHARS (string), - nbytes, NULL, Qnil); - - if (charset_info == 0) - { - /* No multibyte character in OBJ. We need not encode it. */ + if (!require_encoding) + { + /* No multibyte character in OBJ. We need not encode it. */ - /* Need to know final size after CR chars are inserted (the - standard CF_TEXT clipboard format uses CRLF line endings, - while Emacs uses just LF internally). */ + /* Need to know final size after CR chars are inserted (the + standard CF_TEXT clipboard format uses CRLF line endings, + while Emacs uses just LF internally). */ - truelen = nbytes + nlines; + truelen = nbytes + nlines; - if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, truelen)) == NULL) + if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, truelen)) == NULL) goto error; - if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) - goto error; + if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) + goto error; - /* convert to CRLF line endings expected by clipboard */ - while (1) - { - unsigned char *next; - /* copy next line or remaining bytes including '\0' */ - next = _memccpy (dst, src, '\n', nbytes); - if (next) - { - /* copied one line ending with '\n' */ - int copied = next - dst; - nbytes -= copied; - src += copied; - /* insert '\r' before '\n' */ - next[-1] = '\r'; - next[0] = '\n'; - dst = next + 1; - } - else - /* copied remaining partial line -> now finished */ - break; - } + /* convert to CRLF line endings expected by clipboard */ + while (1) + { + unsigned char *next; + /* copy next line or remaining bytes including '\0' */ + next = _memccpy (dst, src, '\n', nbytes); + if (next) + { + /* copied one line ending with '\n' */ + int copied = next - dst; + nbytes -= copied; + src += copied; + /* insert '\r' before '\n' */ + next[-1] = '\r'; + next[0] = '\n'; + dst = next + 1; + } + else + /* copied remaining partial line -> now finished */ + break; + } - GlobalUnlock (htext); + GlobalUnlock (htext); - Vlast_coding_system_used = Qraw_text; - } - else - { - /* We must encode contents of OBJ to the selection coding - system. */ - int bufsize; - struct coding_system coding; - HANDLE htext2; + /* Truncate last_clipboard_text by setting it to the string + terminator. */ + if (last_clipboard_text) + last_clipboard_text[0] = last_clipboard_text[1] = '\0'; - if (NILP (Vnext_selection_coding_system)) - Vnext_selection_coding_system = Vselection_coding_system; - setup_coding_system - (Fcheck_coding_system (Vnext_selection_coding_system), &coding); - if (SYMBOLP (coding.pre_write_conversion) - && !NILP (Ffboundp (coding.pre_write_conversion))) - { - string = run_pre_post_conversion_on_str (string, &coding, 1); - src = SDATA (string); - nbytes = SBYTES (string); - } - coding.src_multibyte = 1; - coding.dst_multibyte = 0; - /* Need to set COMPOSITION_DISABLED, otherwise Emacs crashes in - encode_coding_iso2022 trying to dereference a null pointer. */ - coding.composing = COMPOSITION_DISABLED; - if (coding.type == coding_type_iso2022) - coding.flags |= CODING_FLAG_ISO_SAFE; - Vnext_selection_coding_system = Qnil; - coding.mode |= CODING_MODE_LAST_BLOCK; - bufsize = encoding_buffer_size (&coding, nbytes); - if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bufsize)) == NULL) - goto error; - if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) - goto error; - encode_coding (&coding, src, dst, nbytes, bufsize); - Vlast_coding_system_used = coding.symbol; + Vlast_coding_system_used = Qraw_text; + actual_clipboard_type = CF_TEXT; + } + else + { + /* We must encode contents of OBJ to the selection coding + system. */ + int bufsize; + struct coding_system coding; + HANDLE htext2; + + if (NILP (Vnext_selection_coding_system)) + Vnext_selection_coding_system = Vselection_coding_system; + setup_coding_system + (Fcheck_coding_system (Vnext_selection_coding_system), &coding); + if (SYMBOLP (coding.pre_write_conversion) + && !NILP (Ffboundp (coding.pre_write_conversion))) + { + string = run_pre_post_conversion_on_str (string, &coding, 1); + src = SDATA (string); + nbytes = SBYTES (string); + } + coding.src_multibyte = 1; + coding.dst_multibyte = 0; + /* Need to set COMPOSITION_DISABLED, otherwise Emacs crashes in + encode_coding_iso2022 trying to dereference a null pointer. */ + coding.composing = COMPOSITION_DISABLED; + if (coding.type == coding_type_iso2022) + coding.flags |= CODING_FLAG_ISO_SAFE; + Vnext_selection_coding_system = Qnil; + coding.mode |= CODING_MODE_LAST_BLOCK; + bufsize = encoding_buffer_size (&coding, nbytes) +2; + if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bufsize)) == NULL) + goto error; + if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) + goto error; + + encode_coding (&coding, src, dst, nbytes, bufsize-2); + Vlast_coding_system_used = coding.symbol; + + /* Add the string terminator. */ + dst[coding.produced] = dst[coding.produced+1] = '\0'; + + /* If clipboard sequence numbers are not supported, keep a copy for + later comparison. */ + if (!clipboard_sequence_fn) + { + /* Stash away the data we are about to put into the + clipboard, so we could later check inside + Fw32_get_clipboard_data whether the clipboard still + holds our data. */ + if (clipboard_storage_size < coding.produced+2) + { + clipboard_storage_size = coding.produced + 100; + last_clipboard_text = (char *) xrealloc (last_clipboard_text, + clipboard_storage_size); + } + if (last_clipboard_text) + memcpy (last_clipboard_text, dst, coding.produced+2); + } - /* If clipboard sequence numbers are not supported, keep a copy for - later comparison. */ - if (!clipboard_sequence_fn) - { - /* Stash away the data we are about to put into the - clipboard, so we could later check inside - Fw32_get_clipboard_data whether the clipboard still - holds our data. */ - if (clipboard_storage_size < coding.produced) - { - clipboard_storage_size = coding.produced + 100; - last_clipboard_text = (char *) xrealloc (last_clipboard_text, - clipboard_storage_size); - } - if (last_clipboard_text) - memcpy (last_clipboard_text, dst, coding.produced); - } + GlobalUnlock (htext); - GlobalUnlock (htext); + /* Shrink data block to actual size. */ + htext2 = GlobalReAlloc (htext, coding.produced+2, + GMEM_MOVEABLE | GMEM_DDESHARE); + if (htext2 != NULL) htext = htext2; - /* Shrink data block to actual size. */ - htext2 = GlobalReAlloc (htext, coding.produced, - GMEM_MOVEABLE | GMEM_DDESHARE); - if (htext2 != NULL) htext = htext2; - } - } + if (lcid != LOCALE_NEUTRAL && lcid != DEFAULT_LCID) + { + hlocale = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, sizeof (lcid)); + if (hlocale != NULL) + { + * (LCID*) GlobalLock (hlocale) = lcid; + GlobalUnlock (hlocale); + } + } + } if (!OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL)) goto error; - ok = EmptyClipboard () && SetClipboardData (CF_TEXT, htext); + ok = EmptyClipboard () && SetClipboardData (actual_clipboard_type, htext); + + if (ok && hlocale != NULL) + { + ok = ok && SetClipboardData (CF_LOCALE, hlocale); + if (!ok) EmptyClipboard (); + } CloseClipboard (); @@ -277,8 +456,12 @@ DEFUN ("w32-set-clipboard-data", Fw32_se ok = FALSE; if (htext) GlobalFree (htext); + if (hlocale) GlobalFree (hlocale); + + /* Truncate last_clipboard_text by setting it to the string + terminator. */ if (last_clipboard_text) - *last_clipboard_text = '\0'; + last_clipboard_text[0] = last_clipboard_text[1] = '\0'; last_clipboard_sequence_number = 0; @@ -296,16 +479,40 @@ DEFUN ("w32-get-clipboard-data", Fw32_ge { HANDLE htext; Lisp_Object ret = Qnil; + UINT actual_clipboard_type; + int use_configured_coding_system = 1; if (!NILP (frame)) CHECK_LIVE_FRAME (frame); + setup_parameters (); + actual_clipboard_type = clipboard_type; + BLOCK_INPUT; if (!OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL)) goto done; - if ((htext = GetClipboardData (CF_TEXT)) == NULL) + if ((htext = GetClipboardData (clipboard_type)) == NULL) + { + /* If we want CF_UNICODETEXT but can't get it, the current + coding system is useless. OTOH we can still try and decode + CF_TEXT based on the locale that the system gives us and that + we get down below. Note that Windows is documented to always + generate CF_LOCALE info automatically, so the locale handle + should be always present (actually this is not always true on + W'9x ;-(). */ + if (clipboard_type == CF_UNICODETEXT) + { + htext = GetClipboardData (CF_TEXT); + if (htext != NULL) + { + actual_clipboard_type = CF_TEXT; + use_configured_coding_system = 0; + } + } + } + if (htext == NULL) goto closeclip; { @@ -318,7 +525,10 @@ DEFUN ("w32-get-clipboard-data", Fw32_ge if ((src = (unsigned char *) GlobalLock (htext)) == NULL) goto closeclip; - nbytes = strlen (src); + if (actual_clipboard_type == CF_UNICODETEXT) + nbytes = (lstrlenW ((WCHAR *)src) + 1) * 2; + else + nbytes = strlen (src) + 1; /* If the text in clipboard is identical to what we put there last time w32_set_clipboard_data was called, pretend there's no @@ -329,34 +539,99 @@ DEFUN ("w32-get-clipboard-data", Fw32_ge && clipboard_sequence_fn () == last_clipboard_sequence_number) || (last_clipboard_text && clipboard_storage_size >= nbytes - && memcmp(last_clipboard_text, src, nbytes) == 0)) + && memcmp (last_clipboard_text, src, nbytes) == 0)) goto closeclip; - { - /* If the clipboard data contains any non-ascii code, we - need to decode it. */ - int i; + /* Drop the string terminator from here on. */ + nbytes -= actual_clipboard_type == CF_UNICODETEXT ? 2 : 1; - for (i = 0; i < nbytes; i++) - { - if (src[i] >= 0x80) - { - require_decoding = 1; - break; - } - } - } + /* If the clipboard data contains any non-ascii code, we need to + decode it with a coding system. + FIXME: Repeat the code for the Unicode case. */ + if (actual_clipboard_type == CF_UNICODETEXT) + require_decoding = 1; + else + { + int i; + + for (i = 0; i < nbytes; i++) + { + if (src[i] >= 0x80) + { + require_decoding = 1; + break; + } + } + } if (require_decoding) { int bufsize; unsigned char *buf; struct coding_system coding; + Lisp_Object coding_system = Qnil; + + /* `next-selection-coding-system' should override everything, + even when the locale passed by the system disagrees. The + only exception is when `next-selection-coding-system' + requested CF_UNICODETEXT and we couldn't get that. */ + if (use_configured_coding_system + && !NILP (Vnext_selection_coding_system)) + coding_system = Vnext_selection_coding_system; + + /* If we have CF_TEXT or CF_OEMTEXT, we want to check out + CF_LOCALE, too. */ + else if (actual_clipboard_type != CF_UNICODETEXT) + { + HANDLE hlocale; + LCID cb_lcid; + UINT cp; + + hlocale = GetClipboardData (CF_LOCALE); + if (hlocale != NULL) + { + cb_lcid = *(LCID*)GlobalLock (hlocale); + GlobalUnlock (hlocale); - if (NILP (Vnext_selection_coding_system)) - Vnext_selection_coding_system = Vselection_coding_system; - setup_coding_system - (Fcheck_coding_system (Vnext_selection_coding_system), &coding); + /* W'9x has garbage as the sort order (i.e. there is + another instance of the language id in the upper + word). We can just filter out the unneeded + mis-information to avoid irritations. */ + cb_lcid = MAKELCID (LANGIDFROMLCID (cb_lcid), SORT_DEFAULT); + } + else + cb_lcid = DEFAULT_LCID; + + /* If we are using fallback from CF_UNICODETEXT, we can't + use the configured coding system. Also we don't want + to use it, if the system has supplied us with a locale + and it is not just the system default. */ + if (!use_configured_coding_system || cb_lcid != DEFAULT_LCID) + { + cp = cp_from_locale (cb_lcid, + actual_clipboard_type == CF_TEXT + ? LOCALE_IDEFAULTANSICODEPAGE + : LOCALE_IDEFAULTCODEPAGE); + /* If it's just our current setting anyway, use the + coding system that the user has selected. + Otherwise create a new spec to match the locale + that was specified by the other side or the + system. */ + if (!use_configured_coding_system || cp != codepage) + { + char buffer[30]; + sprintf (buffer, "cp%d-dos", (int) cp); + coding_system = intern (buffer); + /* FIXME: We should verify that this coding system + actually exists. */ + } + } + } + + if (NILP (coding_system)) + coding_system = Vselection_coding_system; + + setup_coding_system (Fcheck_coding_system (coding_system), &coding); coding.src_multibyte = 0; coding.dst_multibyte = 1; Vnext_selection_coding_system = Qnil; @@ -459,8 +734,12 @@ and t is the same as `SECONDARY'. */) if (OpenClipboard (NULL)) { int format = 0; + setup_parameters (); while (format = EnumClipboardFormats (format)) - if (format == CF_TEXT) + /* Check CF_TEXT in addition to clipboard_type, because we + fall back on that if CF_UNICODETEXT is not + available. */ + if (format == clipboard_type || format == CF_TEXT) { val = Qt; break; @@ -500,6 +779,8 @@ set to nil. */); Vnext_selection_coding_system = Qnil; QCLIPBOARD = intern ("CLIPBOARD"); staticpro (&QCLIPBOARD); + + cached_coding_system = Qnil; staticpro (&cached_coding_system); } /* arch-tag: c96e9724-5eb1-4dad-be07-289f092fd2af