Index: lisp/international/mule-cmds.el =================================================================== RCS file: /cvsroot/emacs/emacs/lisp/international/mule-cmds.el,v retrieving revision 1.257 diff -u -p -r1.257 mule-cmds.el --- lisp/international/mule-cmds.el 12 Jun 2004 02:21:21 -0000 1.257 +++ lisp/international/mule-cmds.el 26 Jul 2004 18:33:01 -0000 @@ -2422,13 +2422,13 @@ See also `locale-charset-language-names' (prefer-coding-system coding-system) (setq locale-coding-system coding-system)))) - ;; On Windows, override locale-coding-system, keyboard-coding-system, - ;; selection-coding-system with system codepage. + ;; On Windows, override locale-coding-system, + ;; keyboard-coding-system, with system codepage. Note: + ;; selection-coding-system is already set in w32select.c. (when (boundp 'w32-ansi-code-page) (let ((code-page-coding (intern (format "cp%d" w32-ansi-code-page)))) (when (coding-system-p code-page-coding) (setq locale-coding-system code-page-coding) - (set-selection-coding-system code-page-coding) (set-keyboard-coding-system code-page-coding) (set-terminal-coding-system code-page-coding)))) Index: src/emacs.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/emacs.c,v retrieving revision 1.342 diff -u -p -r1.342 emacs.c --- src/emacs.c 24 Jun 2004 20:24:52 -0000 1.342 +++ src/emacs.c 26 Jul 2004 18:33:17 -0000 @@ -42,6 +42,8 @@ Boston, MA 02111-1307, USA. */ #ifdef WINDOWSNT #include +#include /* just for w32.h */ +#include "w32.h" #endif #include "lisp.h" @@ -1578,6 +1580,7 @@ main (argc, argv #ifdef HAVE_NTGUI globals_of_w32fns (); globals_of_w32menu (); + globals_of_w32select (); #endif /* end #ifdef HAVE_NTGUI */ } Index: src/w32.h =================================================================== RCS file: /cvsroot/emacs/emacs/src/w32.h,v retrieving revision 1.15 diff -u -p -r1.15 w32.h --- src/w32.h 1 Sep 2003 15:45:57 -0000 1.15 +++ src/w32.h 26 Jul 2004 18:33:24 -0000 @@ -122,16 +122,17 @@ extern void reset_standard_handles (int /* Return the string resource associated with KEY of type TYPE. */ extern LPBYTE w32_get_resource (char * key, LPDWORD type); -extern void init_ntproc (); -extern void term_ntproc (); -extern void globals_of_w32 (); -extern void syms_of_w32term (); -extern void syms_of_w32fns (); -extern void globals_of_w32fns (); -extern void syms_of_w32select (); -extern void syms_of_w32menu (); -extern void globals_of_w32menu (); -extern void syms_of_fontset (); +extern void init_ntproc (void); +extern void term_ntproc (void); +extern void globals_of_w32 (void); +extern void syms_of_w32term (void); +extern void syms_of_w32fns (void); +extern void globals_of_w32fns (void); +extern void syms_of_w32select (void); +extern void globals_of_w32select (void); +extern void syms_of_w32menu (void); +extern void globals_of_w32menu (void); +extern void syms_of_fontset (void); #endif /* EMACS_W32_H */ Index: src/w32select.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/w32select.c,v retrieving revision 1.33 diff -u -p -r1.33 w32select.c --- src/w32select.c 30 Jun 2004 23:30:39 -0000 1.33 +++ src/w32select.c 26 Jul 2004 18:33:24 -0000 @@ -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,672 @@ along with GNU Emacs; see the file COPYI 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 */ #include #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_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 atexit_callback (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); + + Lisp_Object QCLIPBOARD; /* Coding system for communicating with other Windows 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 Windows + 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; + + 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; + } - return (ok ? frame : Qnil); + 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; + struct gcpro gcpro1; + + ONTRACE (fprintf (stderr, "convert_to_handle_as_coded: %s\n", + SDATA (SYMBOL_NAME (coding_system)))); + + /* FIXME: It looks like GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS on + Windows (see src/lisp.h and nt/config.nt), so this is probably + unnecessary. If it was necessary, we'd probably have to protect + all the symbols, too. */ + GCPRO1 (string); + + 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; - BLOCK_INPUT; + nbytes = SBYTES (string); + src = SDATA (string); - ok = EmptyClipboard (); + bufsize = encoding_buffer_size (&coding, nbytes) +2; + htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bufsize); - UNBLOCK_INPUT; + if (htext != NULL) + dst = (unsigned char *) GlobalLock (htext); + + 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'; + } + + 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; + } + + UNGCPRO; + + return htext; +} + +static Lisp_Object +render (Lisp_Object oformat) +{ + 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) + SetClipboardData (format, htext); + + 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")); - return (ok ? Qt : Qnil); + /* 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 Windows convert to CF_OEMTEXT and + Windows 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. On NT/W2K/XP we could even drop + the CF_TEXT, because it too could be generated by the OS, but + 9x ignores CF_UNICODETEXT, even though some applications can + still handle it. */ + + render (make_number (current_clipboard_type)); + if (current_clipboard_type == CF_UNICODETEXT) + render (make_number (CF_TEXT)); + + CloseClipboard (); + + return Qnil; } -DEFUN ("w32-close-clipboard", Fw32_close_clipboard, - Sw32_close_clipboard, 0, 0, 0, - doc: /* Close the clipboard. */) - () +static void +run_protected (Lisp_Object (*code) (), Lisp_Object arg) { - BOOL ok = FALSE; + extern int waiting_for_input; + int owfi; BLOCK_INPUT; - ok = CloseClipboard (); + /* Fsignal calls abort() if it sees that waiting_for_input 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; } -#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; +static LRESULT CALLBACK +owner_callback (HWND win, UINT msg, WPARAM wp, LPARAM lp) { - BOOL ok = TRUE; - HANDLE htext; - int nbytes; - int truelen, nlines = 0; - unsigned char *src; - unsigned char *dst; + 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; - CHECK_STRING (string); + 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; - if (!NILP (frame)) - CHECK_LIVE_FRAME (frame); + case WM_DESTROY: + if (win == clipboard_owner) + clipboard_owner = NULL; + break; + } - BLOCK_INPUT; + return DefWindowProc (win, msg, wp, lp); +} - /* Include the terminating NULL character in the source of - conversion. */ - nbytes = SBYTES (string) + 1; - src = SDATA (string); - dst = src; +static HWND +create_owner (void) +{ + static const char CLASSNAME[] = "Emacs Clipboard"; + WNDCLASS wc; + HWND hwnd; + + memset (&wc, 0, sizeof (wc)); + wc.lpszClassName = CLASSNAME; + wc.lpfnWndProc = owner_callback; + RegisterClass (&wc); + + hwnd = CreateWindow (CLASSNAME, CLASSNAME, 0, 0, 0, 0, 0, NULL, NULL, + NULL, NULL); + + /* FIXME: w32.c says that atexit() is broken. This works here with + Mingw. The note in w32.c is probably about a static build, which + Mingw doesn't support anyway. Anyway the alternative + implementation using signal(SIGABRT) as in w32.c doesn't work + here, so standard C is our best bet. */ + + if (hwnd != NULL) + atexit (atexit_callback); + + return hwnd; +} - /* 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) +static void +atexit_callback (void) +{ + /* Make sure 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 active_coding_system; + + CHECK_SYMBOL (Vselection_coding_system); + + /* Check if we have it cached */ + active_coding_system = NILP (Vnext_selection_coding_system) ? + Vselection_coding_system : Vnext_selection_coding_system; + if (!NILP (cfg_coding_system) + && EQ (cfg_coding_system, active_coding_system)) + return; + cfg_coding_system = active_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 (active_coding_system)); + + /* "(.*-)?utf-16.*" -> CF_UNICODETEXT */ + cp = strstr (coding_name, "utf-16"); + if (cp != NULL && (cp == coding_name || cp[-1] == '-')) { - nlines++; - dst++; + cfg_clipboard_type = CF_UNICODETEXT; + return; } - { - /* 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); + /* "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; + } - if (charset_info == 0) - { - /* No multibyte character in OBJ. We need not encode it. */ + /* 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; + } - /* 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). */ + /* Else determine a suitable locale the hard way. */ + EnumSystemLocales (enum_locale_callback, LCID_INSTALLED); +} - truelen = nbytes + nlines; +static BOOL WINAPI +enum_locale_callback (/*const*/ char* loc_string) +{ + LCID lcid; + UINT codepage; - if ((htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, truelen)) == NULL) - goto error; + lcid = strtoul (loc_string, NULL, 16); - if ((dst = (unsigned char *) GlobalLock (htext)) == NULL) - goto error; + /* 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 */ + } - /* 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; - } + return TRUE; /* Continue enumeration */ +} - GlobalUnlock (htext); +static UINT +cp_from_locale (LCID lcid, UINT format) +{ + char buffer[20] = ""; + UINT variant, cp; - 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; + variant = + format == CF_TEXT ? LOCALE_IDEFAULTANSICODEPAGE : LOCALE_IDEFAULTCODEPAGE; - 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; + GetLocaleInfo (lcid, variant, buffer, sizeof (buffer)); + cp = strtoul (buffer, NULL, 10); - /* 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); - } + if (cp == CP_ACP) + return ANSICP; + else if (cp == CP_OEMCP) + return OEMCP; + else + return cp; +} - GlobalUnlock (htext); +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. */ +} - /* Shrink data block to actual size. */ - htext2 = GlobalReAlloc (htext, coding.produced, - GMEM_MOVEABLE | GMEM_DDESHARE); - if (htext2 != NULL) htext = htext2; - } - } - if (!OpenClipboard ((!NILP (frame) && FRAME_W32_P (XFRAME (frame))) ? FRAME_W32_WINDOW (XFRAME (frame)) : NULL)) +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, ignored) + Lisp_Object string, ignored; +{ + BOOL ok = TRUE; + HGLOBAL hlocale = NULL; + int nbytes; + unsigned char *src; + unsigned char *dst; + unsigned char *end; + + (void) ignored; + + CHECK_STRING (string); + + 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; + + /* 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 note in convert_to_handle_as_ascii() above). */ + nbytes = SBYTES (string); + src = SDATA (string); + + for (dst = src, end = src+nbytes; dst < end; dst++) + { + if (*dst == '\n') + current_num_nls++; + else if (*dst >= 0x80 || *dst == 0) + { + current_requires_encoding = 1; + break; + } + } + + 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; + } + else + { + /* If we have something non-ASCII we may want to set a locale. + We always do that directly (non-delayed), as it's just a + small bit and it simplifies the rest of the code. */ + if (cfg_lcid != LOCALE_NEUTRAL && cfg_lcid != DEFAULT_LCID) + { + hlocale = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, + sizeof (cfg_lcid)); + if (hlocale != NULL) + { + LCID * lcid_ptr; + if ((lcid_ptr = (LCID *) GlobalLock (hlocale)) != NULL) + { + *lcid_ptr = cfg_lcid; + GlobalUnlock (hlocale); + } + } + } + } + + if (!OpenClipboard (clipboard_owner)) goto error; - ok = EmptyClipboard () && SetClipboardData (CF_TEXT, htext); + ++modifying_clipboard; + ok = EmptyClipboard (); + --modifying_clipboard; + + 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. */ + 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 WM_RENDERALLFORMATS handler. */ + SetClipboardData (CF_UNICODETEXT, NULL); + SetClipboardData (CF_TEXT, NULL); + SetClipboardData (CF_OEMTEXT, NULL); + } + + if (hlocale != NULL) + { + ok = SetClipboardData (CF_LOCALE, hlocale) != NULL; + if (!ok) EmptyClipboard (); + } + } CloseClipboard (); - /* 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 (); + /* 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. + + 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. */ + Vlast_coding_system_used = current_coding_system; + + Vnext_selection_coding_system = Qnil; if (ok) goto done; error: ok = FALSE; - if (htext) GlobalFree (htext); - if (last_clipboard_text) - *last_clipboard_text = '\0'; + if (hlocale) GlobalFree (hlocale); - last_clipboard_sequence_number = 0; + current_text = Qnil; + current_coding_system = Qnil; done: UNBLOCK_INPUT; @@ -292,24 +691,50 @@ DEFUN ("w32-set-clipboard-data", Fw32_se 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; + + (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 +747,107 @@ DEFUN ("w32-get-clipboard-data", Fw32_ge 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. + FIXME: Repeat the code for the Unicode case. */ + 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; + + /* Windows is documented to always generate CF_LOCALE info + automatically, so the locale handle should be always + present, but 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 can just filter out the unneeded mis-information + to avoid irritations. */ + lcid = MAKELCID (LANGIDFROMLCID (lcid), SORT_DEFAULT); + } - 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 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; + + 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 +861,10 @@ DEFUN ("w32-get-clipboard-data", Fw32_ge } 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. */ + /* 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 +941,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 +960,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 +989,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