From: Po Lu Date: Sun, 5 Jun 2022 07:34:49 +0000 (+0800) Subject: Fix drag-and-drop of files with multibyte filenames X-Git-Tag: emacs-29.0.90~1910^2~223 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=6011d39b6a4bc659da364255bcae22c4e6ef3a3f;p=emacs.git 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. --- diff --git a/lisp/dired.el b/lisp/dired.el index 7df50a7b2ae..55e150e9e04 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -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) diff --git a/lisp/select.el b/lisp/select.el index df1d4026552..c5412f2a73b 100644 --- a/lisp/select.el +++ b/lisp/select.el @@ -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) diff --git a/src/xgselect.c b/src/xgselect.c index 7252210c686..6e09a15fa84 100644 --- a/src/xgselect.c +++ b/src/xgselect.c @@ -33,6 +33,9 @@ along with GNU Emacs. If not, see . */ 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); diff --git a/src/xgselect.h b/src/xgselect.h index 15482cbf922..156d4bde59f 100644 --- a/src/xgselect.h +++ b/src/xgselect.h @@ -25,9 +25,10 @@ along with GNU Emacs. If not, see . */ 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); diff --git a/src/xterm.c b/src/xterm.c index a6ef2bfd159..4d8d7e80eb5 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -700,6 +700,10 @@ along with GNU Emacs. If not, see . */ #endif #endif +#ifdef USE_GTK +#include +#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);