#include <config.h>
#include "lisp.h"
#include "w32term.h"
#include "w32heap.h"
#include "blockinput.h"
#include "keyboard.h"
#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 ( char* loc_string);
static UINT cp_from_locale (LCID lcid, UINT format);
static Lisp_Object coding_from_cp (UINT codepage);
Lisp_Object QCLIPBOARD;
static Lisp_Object Vselection_coding_system;
static Lisp_Object Vnext_selection_coding_system;
static LCID DEFAULT_LCID;
static UINT ANSICP, OEMCP;
static Lisp_Object QUNICODE, QANSICP, QOEMCP;
static HWND clipboard_owner;
static int modifying_clipboard = 0;
static Lisp_Object cfg_coding_system;
static UINT cfg_codepage;
static LCID cfg_lcid;
static UINT cfg_clipboard_type;
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)
#endif
static HGLOBAL
convert_to_handle_as_ascii (void)
{
HGLOBAL htext = NULL;
int nbytes;
int truelen;
unsigned char *src;
unsigned char *dst;
ONTRACE (fprintf (stderr, "convert_to_handle_as_ascii\n"));
nbytes = SBYTES (current_text) + 1;
src = SDATA (current_text);
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;
}
while (1)
{
unsigned char *next;
next = _memccpy (dst, src, '\n', nbytes);
if (next)
{
int copied = next - dst;
nbytes -= copied;
src += copied;
next[-1] = '\r';
next[0] = '\n';
dst = next + 1;
}
else
break;
}
GlobalUnlock (htext);
return htext;
}
static HGLOBAL
convert_to_handle_as_coded (Lisp_Object coding_system)
{
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;
coding.composing = COMPOSITION_DISABLED;
if (coding.type == coding_type_iso2022)
coding.flags |= CODING_FLAG_ISO_SAFE;
coding.mode |= CODING_MODE_LAST_BLOCK;
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);
bufsize = encoding_buffer_size (&coding, nbytes) +2;
htext = GlobalAlloc (GMEM_MOVEABLE | GMEM_DDESHARE, bufsize);
if (htext != NULL)
dst = (unsigned char *) GlobalLock (htext);
if (dst != NULL)
{
encode_coding (&coding, src, dst, nbytes, bufsize-2);
dst[coding.produced] = dst[coding.produced+1] = '\0';
}
if (dst != NULL)
GlobalUnlock (htext);
if (htext != NULL)
{
htext2 = GlobalReAlloc (htext, coding.produced+2,
GMEM_MOVEABLE | GMEM_DDESHARE);
if (htext2 != NULL) htext = htext2;
}
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)
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;
}
static Lisp_Object
render_all (void)
{
ONTRACE (fprintf (stderr, "render_all\n"));
OpenClipboard (NULL);
++modifying_clipboard;
EmptyClipboard ();
--modifying_clipboard;
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)
{
extern int waiting_for_input;
int owfi;
BLOCK_INPUT;
owfi = waiting_for_input;
waiting_for_input = 0;
internal_condition_case_1 (code, arg, Qt, lisp_error_handler);
waiting_for_input = owfi;
UNBLOCK_INPUT;
}
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);
}
void
term_w32select (void)
{
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);
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;
cfg_codepage = ANSICP;
cfg_lcid = LOCALE_NEUTRAL;
cfg_clipboard_type = CF_TEXT;
coding_name = SDATA (SYMBOL_NAME (cfg_coding_system));
cp = strstr (coding_name, "utf-16");
if (cp != NULL && (cp == coding_name || cp[-1] == '-'))
{
cfg_clipboard_type = CF_UNICODETEXT;
return;
}
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);
if (cfg_codepage == 0 || (end-cp) < 2 )
{
cfg_codepage = ANSICP;
return;
}
if (cfg_codepage == ANSICP)
{
return;
}
if (cfg_codepage == OEMCP)
{
cfg_clipboard_type = CF_OEMTEXT;
return;
}
EnumSystemLocales (enum_locale_callback, LCID_INSTALLED);
}
static BOOL WINAPI
enum_locale_callback ( char* loc_string)
{
LCID lcid;
UINT codepage;
lcid = strtoul (loc_string, NULL, 16);
codepage = cp_from_locale (lcid, CF_TEXT);
if (codepage == cfg_codepage)
{
cfg_lcid = lcid;
cfg_clipboard_type = CF_TEXT;
return FALSE;
}
codepage = cp_from_locale (lcid, CF_OEMTEXT);
if (codepage == cfg_codepage)
{
cfg_lcid = lcid;
cfg_clipboard_type = CF_OEMTEXT;
return FALSE;
}
return TRUE;
}
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);
}
DEFUN ("w32-set-clipboard-data", Fw32_set_clipboard_data,
Sw32_set_clipboard_data, 1, 2, 0,
doc: )
(string, ignored)
Lisp_Object string, ignored;
{
BOOL ok = TRUE;
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;
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)
{
current_coding_system = Qraw_text;
current_clipboard_type = CF_TEXT;
current_lcid = LOCALE_NEUTRAL;
}
if (!OpenClipboard (clipboard_owner))
goto error;
++modifying_clipboard;
ok = EmptyClipboard ();
--modifying_clipboard;
if (ok)
ok = !NILP(render_locale());
if (ok)
{
if (clipboard_owner == NULL)
{
ok = !NILP(render (make_number (current_clipboard_type)));
current_text = Qnil;
current_coding_system = Qnil;
}
else
{
SetClipboardData (CF_UNICODETEXT, NULL);
SetClipboardData (CF_TEXT, NULL);
SetClipboardData (CF_OEMTEXT, NULL);
}
}
CloseClipboard ();
if (ok)
Vlast_coding_system_used = current_coding_system;
Vnext_selection_coding_system = Qnil;
if (ok) goto done;
error:
ok = FALSE;
current_text = Qnil;
current_coding_system = Qnil;
done:
UNBLOCK_INPUT;
return (ok ? string : Qnil);
}
DEFUN ("w32-get-clipboard-data", Fw32_get_clipboard_data,
Sw32_get_clipboard_data, 0, 1, 0,
doc: )
(ignored)
Lisp_Object ignored;
{
HGLOBAL htext;
Lisp_Object ret = Qnil;
UINT actual_clipboard_type;
int use_configured_coding_system = 1;
(void) ignored;
if (!NILP (current_text))
return ret;
setup_config ();
actual_clipboard_type = cfg_clipboard_type;
BLOCK_INPUT;
if (!OpenClipboard (clipboard_owner))
goto done;
if ((htext = GetClipboardData (actual_clipboard_type)) == NULL)
{
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;
{
unsigned char *src;
unsigned char *dst;
int nbytes;
int truelen;
int require_decoding = 0;
if ((src = (unsigned char *) GlobalLock (htext)) == NULL)
goto closeclip;
if (actual_clipboard_type == CF_UNICODETEXT)
{
nbytes = lstrlenW ((WCHAR *)src) * 2;
require_decoding = 1;
}
else
{
int i;
nbytes = strlen (src);
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;
if (use_configured_coding_system
&& !NILP (Vnext_selection_coding_system))
coding_system = Vnext_selection_coding_system;
else if (actual_clipboard_type != CF_UNICODETEXT)
{
HGLOBAL hlocale;
LCID lcid = DEFAULT_LCID;
UINT cp;
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);
}
lcid = MAKELCID (LANGIDFROMLCID (lcid), SORT_DEFAULT);
}
if (!use_configured_coding_system || lcid != DEFAULT_LCID)
{
cp = cp_from_locale (lcid, actual_clipboard_type);
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;
coding.mode |= CODING_MODE_LAST_BLOCK;
coding.composing = COMPOSITION_DISABLED;
coding.eol_type = CODING_EOL_CRLF;
bufsize = decoding_buffer_size (&coding, nbytes);
buf = (unsigned char *) xmalloc (bufsize);
decode_coding (&coding, src, buf, nbytes, bufsize);
Vlast_coding_system_used = coding.symbol;
ret = make_string_from_bytes ((char *) buf,
coding.produced_char, coding.produced);
xfree (buf);
if (SYMBOLP (coding.post_read_conversion)
&& !NILP (Ffboundp (coding.post_read_conversion)))
ret = run_pre_post_conversion_on_str (ret, &coding, 0);
}
else
{
truelen = nbytes;
dst = src;
while ((dst = memchr (dst, '\r', nbytes - (dst - src))) != NULL)
{
if (dst[1] == '\n')
truelen--;
dst++;
}
ret = make_uninit_string (truelen);
dst = SDATA (ret);
while (1)
{
unsigned char *next;
next = _memccpy (dst, src, '\r', nbytes);
if (next)
{
int copied = next - dst;
nbytes -= copied;
dst += copied;
src += copied;
if (*src == '\n')
dst--;
}
else
break;
}
Vlast_coding_system_used = Qraw_text;
}
GlobalUnlock (htext);
}
closeclip:
CloseClipboard ();
done:
UNBLOCK_INPUT;
return (ret);
}
DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p,
0, 1, 0,
doc: )
(selection)
Lisp_Object selection;
{
CHECK_SYMBOL (selection);
if (EQ (selection, QCLIPBOARD))
{
Lisp_Object val = Qnil;
if (OpenClipboard (NULL))
{
UINT format = 0;
setup_config ();
while ((format = EnumClipboardFormats (format)))
if (format == cfg_clipboard_type || format == CF_TEXT)
{
val = Qt;
break;
}
CloseClipboard ();
}
return val;
}
return Qnil;
}
void
syms_of_w32select ()
{
defsubr (&Sw32_set_clipboard_data);
defsubr (&Sw32_get_clipboard_data);
defsubr (&Sx_selection_exists_p);
DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system,
doc: );
Vselection_coding_system = Qnil;
DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system,
doc: );
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);
}
void
globals_of_w32select ()
{
DEFAULT_LCID = GetUserDefaultLCID ();
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 ();
}