mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2025-01-01 11:14:55 +00:00
Fix drag-and-drop of files with multibyte filenames
* lisp/dired.el (dired-mouse-drag): Fix re-signalling of errors. * lisp/select.el (xselect-convert-to-filename): (xselect-convert-to-text-uri-list): (xselect-convert-to-dt-netfile): Encode in raw-text-unix. * src/xgselect.c (suppress_xg_select, release_xg_select): New functions. (xg_select): Respect xg_select suppression by delegating to pselect. * src/xgselect.h: Update prototypes. * src/xterm.c (x_dnd_begin_drag_and_drop): Suppress xg_select during the nested event loop. (handle_one_xevent): Handle cases where hold_quit is nil inside a selection event handler during DND.
This commit is contained in:
parent
993853531a
commit
6011d39b6a
@ -1787,12 +1787,12 @@ other marked file as well. Otherwise, unmark all files."
|
||||
nil action t))))
|
||||
(error (when (eq (event-basic-type new-event) 'mouse-1)
|
||||
(push new-event unread-command-events))
|
||||
;; Errors from `dnd-begin-drag-file' should be
|
||||
;; Errors from `dnd-begin-drag-files' should be
|
||||
;; treated as user errors, since they should
|
||||
;; only occur when the user performs an invalid
|
||||
;; action, such as trying to create a link to
|
||||
;; an invalid file.
|
||||
(user-error error))))))))))
|
||||
;; a remote file.
|
||||
(user-error (cadr error)))))))))))
|
||||
|
||||
(defvar dired-mouse-drag-files-map (let ((keymap (make-sparse-keymap)))
|
||||
(define-key keymap [down-mouse-1] #'dired-mouse-drag)
|
||||
|
@ -630,20 +630,20 @@ two markers or an overlay. Otherwise, it is nil."
|
||||
(xselect--encode-string 'TEXT (buffer-file-name (nth 2 value))))
|
||||
(if (and (stringp value)
|
||||
(file-exists-p value))
|
||||
(xselect--encode-string 'TEXT (expand-file-name value)
|
||||
nil t)
|
||||
;; Motif expects this to be STRING, but it treats the data as
|
||||
;; a sequence of bytes instead of a Latin-1 string.
|
||||
(cons 'STRING (encode-coding-string (expand-file-name value)
|
||||
'raw-text-unix))
|
||||
(when (vectorp value)
|
||||
(with-temp-buffer
|
||||
(cl-loop for file across value
|
||||
do (progn (insert (encode-coding-string
|
||||
(expand-file-name file)
|
||||
file-name-coding-system))
|
||||
(insert "\0")))
|
||||
do (insert (expand-file-name file) "\0"))
|
||||
;; Get rid of the last NULL byte.
|
||||
(when (> (point) 1)
|
||||
(delete-char -1))
|
||||
;; Motif wants STRING.
|
||||
(cons 'STRING (buffer-string)))))))
|
||||
(cons 'STRING (encode-coding-string (buffer-string)
|
||||
'raw-text-unix)))))))
|
||||
|
||||
(defun xselect-convert-to-charpos (_selection _type value)
|
||||
(when (setq value (xselect--selection-bounds value))
|
||||
@ -710,14 +710,15 @@ This function returns the string \"emacs\"."
|
||||
|
||||
(defun xselect-convert-to-text-uri-list (_selection _type value)
|
||||
(if (stringp value)
|
||||
(concat (url-encode-url value) "\n")
|
||||
(xselect--encode-string 'TEXT
|
||||
(concat (url-encode-url value) "\n"))
|
||||
(when (vectorp value)
|
||||
(with-temp-buffer
|
||||
(cl-loop for tem across value
|
||||
do (progn
|
||||
(insert (url-encode-url tem))
|
||||
(insert "\n")))
|
||||
(buffer-string)))))
|
||||
(xselect--encode-string 'TEXT (buffer-string))))))
|
||||
|
||||
(defun xselect-convert-to-xm-file (selection _type value)
|
||||
(when (and (stringp value)
|
||||
@ -770,7 +771,8 @@ VALUE should be SELECTION's local value."
|
||||
(stringp value)
|
||||
(file-exists-p value)
|
||||
(not (file-remote-p value)))
|
||||
(xselect-tt-net-file value)))
|
||||
(encode-coding-string (xselect-tt-net-file value)
|
||||
'raw-text-unix t)))
|
||||
|
||||
(setq selection-converter-alist
|
||||
'((TEXT . xselect-convert-to-string)
|
||||
|
@ -33,6 +33,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
static ptrdiff_t threads_holding_glib_lock;
|
||||
static GMainContext *glib_main_context;
|
||||
|
||||
/* The depth of xg_select suppression. */
|
||||
static int xg_select_suppress_count;
|
||||
|
||||
void
|
||||
release_select_lock (void)
|
||||
{
|
||||
@ -69,6 +72,23 @@ acquire_select_lock (GMainContext *context)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Call this to not use xg_select when using it would be a bad idea,
|
||||
i.e. during drag-and-drop. */
|
||||
void
|
||||
suppress_xg_select (void)
|
||||
{
|
||||
++xg_select_suppress_count;
|
||||
}
|
||||
|
||||
void
|
||||
release_xg_select (void)
|
||||
{
|
||||
if (!xg_select_suppress_count)
|
||||
emacs_abort ();
|
||||
|
||||
--xg_select_suppress_count;
|
||||
}
|
||||
|
||||
/* `xg_select' is a `pselect' replacement. Why do we need a separate function?
|
||||
1. Timeouts. Glib and Gtk rely on timer events. If we did pselect
|
||||
with a greater timeout then the one scheduled by Glib, we would
|
||||
@ -100,6 +120,9 @@ xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||
bool already_has_events;
|
||||
#endif
|
||||
|
||||
if (xg_select_suppress_count)
|
||||
return pselect (fds_lim, rfds, wfds, efds, timeout, sigmask);
|
||||
|
||||
context = g_main_context_default ();
|
||||
acquire_select_lock (context);
|
||||
|
||||
|
@ -25,9 +25,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
struct timespec;
|
||||
|
||||
extern int xg_select (int max_fds,
|
||||
fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||
struct timespec *timeout, sigset_t *sigmask);
|
||||
extern int xg_select (int, fd_set *, fd_set *, fd_set *,
|
||||
struct timespec *, sigset_t *);
|
||||
extern void suppress_xg_select (void);
|
||||
extern void release_xg_select (void);
|
||||
|
||||
extern void release_select_lock (void);
|
||||
|
||||
|
19
src/xterm.c
19
src/xterm.c
@ -700,6 +700,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_GTK
|
||||
#include <xgselect.h>
|
||||
#endif
|
||||
|
||||
#include "bitmaps/gray.xbm"
|
||||
|
||||
#ifdef HAVE_XKB
|
||||
@ -10760,6 +10764,13 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
|
||||
if (x_dnd_toplevels)
|
||||
x_dnd_free_toplevels (true);
|
||||
|
||||
#ifdef USE_GTK
|
||||
/* Prevent GTK+ timeouts from being run, since they can call
|
||||
handle_one_xevent behind our back. */
|
||||
suppress_xg_select ();
|
||||
record_unwind_protect_void (release_xg_select);
|
||||
#endif
|
||||
|
||||
x_dnd_in_progress = true;
|
||||
x_dnd_frame = f;
|
||||
x_dnd_last_seen_window = None;
|
||||
@ -15818,7 +15829,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
|
||||
|| (x_dnd_waiting_for_finish
|
||||
&& dpyinfo->display == x_dnd_finish_display))
|
||||
{
|
||||
#ifndef USE_GTK
|
||||
eassume (hold_quit);
|
||||
#else
|
||||
/* If the debugger runs inside a selection converter, then
|
||||
xg_select can call handle_one_xevent with no
|
||||
hold_quit. */
|
||||
if (!hold_quit)
|
||||
goto done;
|
||||
#endif
|
||||
|
||||
*hold_quit = inev.ie;
|
||||
EVENT_INIT (inev.ie);
|
||||
|
Loading…
Reference in New Issue
Block a user