diff options
Diffstat (limited to 'src/w32fns.c')
-rw-r--r-- | src/w32fns.c | 267 |
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)) |