]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix drag-and-drop of files with multibyte filenames
authorPo Lu <luangruo@yahoo.com>
Sun, 5 Jun 2022 07:34:49 +0000 (15:34 +0800)
committerPo Lu <luangruo@yahoo.com>
Sun, 5 Jun 2022 07:38:23 +0000 (15:38 +0800)
* 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.

lisp/dired.el
lisp/select.el
src/xgselect.c
src/xgselect.h
src/xterm.c

index 7df50a7b2ae823484c2658e2ad3a4e11cae2adfd..55e150e9e04e3d22c398c6cbf8d461395990a460 100644 (file)
@@ -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)
index df1d4026552827faf5ff785046aeeebcaf3b0a19..c5412f2a73b92a556f049d7757f004720e03d9c3 100644 (file)
@@ -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)
index 7252210c68637a5bf8fe33cedfcb2a9a4c7c99c0..6e09a15fa84f3101a49a8e578becdb583e12e2c2 100644 (file)
@@ -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);
 
index 15482cbf922f6822fb16bd3c59bc1e84c7b04f4a..156d4bde59f7423a2d593871b9a9eb423cbe5790 100644 (file)
@@ -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);
 
index a6ef2bfd159e613741cd333caf6ccf40af80b3cb..4d8d7e80eb5174b979043ee3b311c399b8e36548 100644 (file)
@@ -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);