summaryrefslogtreecommitdiff
path: root/src/w32fns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/w32fns.c')
-rw-r--r--src/w32fns.c267
1 files changed, 255 insertions, 12 deletions
diff --git a/src/w32fns.c b/src/w32fns.c
index 1d79f0618fa..c7963d2c616 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -34,6 +34,12 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <c-ctype.h>
+#define COBJMACROS /* Ask for C definitions for COM. */
+#include <shlobj.h>
+#include <oleidl.h>
+#include <objidl.h>
+#include <ole2.h>
+
#include "lisp.h"
#include "w32term.h"
#include "frame.h"
@@ -359,6 +365,9 @@ extern HANDLE keyboard_handle;
static struct w32_display_info *w32_display_info_for_name (Lisp_Object);
+static void my_post_msg (W32Msg *, HWND, UINT, WPARAM, LPARAM);
+static unsigned int w32_get_modifiers (void);
+
/* Let the user specify a display with a frame.
nil stands for the selected frame--or, if that is not a w32 frame,
the first display on the list. */
@@ -937,13 +946,13 @@ x_to_w32_color (const char * colorname)
{
int len = strlen (colorname);
- if (isdigit (colorname[len - 1]))
+ if (c_isdigit (colorname[len - 1]))
{
char *ptr, *approx = alloca (len + 1);
strcpy (approx, colorname);
ptr = &approx[len - 1];
- while (ptr > approx && isdigit (*ptr))
+ while (ptr > approx && c_isdigit (*ptr))
*ptr-- = '\0';
ret = w32_color_map_lookup (approx);
@@ -2464,6 +2473,214 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar)
return hwnd;
}
+/* From the DROPFILES struct, extract the filenames and return as a list
+ of strings. */
+static Lisp_Object
+process_dropfiles (DROPFILES *files)
+{
+ char *start_of_files = (char *) files + files->pFiles;
+#ifndef NTGUI_UNICODE
+ char filename[MAX_UTF8_PATH];
+#endif
+ Lisp_Object lisp_files = Qnil;
+
+#ifdef NTGUI_UNICODE
+ WCHAR *p = (WCHAR *) start_of_files;
+ for (; *p; p += wcslen (p) + 1)
+ {
+ Lisp_Object fn = from_unicode_buffer (p);
+#ifdef CYGWIN
+ fn = Fcygwin_convert_file_name_to_windows (fn, Qt);
+#endif
+ lisp_files = Fcons (fn, lisp_files);
+ }
+#else
+ if (files->fWide)
+ {
+ WCHAR *p = (WCHAR *) start_of_files;
+ for (; *p; p += wcslen (p) + 1)
+ {
+ filename_from_utf16 (p, filename);
+ lisp_files = Fcons (DECODE_FILE (build_unibyte_string (filename)),
+ lisp_files);
+ }
+ }
+ else
+ {
+ char *p = start_of_files;
+ for (; *p; p += strlen (p) + 1)
+ {
+ filename_from_ansi (p, filename);
+ lisp_files = Fcons (DECODE_FILE (build_unibyte_string (filename)),
+ lisp_files);
+ }
+ }
+#endif
+ return lisp_files;
+}
+
+/* This function can be called ONLY between calls to
+ block_input/unblock_input. It is used in w32_read_socket. */
+Lisp_Object
+w32_process_dnd_data (int format, void *hGlobal)
+{
+ Lisp_Object result = Qnil;
+ HGLOBAL hg = (HGLOBAL) hGlobal;
+
+ switch (format)
+ {
+ case CF_HDROP:
+ {
+ DROPFILES *files = (DROPFILES *) GlobalLock (hg);
+ if (files)
+ result = process_dropfiles (files);
+ GlobalUnlock (hg);
+ break;
+ }
+ case CF_UNICODETEXT:
+ {
+ WCHAR *text = (WCHAR *) GlobalLock (hg);
+ result = from_unicode_buffer (text);
+ GlobalUnlock (hg);
+ break;
+ }
+ case CF_TEXT:
+ {
+ char *text = (char *) GlobalLock (hg);
+ result = DECODE_SYSTEM (build_unibyte_string (text));
+ GlobalUnlock (hg);
+ break;
+ }
+ }
+
+ GlobalFree (hg);
+
+ return result;
+}
+
+struct w32_drop_target {
+ /* i_drop_target must be the first member. */
+ IDropTarget i_drop_target;
+ HWND hwnd;
+ int ref_count;
+};
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_QueryInterface (IDropTarget *t, REFIID ri, void **r)
+{
+ return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE
+w32_drop_target_AddRef (IDropTarget *This)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *) This;
+ return ++target->ref_count;
+}
+
+static ULONG STDMETHODCALLTYPE
+w32_drop_target_Release (IDropTarget *This)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *) This;
+ if (--target->ref_count > 0)
+ return target->ref_count;
+ free (target->i_drop_target.lpVtbl);
+ free (target);
+ return 0;
+}
+
+static void
+w32_handle_drag_movement (IDropTarget *This, POINTL pt)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *)This;
+
+ W32Msg msg = {0};
+ msg.dwModifiers = w32_get_modifiers ();
+ msg.msg.time = GetMessageTime ();
+ msg.msg.pt.x = pt.x;
+ msg.msg.pt.y = pt.y;
+ my_post_msg (&msg, target->hwnd, WM_EMACS_DRAGOVER, 0, 0 );
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragEnter (IDropTarget *This, IDataObject *pDataObj,
+ DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ /* Possible 'effect' values are COPY, MOVE, LINK or NONE. This choice
+ changes the mouse pointer shape to inform the user of what will
+ happen on drop. We send COPY because our use cases don't modify
+ or link to the original data. */
+ *pdwEffect = DROPEFFECT_COPY;
+ w32_handle_drag_movement (This, pt);
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragOver (IDropTarget *This, DWORD grfKeyState, POINTL pt,
+ DWORD *pdwEffect)
+{
+ /* See comment in w32_drop_target_DragEnter. */
+ *pdwEffect = DROPEFFECT_COPY;
+ w32_handle_drag_movement (This, pt);
+ return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_DragLeave (IDropTarget *This)
+{
+ return S_OK;
+}
+
+static HGLOBAL w32_try_get_data (IDataObject *pDataObj, int format)
+{
+ FORMATETC formatetc = { format, NULL, DVASPECT_CONTENT, -1,
+ TYMED_HGLOBAL };
+ STGMEDIUM stgmedium;
+ HRESULT r = IDataObject_GetData (pDataObj, &formatetc, &stgmedium);
+ if (SUCCEEDED (r))
+ {
+ if (stgmedium.tymed == TYMED_HGLOBAL)
+ return stgmedium.hGlobal;
+ ReleaseStgMedium (&stgmedium);
+ }
+ return NULL;
+}
+
+static HRESULT STDMETHODCALLTYPE
+w32_drop_target_Drop (IDropTarget *This, IDataObject *pDataObj,
+ DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ struct w32_drop_target *target = (struct w32_drop_target *)This;
+ *pdwEffect = DROPEFFECT_COPY;
+
+ W32Msg msg = {0};
+ msg.dwModifiers = w32_get_modifiers ();
+ msg.msg.time = GetMessageTime ();
+ msg.msg.pt.x = pt.x;
+ msg.msg.pt.y = pt.y;
+
+ int format = CF_HDROP;
+ HGLOBAL hGlobal = w32_try_get_data (pDataObj, format);
+
+ if (!hGlobal)
+ {
+ format = CF_UNICODETEXT;
+ hGlobal = w32_try_get_data (pDataObj, format);
+ }
+
+ if (!hGlobal)
+ {
+ format = CF_TEXT;
+ hGlobal = w32_try_get_data (pDataObj, format);
+ }
+
+ if (hGlobal)
+ my_post_msg (&msg, target->hwnd, WM_EMACS_DROP, format,
+ (LPARAM) hGlobal);
+
+ return S_OK;
+}
+
static void
w32_createwindow (struct frame *f, int *coords)
{
@@ -2548,7 +2765,31 @@ w32_createwindow (struct frame *f, int *coords)
SetWindowLong (hwnd, WND_BACKGROUND_INDEX, FRAME_BACKGROUND_PIXEL (f));
/* Enable drag-n-drop. */
- DragAcceptFiles (hwnd, TRUE);
+ struct w32_drop_target *drop_target
+ = malloc (sizeof (struct w32_drop_target));
+
+ if (drop_target != NULL)
+ {
+ IDropTargetVtbl *vtbl = malloc (sizeof (IDropTargetVtbl));
+ if (vtbl != NULL)
+ {
+ drop_target->hwnd = hwnd;
+ drop_target->ref_count = 0;
+ drop_target->i_drop_target.lpVtbl = vtbl;
+ vtbl->QueryInterface = w32_drop_target_QueryInterface;
+ vtbl->AddRef = w32_drop_target_AddRef;
+ vtbl->Release = w32_drop_target_Release;
+ vtbl->DragEnter = w32_drop_target_DragEnter;
+ vtbl->DragOver = w32_drop_target_DragOver;
+ vtbl->DragLeave = w32_drop_target_DragLeave;
+ vtbl->Drop = w32_drop_target_Drop;
+ RegisterDragDrop (hwnd, &drop_target->i_drop_target);
+ }
+ else
+ {
+ free (drop_target);
+ }
+ }
/* Enable system light/dark theme. */
w32_applytheme (hwnd);
@@ -3399,6 +3640,8 @@ w32_name_of_message (UINT msg)
M (WM_EMACS_PAINT),
M (WM_EMACS_IME_STATUS),
M (WM_CHAR),
+ M (WM_EMACS_DRAGOVER),
+ M (WM_EMACS_DROP),
#undef M
{ 0, 0 }
};
@@ -3465,13 +3708,14 @@ w32_msg_pump (deferred_msg * msg_buf)
/* Produced by complete_deferred_msg; just ignore. */
break;
case WM_EMACS_CREATEWINDOW:
- /* Initialize COM for this window. Even though we don't use it,
- some third party shell extensions can cause it to be used in
+ /* Initialize COM for this window. Needed for RegisterDragDrop.
+ Some third party shell extensions can cause it to be used in
system dialogs, which causes a crash if it is not initialized.
This is a known bug in Windows, which was fixed long ago, but
the patch for XP is not publicly available until XP SP3,
and older versions will never be patched. */
- CoInitialize (NULL);
+ OleInitialize (NULL);
+
w32_createwindow ((struct frame *) msg.wParam,
(int *) msg.lParam);
if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
@@ -3725,7 +3969,7 @@ post_character_message (HWND hwnd, UINT msg,
message that has no particular effect. */
{
int c = wParam;
- if (isalpha (c) && wmsg.dwModifiers == ctrl_modifier)
+ if (c_isalpha (c) && wmsg.dwModifiers == ctrl_modifier)
c = make_ctrl_char (c) & 0377;
if (c == quit_char
|| (wmsg.dwModifiers == 0
@@ -5106,7 +5350,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
return 0;
case WM_MOUSEWHEEL:
- case WM_DROPFILES:
wmsg.dwModifiers = w32_get_modifiers ();
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
signal_user_input ();
@@ -5597,7 +5840,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
}
case WM_EMACS_DESTROYWINDOW:
- DragAcceptFiles ((HWND) wParam, FALSE);
+ RevokeDragDrop ((HWND) wParam);
return DestroyWindow ((HWND) wParam);
case WM_EMACS_HIDE_CARET:
@@ -8748,7 +8991,7 @@ lookup_vk_code (char *key)
|| (key[0] >= '0' && key[0] <= '9'))
return key[0];
if (key[0] >= 'a' && key[0] <= 'z')
- return toupper(key[0]);
+ return c_toupper (key[0]);
}
}
@@ -9518,7 +9761,7 @@ DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
BOOL result;
/* find the root name of the volume if given */
- if (isalpha (name[0]) && name[1] == ':')
+ if (c_isalpha (name[0]) && name[1] == ':')
{
rootname[0] = name[0];
rootname[1] = name[1];
@@ -10583,7 +10826,7 @@ to be converted to forward slashes by the caller. */)
else if (EQ (root, QHKCC))
rootkey = HKEY_CURRENT_CONFIG;
else if (!NILP (root))
- error ("unknown root key: %s", SDATA (SYMBOL_NAME (root)));
+ error ("Unknown root key: %s", SDATA (SYMBOL_NAME (root)));
Lisp_Object val = w32_read_registry (rootkey, key, name);
if (NILP (val) && NILP (root))