diff options
author | Jason Rumney <jasonr@gnu.org> | 2005-02-15 23:19:26 +0000 |
---|---|---|
committer | Jason Rumney <jasonr@gnu.org> | 2005-02-15 23:19:26 +0000 |
commit | 52c7f9eea2faf4094df00bf30d2d7cd2210c8cb5 (patch) | |
tree | 75db6243e27131ee03d6c73d2323841e627b19e7 /src/w32select.c | |
parent | d02d0705b396b0deed399af01a370e8c9de6f7e6 (diff) | |
download | emacs-52c7f9eea2faf4094df00bf30d2d7cd2210c8cb5.tar.gz |
2004-11-08 Benjamin Riefenstahl <Benjamin.Riefenstahl@epost.de>
* w32select.c: Summary: Thorough rework to implement Unicode
clipboard operations and delayed rendering.
Drop last_clipboard_text and related code, keep track of
ownership via clipboard_owner instead. Drop old #if0
sections.
(DEFAULT_LCID, ANSICP, OEMCP, QUNICODE, QANSICP, QOEMCP)
(clipboard_owner, modifying_clipboard, cfg_coding_system)
(cfg_codepage, cfg_lcid, cfg_clipboard_type, current_text)
(current_coding_system, current_requires_encoding)
(current_num_nls, current_clipboard_type, current_lcid): New
static variables.
(convert_to_handle_as_ascii, convert_to_handle_as_coded)
(render, render_all, run_protected, lisp_error_handler)
(owner_callback, create_owner, setup_config)
(enum_locale_callback, cp_from_locale, coding_from_cp): New
local functions.
(term_w32select, globals_of_w32select): New global functions.
(Fw32_set_clipboard_data): Ignore parameter FRAME, use
clipboard_owner instead. Use delayed rendering and provide
all text formats. Provide CF_LOCALE if necessary.
(Fw32_get_clipboard_data): Handle CF_UNICODETEXT and
CF_LOCALE. Fall back to CF_TEXT, if CF_UNICODETEXT is not
available. Force DOS line-ends for decoding.
(Fx_selection_exists_p): Handle CF_UNICODETEXT.
(syms_of_w32select): Init and register new variables.
* w32.h: Add prototypes for globals_of_w32select and
term_w32select. Make the neighboring K&R declarations into
prototypes, too.
* emacs.c: Include w32.h to get function prototypes.
(main): Call globals_of_w32select.
* w32.c (term_ntproc): Call term_w32select.
* mule-cmds.el (set-locale-environment): Remove call to
set-selection-coding-system on Windows.
* s/ms-w32.h: Guard MSC-specific #pragmas with an #ifdef.
Diffstat (limited to 'src/w32select.c')
-rw-r--r-- | src/w32select.c | 1095 |
1 files changed, 846 insertions, 249 deletions
diff --git a/src/w32select.c b/src/w32select.c index 20f7cfc457f..e562dc7efbb 100644 --- a/src/w32select.c +++ b/src/w32select.c @@ -1,5 +1,5 @@ /* Selection processing for Emacs on the Microsoft W32 API. - Copyright (C) 1993, 1994 Free Software Foundation. + Copyright (C) 1993, 1994, 2004 Free Software Foundation. This file is part of GNU Emacs. @@ -18,273 +18,744 @@ along with GNU Emacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* Written by Kevin Gallo */ - +/* Written by Kevin Gallo, Benjamin Riefenstahl */ + + +/* + * Notes on usage of selection-coding-system and + * next-selection-coding-system on MS Windows: + * + * The selection coding system variables apply only to the version of + * the clipboard data that is closest in type, i.e. when a 16-bit + * Unicode coding system is given, they apply to he Unicode clipboard + * (CF_UNICODETEXT), when a well-known console codepage is given, they + * apply to the console version of the clipboard data (CF_OEMTEXT), + * else they apply to the normal 8-bit text clipboard (CF_TEXT). + * + * When pasting (getting data from the OS), the clipboard format that + * matches the {next-}selection-coding-system is retrieved. If + * Unicode is requested, but not available, 8-bit text (CF_TEXT) is + * used. In all other cases the OS will transparently convert + * formats, so no other fallback is needed. + * + * When copying or cutting (sending data to the OS), the data is + * announced and stored internally, but only actually rendered on + * request. The requester determines the format provided. The + * {next-}selection-coding-system is only used, when its corresponding + * clipboard type matches the type requested. + * + * Scenarios to use the facilities for customizing the selection + * coding system are: + * + * ;; Generally use KOI8-R instead of the russian MS codepage for + * ;; the 8-bit clipboard. + * (set-selection-coding-system 'koi8-r-dos) + * + * Or + * + * ;; Create a special clipboard copy function that uses codepage + * ;; 1253 (Greek) to copy Greek text to a specific non-Unicode + * ;; application. + * (defun greek-copy (beg end) + * (interactive "r") + * (set-next-selection-coding-system 'cp1253-dos) + * (copy-region-as-kill beg end)) + * (global-set-key "\C-c\C-c" 'greek-copy) + */ + +/* + * Ideas for further directions: + * + * The encoding and decoding routines could be moved to Lisp code + * similar to how xselect.c does it (using well-known routine names + * for the delayed rendering). If the definition of which clipboard + * types should be supported is also moved to Lisp, functionality + * could be expanded to CF_HTML, CF_RTF and maybe other types. + */ + #include <config.h> #include "lisp.h" #include "w32term.h" /* for all of the w32 includes */ -#include "dispextern.h" /* frame.h seems to want this */ -#include "keyboard.h" -#include "frame.h" /* Need this to get the X window of selected_frame */ +#include "w32heap.h" /* os_subtype */ #include "blockinput.h" -#include "buffer.h" +#include "keyboard.h" /* cmd_error_internal() */ #include "charset.h" #include "coding.h" #include "composite.h" + +static HGLOBAL convert_to_handle_as_ascii (void); +static HGLOBAL convert_to_handle_as_coded (Lisp_Object coding_system); +static Lisp_Object render (Lisp_Object oformat); +static Lisp_Object render_locale (void); +static Lisp_Object render_all (void); +static void run_protected (Lisp_Object (*code) (), Lisp_Object arg); +static Lisp_Object lisp_error_handler (Lisp_Object error); +static LRESULT CALLBACK owner_callback (HWND win, UINT msg, + WPARAM wp, LPARAM lp); +static HWND create_owner (void); + +static void setup_config (void); +static BOOL WINAPI enum_locale_callback (/*const*/ char* loc_string); +static UINT cp_from_locale (LCID lcid, UINT format); +static Lisp_Object coding_from_cp (UINT codepage); + + +/* A remnant from X11: Symbol for the CLIPBORD selection type. Other + selections are not used on Windows, so we don't need symbols for + PRIMARY and SECONDARY. */ Lisp_Object QCLIPBOARD; -/* Coding system for communicating with other Windows programs via the +/* Coding system for communicating with other programs via the clipboard. */ static Lisp_Object Vselection_coding_system; -/* Coding system for the next communicating with other Windows programs. */ +/* Coding system for the next communication with other programs. */ static Lisp_Object Vnext_selection_coding_system; -/* Sequence number, used where possible to detect when we are pasting - our own text. */ -static DWORD last_clipboard_sequence_number; -extern ClipboardSequence_Proc clipboard_sequence_fn; - -/* The last text we put into the clipboard. This is used when the OS - does not support sequence numbers (NT4, 95). It is undesirable to - 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. */ -static unsigned char *last_clipboard_text = NULL; -static size_t clipboard_storage_size = 0; - -#if 0 -DEFUN ("w32-open-clipboard", Fw32_open_clipboard, Sw32_open_clipboard, 0, 1, 0, - doc: /* This opens the clipboard with the given frame pointer. */) - (frame) - Lisp_Object frame; +/* Internal pseudo-constants, initialized in globals_of_w32select() + based on current system parameters. */ +static LCID DEFAULT_LCID; +static UINT ANSICP, OEMCP; +static Lisp_Object QUNICODE, QANSICP, QOEMCP; + +/* A hidden window just for the clipboard management. */ +static HWND clipboard_owner; +/* A flag to tell WM_DESTROYCLIPBOARD who is to blame this time (just + checking GetClipboardOwner() doesn't work, sadly). */ +static int modifying_clipboard = 0; + +/* Configured transfer parameters, based on the last inspection of + selection-coding-system. */ +static Lisp_Object cfg_coding_system; +static UINT cfg_codepage; +static LCID cfg_lcid; +static UINT cfg_clipboard_type; + +/* The current state for delayed rendering. */ +static Lisp_Object current_text; +static Lisp_Object current_coding_system; +static int current_requires_encoding, current_num_nls; +static UINT current_clipboard_type; +static LCID current_lcid; + +#if TRACE +#define ONTRACE(stmt) stmt +#else +#define ONTRACE(stmt) /*stmt*/ +#endif + + +/* This function assumes that there is no multibyte character in + current_text, so we can short-cut encoding. */ + +static HGLOBAL +convert_to_handle_as_ascii (void) { - BOOL ok = FALSE; + HGLOBAL htext = NULL; + int nbytes; + int truelen; + unsigned char *src; + unsigned char *dst; - if (!NILP (frame)) - CHECK_LIVE_FRAME (frame); + ONTRACE (fprintf (stderr, "convert_to_handle_as_ascii\n")); - BLOCK_INPUT; + nbytes = SBYTES (current_text) + 1; + src = SDATA (current_text); - ok = OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL); + /* We need to add to the size the number of LF chars where we have + to insert CR chars (the standard CF_TEXT clipboard format uses + CRLF line endings, while Emacs uses just LF internally). */ - UNBLOCK_INPUT; + truelen = nbytes + current_num_nls; + + if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, truelen)) == NULL) + return NULL; - return (ok ? frame : Qnil); + if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) + { + GlobalFree (htext); + return NULL; + } + + /* 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); + + return htext; } -DEFUN ("w32-empty-clipboard", Fw32_empty_clipboard, - Sw32_empty_clipboard, 0, 0, 0, - doc: /* Empty the clipboard. -Assigns ownership of the clipboard to the window which opened it. */) - () +/* This function assumes that there are multibyte or NUL characters in + current_text, or that we need to construct Unicode. It runs the + text through the encoding machinery. */ + +static HGLOBAL +convert_to_handle_as_coded (Lisp_Object coding_system) { - BOOL ok = FALSE; + HGLOBAL htext = NULL, htext2; + int nbytes; + unsigned char *src; + unsigned char *dst = NULL; + int bufsize; + struct coding_system coding; + Lisp_Object string = Qnil; + + ONTRACE (fprintf (stderr, "convert_to_handle_as_coded: %s\n", + SDATA (SYMBOL_NAME (coding_system)))); + + setup_coding_system (Fcheck_coding_system (coding_system), &coding); + 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; + coding.mode |= CODING_MODE_LAST_BLOCK; + /* Force DOS line-ends. */ + coding.eol_type = CODING_EOL_CRLF; + + if (SYMBOLP (coding.pre_write_conversion) + && !NILP (Ffboundp (coding.pre_write_conversion))) + string = run_pre_post_conversion_on_str (current_text, &coding, 1); + else + string = current_text; + + nbytes = SBYTES (string); + src = SDATA (string); - BLOCK_INPUT; + bufsize = encoding_buffer_size (&coding, nbytes) +2; + htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bufsize); - ok = EmptyClipboard (); + if (htext != NULL) + dst = (unsigned char *) GlobalLock (htext); - UNBLOCK_INPUT; + if (dst != NULL) + { + encode_coding (&coding, src, dst, nbytes, bufsize-2); + /* Add the string terminator. Add two NULs in case we are + producing Unicode here. */ + dst[coding.produced] = dst[coding.produced+1] = '\0'; + } - return (ok ? Qt : Qnil); + if (dst != NULL) + GlobalUnlock (htext); + + if (htext != NULL) + { + /* Shrink data block to actual size. */ + htext2 = GlobalReAlloc (htext, coding.produced+2, + GMEM_MOVEABLE | GMEM_DDESHARE); + if (htext2 != NULL) htext = htext2; + } + + return htext; } -DEFUN ("w32-close-clipboard", Fw32_close_clipboard, - Sw32_close_clipboard, 0, 0, 0, - doc: /* Close the clipboard. */) - () +static Lisp_Object +render (Lisp_Object oformat) { - BOOL ok = FALSE; + HGLOBAL htext = NULL; + UINT format = XFASTINT (oformat); + + ONTRACE (fprintf (stderr, "render\n")); + + if (NILP (current_text)) + return Qnil; + + if (current_requires_encoding || format == CF_UNICODETEXT) + { + if (format == current_clipboard_type) + htext = convert_to_handle_as_coded (current_coding_system); + else + switch (format) + { + case CF_UNICODETEXT: + htext = convert_to_handle_as_coded (QUNICODE); + break; + case CF_TEXT: + case CF_OEMTEXT: + { + Lisp_Object cs; + cs = coding_from_cp (cp_from_locale (current_lcid, format)); + htext = convert_to_handle_as_coded (cs); + break; + } + } + } + else + htext = convert_to_handle_as_ascii (); + + ONTRACE (fprintf (stderr, "render: htext = 0x%08X\n", (unsigned) htext)); + + if (htext == NULL) + return Qnil; + + if (SetClipboardData (format, htext) == NULL) + { + GlobalFree(htext); + return Qnil; + } + + return Qt; +} + +static Lisp_Object +render_locale (void) +{ + HANDLE hlocale = NULL; + LCID * lcid_ptr; + + ONTRACE (fprintf (stderr, "render_locale\n")); + + if (current_lcid == LOCALE_NEUTRAL || current_lcid == DEFAULT_LCID) + return Qt; + + hlocale = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, sizeof (current_lcid)); + if (hlocale == NULL) + return Qnil; + + if ((lcid_ptr = (LCID *) GlobalLock (hlocale)) == NULL) + { + GlobalFree(hlocale); + return Qnil; + } + + *lcid_ptr = current_lcid; + GlobalUnlock (hlocale); + + if (SetClipboardData (CF_LOCALE, hlocale) == NULL) + { + GlobalFree(hlocale); + return Qnil; + } + + return Qt; +} + +/* At the end of the program, we want to ensure that our clipboard + data survives us. This code will do that. */ + +static Lisp_Object +render_all (void) +{ + ONTRACE (fprintf (stderr, "render_all\n")); + + /* According to the docs we should not call OpenClipboard() here, + but testing on W2K and working code in other projects shows that + it is actually necessary. */ + + OpenClipboard (NULL); + + /* There is no usefull means to report errors here, there are none + expected anyway, and even if there were errors, they wouldn't do + any harm. So we just go ahead and do what has to be done without + bothering with error handling. */ + + ++modifying_clipboard; + EmptyClipboard (); + --modifying_clipboard; + + /* For text formats that we don't render here, the OS can use its + own translation rules instead, so we don't really need to offer + everything. To minimize memory consumption we cover three + possible situations based on our primary format as detected from + selection-coding-system (see setup_config()): + + - Post CF_TEXT only. Let the OS convert to CF_OEMTEXT and the OS + (on NT) or the application (on 9x/Me) convert to + CF_UNICODETEXT. + + - Post CF_OEMTEXT only. Similar automatic conversions happen as + for CF_TEXT. + + - Post CF_UNICODETEXT + CF_TEXT. 9x itself ignores + CF_UNICODETEXT, even though some applications can still handle + it. + + Note 1: We render the less capable CF_TEXT *before* the more + capable CF_UNICODETEXT, to prevent clobbering through automatic + conversions, just in case. + + Note 2: We could check os_subtype here and only render the + additional CF_TEXT on 9x/Me. But OTOH with + current_clipboard_type == CF_UNICODETEXT we don't involve the + automatic conversions anywhere else, so to get consistent + results, we probably don't want to rely on it here either. */ + + render_locale(); + + if (current_clipboard_type == CF_UNICODETEXT) + render (make_number (CF_TEXT)); + render (make_number (current_clipboard_type)); + + CloseClipboard (); + + return Qnil; +} + +static void +run_protected (Lisp_Object (*code) (), Lisp_Object arg) +{ + /* FIXME: This works but it doesn't feel right. Too much fiddling + with global variables and calling strange looking functions. Is + this really the right way to run Lisp callbacks? */ + + extern int waiting_for_input; + int owfi; BLOCK_INPUT; - ok = CloseClipboard (); + /* Fsignal calls abort() if it sees that waiting_for_input is + set. */ + owfi = waiting_for_input; + waiting_for_input = 0; + + internal_condition_case_1 (code, arg, Qt, lisp_error_handler); + + waiting_for_input = owfi; UNBLOCK_INPUT; +} - return (ok ? Qt : Qnil); +static Lisp_Object +lisp_error_handler (Lisp_Object error) +{ + Vsignaling_function = Qnil; + cmd_error_internal (error, "Error in delayed clipboard rendering: "); + Vinhibit_quit = Qt; + return Qt; +} + + +static LRESULT CALLBACK +owner_callback (HWND win, UINT msg, WPARAM wp, LPARAM lp) +{ + switch (msg) + { + case WM_RENDERFORMAT: + ONTRACE (fprintf (stderr, "WM_RENDERFORMAT\n")); + run_protected (render, make_number (wp)); + return 0; + + case WM_RENDERALLFORMATS: + ONTRACE (fprintf (stderr, "WM_RENDERALLFORMATS\n")); + run_protected (render_all, Qnil); + return 0; + + case WM_DESTROYCLIPBOARD: + if (!modifying_clipboard) + { + ONTRACE (fprintf (stderr, "WM_DESTROYCLIPBOARD (other)\n")); + current_text = Qnil; + current_coding_system = Qnil; + } + else + { + ONTRACE (fprintf (stderr, "WM_DESTROYCLIPBOARD (self)\n")); + } + return 0; + + case WM_DESTROY: + if (win == clipboard_owner) + clipboard_owner = NULL; + break; + } + + return DefWindowProc (win, msg, wp, lp); +} + +static HWND +create_owner (void) +{ + static const char CLASSNAME[] = "Emacs Clipboard"; + WNDCLASS wc; + + memset (&wc, 0, sizeof (wc)); + wc.lpszClassName = CLASSNAME; + wc.lpfnWndProc = owner_callback; + RegisterClass (&wc); + + return CreateWindow (CLASSNAME, CLASSNAME, 0, 0, 0, 0, 0, NULL, NULL, + NULL, NULL); +} + +/* Called on exit by term_ntproc() in w32.c */ + +void +term_w32select (void) +{ + /* This is needed to trigger WM_RENDERALLFORMATS. */ + if (clipboard_owner != NULL) + DestroyWindow (clipboard_owner); +} + +static void +setup_config (void) +{ + const char *coding_name; + const char *cp; + char *end; + int slen; + Lisp_Object new_coding_system; + + CHECK_SYMBOL (Vselection_coding_system); + + /* Check if we have it cached */ + new_coding_system = NILP (Vnext_selection_coding_system) ? + Vselection_coding_system : Vnext_selection_coding_system; + if (!NILP (cfg_coding_system) + && EQ (cfg_coding_system, new_coding_system)) + return; + cfg_coding_system = new_coding_system; + + /* Set some sensible fallbacks */ + cfg_codepage = ANSICP; + cfg_lcid = LOCALE_NEUTRAL; + cfg_clipboard_type = CF_TEXT; + + /* Interpret the coding system symbol name */ + coding_name = SDATA (SYMBOL_NAME (cfg_coding_system)); + + /* "(.*-)?utf-16.*" -> CF_UNICODETEXT */ + cp = strstr (coding_name, "utf-16"); + if (cp != NULL && (cp == coding_name || cp[-1] == '-')) + { + cfg_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; + cfg_codepage = strtol (cp, &end, 10); + + /* Error return from strtol() or number of digits < 2 -> Restore the + default and drop it. */ + if (cfg_codepage == 0 || (end-cp) < 2 ) + { + cfg_codepage = ANSICP; + return; + } + + /* Is it the currently active system default? */ + if (cfg_codepage == ANSICP) + { + /* cfg_clipboard_type = CF_TEXT; */ + return; + } + if (cfg_codepage == OEMCP) + { + cfg_clipboard_type = CF_OEMTEXT; + return; + } + + /* Else determine a suitable locale the hard way. */ + EnumSystemLocales (enum_locale_callback, LCID_INSTALLED); +} + +static BOOL WINAPI +enum_locale_callback (/*const*/ char* loc_string) +{ + LCID lcid; + UINT codepage; + + lcid = strtoul (loc_string, NULL, 16); + + /* Is the wanted codepage the "ANSI" codepage for this locale? */ + codepage = cp_from_locale (lcid, CF_TEXT); + if (codepage == cfg_codepage) + { + cfg_lcid = lcid; + cfg_clipboard_type = CF_TEXT; + return FALSE; /* Stop enumeration */ + } + + /* Is the wanted codepage the OEM codepage for this locale? */ + codepage = cp_from_locale (lcid, CF_OEMTEXT); + if (codepage == cfg_codepage) + { + cfg_lcid = lcid; + cfg_clipboard_type = CF_OEMTEXT; + return FALSE; /* Stop enumeration */ + } + + return TRUE; /* Continue enumeration */ +} + +static UINT +cp_from_locale (LCID lcid, UINT format) +{ + char buffer[20] = ""; + UINT variant, cp; + + variant = + format == CF_TEXT ? LOCALE_IDEFAULTANSICODEPAGE : LOCALE_IDEFAULTCODEPAGE; + + 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 Lisp_Object +coding_from_cp (UINT codepage) +{ + char buffer[30]; + sprintf (buffer, "cp%d-dos", (int) codepage); + return intern (buffer); + /* We don't need to check that this coding system exists right here, + because that is done when the coding system is actually + instantiated, i.e. it is passed through Fcheck_coding_system() + there. */ } -#endif 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. */) - (string, frame) - Lisp_Object string, frame; + (string, ignored) + Lisp_Object string, ignored; { BOOL ok = TRUE; - HANDLE htext; int nbytes; - int truelen, nlines = 0; unsigned char *src; unsigned char *dst; + unsigned char *end; + + /* This parameter used to be the current frame, but we don't use + that any more. */ + (void) ignored; CHECK_STRING (string); - if (!NILP (frame)) - CHECK_LIVE_FRAME (frame); + setup_config (); + current_text = string; + current_coding_system = cfg_coding_system; + current_clipboard_type = cfg_clipboard_type; + current_lcid = cfg_lcid; + current_num_nls = 0; + current_requires_encoding = 0; + BLOCK_INPUT; - /* Include the terminating NULL character in the source of - conversion. */ - nbytes = SBYTES (string) + 1; + /* Check for non-ASCII characters. While we are at it, count the + number of LFs, so we know how many CRs we will have to add later + (just in the case where we can use our internal ASCII rendering, + see code and comment in convert_to_handle_as_ascii() above). */ + nbytes = SBYTES (string); 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) + for (dst = src, end = src+nbytes; dst < end; dst++) { - nlines++; - dst++; + if (*dst == '\n') + current_num_nls++; + else if (*dst >= 0x80 || *dst == 0) + { + current_requires_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. */ - - /* 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; - - if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, truelen)) == 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; - } - - GlobalUnlock (htext); + if (!current_requires_encoding) + { + /* If all we have is ASCII we don't need to pretend we offer + anything fancy. */ + current_coding_system = Qraw_text; + current_clipboard_type = CF_TEXT; + current_lcid = LOCALE_NEUTRAL; + } - 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; - - 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); - /* Include the terminating NULL character in the source of - conversion. */ - nbytes = SBYTES (string) + 1; - } - 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; + if (!OpenClipboard (clipboard_owner)) + goto error; - /* 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); - } + ++modifying_clipboard; + ok = EmptyClipboard (); + --modifying_clipboard; - GlobalUnlock (htext); + /* If we have something non-ASCII we may want to set a locale. We + do that directly (non-delayed), as it's just a small bit. */ + if (ok) + ok = !NILP(render_locale()); - /* Shrink data block to actual size. */ - htext2 = GlobalReAlloc (htext, coding.produced, - GMEM_MOVEABLE | GMEM_DDESHARE); - if (htext2 != NULL) htext = htext2; - } - } + if (ok) + { + if (clipboard_owner == NULL) + { + /* If for some reason we don't have a clipboard_owner, we + just set the text format as chosen by the configuration + and than forget about the whole thing. */ + ok = !NILP(render (make_number (current_clipboard_type))); + current_text = Qnil; + current_coding_system = Qnil; + } + else + { + /* Advertise all supported formats so that whatever the + requester chooses, only one encoding step needs to be + made. This is intentionally different from what we do in + the handler for WM_RENDERALLFORMATS. */ + SetClipboardData (CF_UNICODETEXT, NULL); + SetClipboardData (CF_TEXT, NULL); + SetClipboardData (CF_OEMTEXT, NULL); + } + } - if (!OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL)) - goto error; + CloseClipboard (); - ok = EmptyClipboard () && SetClipboardData (CF_TEXT, htext); + /* With delayed rendering we haven't really "used" this coding + system yet, and it's even unclear if we ever will. But this is a + way to tell the upper level what we *would* use under ideal + circumstances. - CloseClipboard (); + We don't signal the actually used coding-system later when we + finally render, because that can happen at any time and we don't + want to disturb the "foreground" action. */ + if (ok) + Vlast_coding_system_used = current_coding_system; - /* Common sense says to read the sequence number inside the - OpenClipboard/ CloseClipboard block to avoid race conditions - where another app puts something on the clipboard straight after - us. But experience suggests that the sequence number from the - SetClipboardData is not allocated until we close the clipboard! - Since clipboard operations are normally user-driven, the race - condition is probably not going to really happen. */ - if (clipboard_sequence_fn) - last_clipboard_sequence_number = clipboard_sequence_fn (); + Vnext_selection_coding_system = Qnil; if (ok) goto done; error: ok = FALSE; - if (htext) GlobalFree (htext); - if (last_clipboard_text) - *last_clipboard_text = '\0'; - - last_clipboard_sequence_number = 0; + current_text = Qnil; + current_coding_system = Qnil; done: UNBLOCK_INPUT; @@ -292,24 +763,52 @@ DEFUN ("w32-set-clipboard-data", Fw32_set_clipboard_data, return (ok ? string : Qnil); } + DEFUN ("w32-get-clipboard-data", Fw32_get_clipboard_data, Sw32_get_clipboard_data, 0, 1, 0, doc: /* This gets the clipboard data in text format. */) - (frame) - Lisp_Object frame; + (ignored) + Lisp_Object ignored; { - HANDLE htext; + HGLOBAL htext; Lisp_Object ret = Qnil; + UINT actual_clipboard_type; + int use_configured_coding_system = 1; + + /* This parameter used to be the current frame, but we don't use + that any more. */ + (void) ignored; - if (!NILP (frame)) - CHECK_LIVE_FRAME (frame); + /* Don't pass our own text from the clipboard (which might be + troublesome if the killed text includes null characters). */ + if (!NILP (current_text)) + return ret; + + setup_config (); + actual_clipboard_type = cfg_clipboard_type; BLOCK_INPUT; - if (!OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL)) + if (!OpenClipboard (clipboard_owner)) goto done; - if ((htext = GetClipboardData (CF_TEXT)) == NULL) + if ((htext = GetClipboardData (actual_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. */ + if (actual_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; { @@ -322,53 +821,107 @@ DEFUN ("w32-get-clipboard-data", Fw32_get_clipboard_data, if ((src = (unsigned char *) GlobalLock (htext)) == NULL) goto closeclip; - nbytes = strlen (src); - - /* If the text in clipboard is identical to what we put there - last time w32_set_clipboard_data was called, pretend there's no - data in the clipboard. This is so we don't pass our own text - from the clipboard (which might be troublesome if the killed - text includes null characters). */ - if ((clipboard_sequence_fn - && clipboard_sequence_fn () == last_clipboard_sequence_number) - || (last_clipboard_text - && clipboard_storage_size >= nbytes - && memcmp(last_clipboard_text, src, nbytes) == 0)) - goto closeclip; + /* If the clipboard data contains any non-ascii code, we need to + decode it with a coding system. */ + if (actual_clipboard_type == CF_UNICODETEXT) + { + nbytes = lstrlenW ((WCHAR *)src) * 2; + require_decoding = 1; + } + else + { + int i; - { - /* If the clipboard data contains any non-ascii code, we - need to decode it. */ - int i; + nbytes = strlen (src); - for (i = 0; i < nbytes; i++) - { - if (src[i] >= 0x80) - { - require_decoding = 1; - break; - } - } - } + 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) + { + HGLOBAL hlocale; + LCID lcid = DEFAULT_LCID; + UINT cp; + + /* Documentation says that the OS always generates + CF_LOCALE info automatically, so the locale handle + should always be present. Fact is that this is not + always true on 9x ;-(. */ + hlocale = GetClipboardData (CF_LOCALE); + if (hlocale != NULL) + { + const LCID * lcid_ptr; + lcid_ptr = (const LCID *) GlobalLock (hlocale); + if (lcid_ptr != NULL) + { + lcid = *lcid_ptr; + GlobalUnlock (hlocale); + } + + /* 9x has garbage as the sort order (to be exact there + is another instance of the language id in the upper + word). We don't care about sort order anyway, so + we just filter out the unneeded mis-information to + avoid irritations. */ + lcid = MAKELCID (LANGIDFROMLCID (lcid), SORT_DEFAULT); + } + + /* 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 || lcid != DEFAULT_LCID) + { + cp = cp_from_locale (lcid, actual_clipboard_type); + /* If it's just our current standard 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 != cfg_codepage) + coding_system = coding_from_cp (cp); + } + } + + if (NILP (coding_system)) + coding_system = Vselection_coding_system; + Vnext_selection_coding_system = Qnil; - if (NILP (Vnext_selection_coding_system)) - Vnext_selection_coding_system = Vselection_coding_system; - setup_coding_system - (Fcheck_coding_system (Vnext_selection_coding_system), &coding); + setup_coding_system (Fcheck_coding_system (coding_system), &coding); coding.src_multibyte = 0; coding.dst_multibyte = 1; - Vnext_selection_coding_system = Qnil; coding.mode |= CODING_MODE_LAST_BLOCK; /* We explicitely disable composition handling because selection data should not contain any composition sequence. */ coding.composing = COMPOSITION_DISABLED; + /* Force DOS line-ends. */ + coding.eol_type = CODING_EOL_CRLF; + bufsize = decoding_buffer_size (&coding, nbytes); buf = (unsigned char *) xmalloc (bufsize); decode_coding (&coding, src, buf, nbytes, bufsize); @@ -382,10 +935,13 @@ DEFUN ("w32-get-clipboard-data", Fw32_get_clipboard_data, } else { - /* Need to know final size after CR chars are removed because we - can't change the string size manually, and doing an extra - copy is silly. Note that we only remove CR when it appears - as part of CRLF. */ + /* FIXME: We may want to repeat the code in this branch for + the Unicode case. */ + + /* Need to know final size after CR chars are removed because + we can't change the string size manually, and doing an + extra copy is silly. We only remove CR when it appears as + part of CRLF. */ truelen = nbytes; dst = src; @@ -462,9 +1018,14 @@ and t is the same as `SECONDARY'. */) if (OpenClipboard (NULL)) { - int format = 0; - while (format = EnumClipboardFormats (format)) - if (format == CF_TEXT) + UINT format = 0; + setup_config (); + while ((format = EnumClipboardFormats (format))) + /* Check CF_TEXT in addition to cfg_clipboard_type, + because we can fall back on that if CF_UNICODETEXT is + not available. Actually a check for CF_TEXT only + should be enough. */ + if (format == cfg_clipboard_type || format == CF_TEXT) { val = Qt; break; @@ -476,24 +1037,25 @@ and t is the same as `SECONDARY'. */) return Qnil; } +/* One-time init. Called in the un-dumped Emacs, but not in the + dumped version. */ + void syms_of_w32select () { -#if 0 - defsubr (&Sw32_open_clipboard); - defsubr (&Sw32_empty_clipboard); - defsubr (&Sw32_close_clipboard); -#endif defsubr (&Sw32_set_clipboard_data); defsubr (&Sw32_get_clipboard_data); defsubr (&Sx_selection_exists_p); DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system, doc: /* Coding system for communicating with other programs. -When sending or receiving text via cut_buffer, selection, and clipboard, -the text is encoded or decoded by this coding system. -The default value is `iso-latin-1-dos'. */); - Vselection_coding_system = intern ("iso-latin-1-dos"); +When sending or receiving text via cut_buffer, selection, and +clipboard, the text is encoded or decoded by this coding system. +The default value is the current system default encoding on 9x/Me and +`utf-16le-dos' (Unicode) on NT/W2K/XP. */); + /* The actual value is set dynamically in the dumped Emacs, see + below. */ + Vselection_coding_system = Qnil; DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system, doc: /* Coding system for the next communication with other programs. @@ -504,6 +1066,41 @@ set to nil. */); Vnext_selection_coding_system = Qnil; QCLIPBOARD = intern ("CLIPBOARD"); staticpro (&QCLIPBOARD); + + cfg_coding_system = Qnil; staticpro (&cfg_coding_system); + current_text = Qnil; staticpro (¤t_text); + current_coding_system = Qnil; staticpro (¤t_coding_system); + + QUNICODE = intern ("utf-16le-dos"); staticpro (&QUNICODE); + QANSICP = Qnil; staticpro (&QANSICP); + QOEMCP = Qnil; staticpro (&QOEMCP); +} + +/* One-time init. Called in the dumped Emacs, but not in the + un-dumped version. */ + +void +globals_of_w32select () +{ + DEFAULT_LCID = GetUserDefaultLCID (); + /* Drop the sort order from the LCID, so we can compare this with + CF_LOCALE objects that have the same fix on 9x. */ + DEFAULT_LCID = MAKELCID (LANGIDFROMLCID (DEFAULT_LCID), SORT_DEFAULT); + + ANSICP = GetACP (); + OEMCP = GetOEMCP (); + + QANSICP = coding_from_cp (ANSICP); + QOEMCP = coding_from_cp (OEMCP); + + if (os_subtype == OS_NT) + Vselection_coding_system = QUNICODE; + else if (inhibit_window_system) + Vselection_coding_system = QOEMCP; + else + Vselection_coding_system = QANSICP; + + clipboard_owner = create_owner (); } /* arch-tag: c96e9724-5eb1-4dad-be07-289f092fd2af |