aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorCecilio Pardo2024-09-27 23:58:02 +0200
committerEli Zaretskii2024-10-08 16:13:24 +0300
commit9af36e70f83d00eaef2255929e8aed7e1d3ed5b9 (patch)
tree1262465c5a08b767e9e96f8498f2a583d0c13222 /src
parent339ffd79862c322f5b75fed1b55d61efe90bc7a1 (diff)
downloademacs-9af36e70f83d00eaef2255929e8aed7e1d3ed5b9.tar.gz
emacs-9af36e70f83d00eaef2255929e8aed7e1d3ed5b9.zip
Implement drag-n-drop for w32 with support for files and text
Implement drag-n-drop with IDropTarget for MS-Windows. This allows for dropping files or text. * lisp/term/w32-win.el (w32-drag-n-drop): Change to handle files or strings. * src/w32fns.c (process_dropfiles): New function to convert DROPFILES struct to array of strings. (w32_process_dnd_data): New function to process drop data. (w32_try_get_data): Extract data from IDataObject. (w32_createwindow): Assign an IDropTarget to each new frame. (w32_name_of_message): New message. (w32_msg_pump): Changed CoInitialize to OleInitialize, needed by the drag-n-drop functions. (w32_wnd_proc): New struct w32_drop_target, and w32_drop_target_* functions to implement the IDropTarget interface. * src/w32term.c (w32_read_socket): Handle WM_EMACS_DROP and remove WM_EMACS_DROPFILES. * src/w32term.h: New message WM_EMACS_DROP. (Bug#3468) * etc/NEWS: Announce the new feature.
Diffstat (limited to 'src')
-rw-r--r--src/w32fns.c221
-rw-r--r--src/w32term.c98
-rw-r--r--src/w32term.h4
3 files changed, 237 insertions, 86 deletions
diff --git a/src/w32fns.c b/src/w32fns.c
index 0a3f5c38a58..b3d26b841e4 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/>. */
34 34
35#include <c-ctype.h> 35#include <c-ctype.h>
36 36
37#define COBJMACROS /* Ask for C definitions for COM. */
38#include <shlobj.h>
39#include <oleidl.h>
40#include <objidl.h>
41#include <ole2.h>
42
37#include "lisp.h" 43#include "lisp.h"
38#include "w32term.h" 44#include "w32term.h"
39#include "frame.h" 45#include "frame.h"
@@ -359,6 +365,9 @@ extern HANDLE keyboard_handle;
359 365
360static struct w32_display_info *w32_display_info_for_name (Lisp_Object); 366static struct w32_display_info *w32_display_info_for_name (Lisp_Object);
361 367
368static void my_post_msg (W32Msg*, HWND, UINT, WPARAM, LPARAM);
369static unsigned int w32_get_modifiers (void);
370
362/* Let the user specify a display with a frame. 371/* Let the user specify a display with a frame.
363 nil stands for the selected frame--or, if that is not a w32 frame, 372 nil stands for the selected frame--or, if that is not a w32 frame,
364 the first display on the list. */ 373 the first display on the list. */
@@ -2464,6 +2473,182 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar)
2464 return hwnd; 2473 return hwnd;
2465} 2474}
2466 2475
2476/* From the DROPFILES struct, extract the filenames and return as a list
2477 of strings. */
2478static Lisp_Object
2479process_dropfiles (DROPFILES *files)
2480{
2481 char *start_of_files = (char *) files + files->pFiles;
2482 char filename[MAX_UTF8_PATH];
2483 Lisp_Object lisp_files = Qnil;
2484
2485 if (files->fWide)
2486 {
2487 WCHAR *p = (WCHAR *) start_of_files;
2488 for (; *p; p += wcslen (p) + 1)
2489 {
2490 filename_from_utf16(p, filename);
2491 lisp_files = Fcons (DECODE_FILE (build_unibyte_string (filename)),
2492 lisp_files );
2493 }
2494 }
2495 else
2496 {
2497 char *p = start_of_files;
2498 for (; *p; p += strlen(p) + 1)
2499 {
2500 filename_from_ansi (p, filename);
2501 lisp_files = Fcons (DECODE_FILE (build_unibyte_string (filename)),
2502 lisp_files );
2503 }
2504 }
2505 return lisp_files;
2506}
2507
2508
2509/* This function can be called ONLY between calls to
2510 block_input/unblock_input. It is used in w32_read_socket. */
2511Lisp_Object
2512w32_process_dnd_data (int format, void *hGlobal)
2513{
2514 Lisp_Object result = Qnil;
2515 HGLOBAL hg = (HGLOBAL) hGlobal;
2516
2517 switch (format)
2518 {
2519 case CF_HDROP:
2520 {
2521 DROPFILES *files = (DROPFILES *) GlobalLock (hg);
2522 if (files)
2523 result = process_dropfiles (files);
2524 GlobalUnlock (hg);
2525 break;
2526 }
2527 case CF_UNICODETEXT:
2528 {
2529 WCHAR *text = (WCHAR *) GlobalLock (hg);
2530 result = from_unicode_buffer (text);
2531 GlobalUnlock (hg);
2532 break;
2533 }
2534 case CF_TEXT:
2535 {
2536 char *text = (char *) GlobalLock (hg);
2537 result = DECODE_SYSTEM (build_unibyte_string (text));
2538 GlobalUnlock (hg);
2539 break;
2540 }
2541 }
2542
2543 GlobalFree (hg);
2544
2545 return result;
2546}
2547
2548struct w32_drop_target {
2549 /* i_drop_target must be the first member. */
2550 IDropTarget i_drop_target;
2551 HWND hwnd;
2552};
2553
2554static HRESULT STDMETHODCALLTYPE
2555w32_drop_target_QueryInterface (IDropTarget *t, REFIID ri, void **r)
2556{
2557 return E_NOINTERFACE;
2558}
2559
2560static ULONG STDMETHODCALLTYPE
2561w32_drop_target_AddRef (IDropTarget *This)
2562{
2563 return 1;
2564}
2565
2566static ULONG STDMETHODCALLTYPE
2567w32_drop_target_Release (IDropTarget *This)
2568{
2569 struct w32_drop_target *target = (struct w32_drop_target * ) This;
2570 free (target->i_drop_target.lpVtbl);
2571 free (target);
2572 return 0;
2573}
2574
2575static HRESULT STDMETHODCALLTYPE
2576w32_drop_target_DragEnter (IDropTarget *This, IDataObject *pDataObj,
2577 DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
2578{
2579 /* Possible 'effect' values are COPY, MOVE, LINK or NONE. This choice
2580 changes the mouse pointer shape to inform the user of what will
2581 happen on drop. We send COPY because our use cases don't modify
2582 or link to the original data. */
2583 *pdwEffect = DROPEFFECT_COPY;
2584 return S_OK;
2585}
2586
2587static HRESULT STDMETHODCALLTYPE
2588w32_drop_target_DragOver (IDropTarget *This, DWORD grfKeyState, POINTL pt,
2589 DWORD *pdwEffect)
2590{
2591 /* See comment in w32_drop_target_DragEnter. */
2592 *pdwEffect = DROPEFFECT_COPY;
2593 return S_OK;
2594}
2595
2596static HRESULT STDMETHODCALLTYPE
2597w32_drop_target_DragLeave (IDropTarget *This)
2598{
2599 return S_OK;
2600}
2601
2602static HGLOBAL w32_try_get_data (IDataObject *pDataObj, int format)
2603{
2604 FORMATETC formatetc = { format, NULL, DVASPECT_CONTENT, -1,
2605 TYMED_HGLOBAL };
2606 STGMEDIUM stgmedium;
2607 HRESULT r = IDataObject_GetData (pDataObj, &formatetc, &stgmedium);
2608 if (SUCCEEDED (r))
2609 {
2610 if (stgmedium.tymed == TYMED_HGLOBAL)
2611 return stgmedium.hGlobal;
2612 ReleaseStgMedium (&stgmedium);
2613 }
2614 return NULL;
2615}
2616
2617static HRESULT STDMETHODCALLTYPE
2618w32_drop_target_Drop (IDropTarget *This, IDataObject *pDataObj,
2619 DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
2620{
2621 struct w32_drop_target *target = (struct w32_drop_target *)This;
2622 *pdwEffect = DROPEFFECT_COPY;
2623
2624 W32Msg msg = {0};
2625 msg.dwModifiers = w32_get_modifiers();
2626 msg.msg.time = GetMessageTime ();
2627 msg.msg.pt.x = pt.x;
2628 msg.msg.pt.y = pt.y;
2629
2630 int format = CF_HDROP;
2631 HGLOBAL hGlobal = w32_try_get_data (pDataObj, format);
2632
2633 if (!hGlobal)
2634 {
2635 format = CF_UNICODETEXT;
2636 hGlobal = w32_try_get_data (pDataObj, format);
2637 }
2638
2639 if (!hGlobal)
2640 {
2641 format = CF_TEXT;
2642 hGlobal = w32_try_get_data (pDataObj, format);
2643 }
2644
2645 if (hGlobal)
2646 my_post_msg (&msg, target->hwnd, WM_EMACS_DROP, format,
2647 (LPARAM) hGlobal);
2648
2649 return S_OK;
2650}
2651
2467static void 2652static void
2468w32_createwindow (struct frame *f, int *coords) 2653w32_createwindow (struct frame *f, int *coords)
2469{ 2654{
@@ -2548,7 +2733,30 @@ w32_createwindow (struct frame *f, int *coords)
2548 SetWindowLong (hwnd, WND_BACKGROUND_INDEX, FRAME_BACKGROUND_PIXEL (f)); 2733 SetWindowLong (hwnd, WND_BACKGROUND_INDEX, FRAME_BACKGROUND_PIXEL (f));
2549 2734
2550 /* Enable drag-n-drop. */ 2735 /* Enable drag-n-drop. */
2551 DragAcceptFiles (hwnd, TRUE); 2736 struct w32_drop_target *drop_target =
2737 malloc (sizeof (struct w32_drop_target));
2738
2739 if (drop_target != NULL)
2740 {
2741 IDropTargetVtbl *vtbl = malloc (sizeof (IDropTargetVtbl));
2742 if (vtbl != NULL)
2743 {
2744 drop_target->hwnd = hwnd;
2745 drop_target->i_drop_target.lpVtbl = vtbl;
2746 vtbl->QueryInterface = w32_drop_target_QueryInterface;
2747 vtbl->AddRef = w32_drop_target_AddRef;
2748 vtbl->Release = w32_drop_target_Release;
2749 vtbl->DragEnter = w32_drop_target_DragEnter;
2750 vtbl->DragOver = w32_drop_target_DragOver;
2751 vtbl->DragLeave = w32_drop_target_DragLeave;
2752 vtbl->Drop = w32_drop_target_Drop;
2753 RegisterDragDrop (hwnd, &drop_target->i_drop_target);
2754 }
2755 else
2756 {
2757 free (drop_target);
2758 }
2759 }
2552 2760
2553 /* Enable system light/dark theme. */ 2761 /* Enable system light/dark theme. */
2554 w32_applytheme (hwnd); 2762 w32_applytheme (hwnd);
@@ -3399,6 +3607,7 @@ w32_name_of_message (UINT msg)
3399 M (WM_EMACS_PAINT), 3607 M (WM_EMACS_PAINT),
3400 M (WM_EMACS_IME_STATUS), 3608 M (WM_EMACS_IME_STATUS),
3401 M (WM_CHAR), 3609 M (WM_CHAR),
3610 M (WM_EMACS_DROP),
3402#undef M 3611#undef M
3403 { 0, 0 } 3612 { 0, 0 }
3404 }; 3613 };
@@ -3465,13 +3674,14 @@ w32_msg_pump (deferred_msg * msg_buf)
3465 /* Produced by complete_deferred_msg; just ignore. */ 3674 /* Produced by complete_deferred_msg; just ignore. */
3466 break; 3675 break;
3467 case WM_EMACS_CREATEWINDOW: 3676 case WM_EMACS_CREATEWINDOW:
3468 /* Initialize COM for this window. Even though we don't use it, 3677 /* Initialize COM for this window. Needed for RegisterDragDrop.
3469 some third party shell extensions can cause it to be used in 3678 Some third party shell extensions can cause it to be used in
3470 system dialogs, which causes a crash if it is not initialized. 3679 system dialogs, which causes a crash if it is not initialized.
3471 This is a known bug in Windows, which was fixed long ago, but 3680 This is a known bug in Windows, which was fixed long ago, but
3472 the patch for XP is not publicly available until XP SP3, 3681 the patch for XP is not publicly available until XP SP3,
3473 and older versions will never be patched. */ 3682 and older versions will never be patched. */
3474 CoInitialize (NULL); 3683 OleInitialize (NULL);
3684
3475 w32_createwindow ((struct frame *) msg.wParam, 3685 w32_createwindow ((struct frame *) msg.wParam,
3476 (int *) msg.lParam); 3686 (int *) msg.lParam);
3477 if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0)) 3687 if (!PostThreadMessage (dwMainThreadId, WM_EMACS_DONE, 0, 0))
@@ -5106,7 +5316,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5106 return 0; 5316 return 0;
5107 5317
5108 case WM_MOUSEWHEEL: 5318 case WM_MOUSEWHEEL:
5109 case WM_DROPFILES:
5110 wmsg.dwModifiers = w32_get_modifiers (); 5319 wmsg.dwModifiers = w32_get_modifiers ();
5111 my_post_msg (&wmsg, hwnd, msg, wParam, lParam); 5320 my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
5112 signal_user_input (); 5321 signal_user_input ();
@@ -5597,7 +5806,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5597 } 5806 }
5598 5807
5599 case WM_EMACS_DESTROYWINDOW: 5808 case WM_EMACS_DESTROYWINDOW:
5600 DragAcceptFiles ((HWND) wParam, FALSE); 5809 RevokeDragDrop ((HWND) wParam);
5601 return DestroyWindow ((HWND) wParam); 5810 return DestroyWindow ((HWND) wParam);
5602 5811
5603 case WM_EMACS_HIDE_CARET: 5812 case WM_EMACS_HIDE_CARET:
diff --git a/src/w32term.c b/src/w32term.c
index 62037e3b2cd..3a627308137 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3576,81 +3576,6 @@ w32_construct_mouse_wheel (struct input_event *result, W32Msg *msg,
3576 return Qnil; 3576 return Qnil;
3577} 3577}
3578 3578
3579static Lisp_Object
3580w32_construct_drag_n_drop (struct input_event *result, W32Msg *msg,
3581 struct frame *f)
3582{
3583 Lisp_Object files;
3584 Lisp_Object frame;
3585 HDROP hdrop;
3586 POINT p;
3587 WORD num_files;
3588 wchar_t name_w[MAX_PATH];
3589#ifdef NTGUI_UNICODE
3590 const int use_unicode = 1;
3591#else
3592 int use_unicode = w32_unicode_filenames;
3593 char name_a[MAX_PATH];
3594 char file[MAX_UTF8_PATH];
3595#endif
3596 int i;
3597
3598 result->kind = DRAG_N_DROP_EVENT;
3599 result->code = 0;
3600 result->timestamp = msg->msg.time;
3601 result->modifiers = msg->dwModifiers;
3602
3603 hdrop = (HDROP) msg->msg.wParam;
3604 DragQueryPoint (hdrop, &p);
3605
3606#if 0
3607 p.x = LOWORD (msg->msg.lParam);
3608 p.y = HIWORD (msg->msg.lParam);
3609 ScreenToClient (msg->msg.hwnd, &p);
3610#endif
3611
3612 XSETINT (result->x, p.x);
3613 XSETINT (result->y, p.y);
3614
3615 num_files = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
3616 files = Qnil;
3617
3618 for (i = 0; i < num_files; i++)
3619 {
3620 if (use_unicode)
3621 {
3622 eassert (DragQueryFileW (hdrop, i, NULL, 0) < MAX_PATH);
3623 /* If DragQueryFile returns zero, it failed to fetch a file
3624 name. */
3625 if (DragQueryFileW (hdrop, i, name_w, MAX_PATH) == 0)
3626 continue;
3627#ifdef NTGUI_UNICODE
3628 files = Fcons (from_unicode_buffer (name_w), files);
3629#else
3630 filename_from_utf16 (name_w, file);
3631 files = Fcons (DECODE_FILE (build_unibyte_string (file)), files);
3632#endif /* NTGUI_UNICODE */
3633 }
3634#ifndef NTGUI_UNICODE
3635 else
3636 {
3637 eassert (DragQueryFileA (hdrop, i, NULL, 0) < MAX_PATH);
3638 if (DragQueryFileA (hdrop, i, name_a, MAX_PATH) == 0)
3639 continue;
3640 filename_from_ansi (name_a, file);
3641 files = Fcons (DECODE_FILE (build_unibyte_string (file)), files);
3642 }
3643#endif
3644 }
3645
3646 DragFinish (hdrop);
3647
3648 XSETFRAME (frame, f);
3649 result->frame_or_window = frame;
3650 result->arg = files;
3651 return Qnil;
3652}
3653
3654 3579
3655#if HAVE_W32NOTIFY 3580#if HAVE_W32NOTIFY
3656 3581
@@ -5682,11 +5607,26 @@ w32_read_socket (struct terminal *terminal,
5682 } 5607 }
5683 break; 5608 break;
5684 5609
5685 case WM_DROPFILES: 5610 case WM_EMACS_DROP:
5686 f = w32_window_to_frame (dpyinfo, msg.msg.hwnd); 5611 {
5612 int format = msg.msg.wParam;
5613 Lisp_Object drop_object =
5614 w32_process_dnd_data (format, (void *) msg.msg.lParam);
5687 5615
5688 if (f) 5616 f = w32_window_to_frame (dpyinfo, msg.msg.hwnd);
5689 w32_construct_drag_n_drop (&inev, &msg, f); 5617 if (!f || NILP (drop_object))
5618 break;
5619
5620 XSETFRAME (inev.frame_or_window, f);
5621 inev.kind = DRAG_N_DROP_EVENT;
5622 inev.code = 0;
5623 inev.timestamp = msg.msg.time;
5624 inev.modifiers = msg.dwModifiers;
5625 ScreenToClient (msg.msg.hwnd, &msg.msg.pt);
5626 XSETINT (inev.x, msg.msg.pt.x);
5627 XSETINT (inev.y, msg.msg.pt.y);
5628 inev.arg = drop_object;
5629 }
5690 break; 5630 break;
5691 5631
5692 case WM_HSCROLL: 5632 case WM_HSCROLL:
diff --git a/src/w32term.h b/src/w32term.h
index 47be542f570..39e2262e2a8 100644
--- a/src/w32term.h
+++ b/src/w32term.h
@@ -272,6 +272,7 @@ extern const char *w32_get_string_resource (void *v_rdb,
272 272
273/* w32fns.c */ 273/* w32fns.c */
274extern void w32_default_font_parameter (struct frame* f, Lisp_Object parms); 274extern void w32_default_font_parameter (struct frame* f, Lisp_Object parms);
275extern Lisp_Object w32_process_dnd_data (int format, void *pDataObj);
275 276
276 277
277#define PIX_TYPE COLORREF 278#define PIX_TYPE COLORREF
@@ -710,7 +711,8 @@ do { \
710#define WM_EMACS_INPUT_READY (WM_EMACS_START + 24) 711#define WM_EMACS_INPUT_READY (WM_EMACS_START + 24)
711#define WM_EMACS_FILENOTIFY (WM_EMACS_START + 25) 712#define WM_EMACS_FILENOTIFY (WM_EMACS_START + 25)
712#define WM_EMACS_IME_STATUS (WM_EMACS_START + 26) 713#define WM_EMACS_IME_STATUS (WM_EMACS_START + 26)
713#define WM_EMACS_END (WM_EMACS_START + 27) 714#define WM_EMACS_DROP (WM_EMACS_START + 27)
715#define WM_EMACS_END (WM_EMACS_START + 28)
714 716
715#define WND_FONTWIDTH_INDEX (0) 717#define WND_FONTWIDTH_INDEX (0)
716#define WND_LINEHEIGHT_INDEX (4) 718#define WND_LINEHEIGHT_INDEX (4)