emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 6dd4c5b953: Improve drag atom computation


From: Po Lu
Subject: master 6dd4c5b953: Improve drag atom computation
Date: Thu, 16 Jun 2022 00:50:46 -0400 (EDT)

branch: master
commit 6dd4c5b953b666a9079910665d770ed56ecbf65d
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Improve drag atom computation
    
    * src/xterm.c (xm_get_drag_window): Avoid leak if error occured
    creating drag window.  Also use StructureNotifyMask instead of
    ButtonPressMask.
    (xm_get_drag_atom_1): Update.  Make EMACS_DRAG_ATOM a list of
    atoms and use the first one that isn't currently owned.
    (xm_get_drag_atom): Stop owning selection here.
    (xm_setup_drag_info): Record chosen atom.
    (x_dnd_cleanup_drag_and_drop, x_dnd_begin_drag_and_drop)
    (x_dnd_update_state, handle_one_xevent, x_connection_closed):
    Use chosen atom.
    * src/xterm.h (struct x_display_info): New field
    `motif_drag_atom_time'.
---
 src/xterm.c | 160 ++++++++++++++++++++++++++++++++++++++++--------------------
 src/xterm.h |   3 ++
 2 files changed, 110 insertions(+), 53 deletions(-)

diff --git a/src/xterm.c b/src/xterm.c
index 0e00632174..cc47427a9d 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -1164,6 +1164,9 @@ static bool x_dnd_xm_use_help;
 /* Whether or not Motif drag initiator info was set up.  */
 static bool x_dnd_motif_setup_p;
 
+/* The Motif drag atom used during the drag-and-drop operation.  */
+static Atom x_dnd_motif_atom;
+
 /* The target window we are waiting for an XdndFinished message
    from.  */
 static Window x_dnd_pending_finish_target;
@@ -1829,10 +1832,8 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
        {
          drag_window = *(Window *) tmp_data;
          x_catch_errors (dpyinfo->display);
-         /* We use ButtonPressMask since it's one of the events an
-            input-only window can never get.  */
          XSelectInput (dpyinfo->display, drag_window,
-                       ButtonPressMask);
+                       StructureNotifyMask);
          rc = !x_had_errors_p (dpyinfo->display);
          x_uncatch_errors_after_check ();
 
@@ -1928,6 +1929,8 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
         punt.  */
       if (error)
        {
+         XSetCloseDownMode (temp_display, DestroyAll);
+
          /* If the drag window was actually created, delete it now.
             Probably, a BadAlloc happened during the XChangeProperty
             request.  */
@@ -1958,10 +1961,7 @@ xm_get_drag_window (struct x_display_info *dpyinfo)
         current display, and the XOpenDisplay above didn't
         accidentally connect to some other display.  */
       x_catch_errors (dpyinfo->display);
-      /* We use ButtonPressMask since it's one of the events an
-        input-only window can never get.  */
-      XSelectInput (dpyinfo->display, drag_window,
-                   ButtonPressMask);
+      XSelectInput (dpyinfo->display, drag_window, StructureNotifyMask);
       rc = !x_had_errors_p (dpyinfo->display);
       x_uncatch_errors_after_check ();
       unblock_input ();
@@ -2178,14 +2178,24 @@ xm_setup_dnd_targets (struct x_display_info *dpyinfo,
   return idx;
 }
 
+/* Allocate an atom that will be used for the Motif selection during
+   the drag-and-drop operation.
+
+   Grab the server, and then retrieve a list of atoms named
+   _EMACS_DRAG_ATOM from the root window.  Find the first atom that
+   has no selection owner, own it and return it.  If there is no such
+   atom, add a unique atom to the end of the list and return that
+   instead.  */
+
 static Atom
-xm_get_drag_atom_1 (struct x_display_info *dpyinfo)
+xm_get_drag_atom_1 (struct x_display_info *dpyinfo,
+                   struct frame *source_frame)
 {
-  Atom actual_type, atom;
+  Atom actual_type, *atoms, atom;
   unsigned long nitems, bytes_remaining;
   unsigned char *tmp_data;
-  unsigned long inumber;
   int rc, actual_format;
+  unsigned long i;
   char *buffer;
 
   /* Make sure this operation is done atomically.  */
@@ -2193,38 +2203,84 @@ xm_get_drag_atom_1 (struct x_display_info *dpyinfo)
 
   rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
                           dpyinfo->Xatom_EMACS_DRAG_ATOM,
-                          0, 1, False, XA_CARDINAL, &actual_type,
+                          0, LONG_MAX, False, XA_ATOM, &actual_type,
                           &actual_format, &nitems, &bytes_remaining,
                           &tmp_data);
+  atom = None;
+  /* GCC thinks i is used unitialized, but it's always initialized if
+     `atoms' exists at that particular spot.  */
+  i = 0;
 
   if (rc == Success
-      && actual_format == 32 && nitems == 1
-      && actual_type == XA_CARDINAL)
+      && actual_format == 32 && nitems
+      && actual_type == XA_ATOM)
     {
-      inumber = *(unsigned long *) tmp_data;
-      inumber &= 0xffffffff;
+      atoms = (Atom *) tmp_data;
+
+      x_catch_errors (dpyinfo->display);
+
+      for (i = 0; i < nitems; ++i)
+       {
+         if (XGetSelectionOwner (dpyinfo->display,
+                                 atoms[i]) == None
+             && !x_had_errors_p (dpyinfo->display))
+           {
+             atom = atoms[i];
+             break;
+           }
+       }
+
+      x_uncatch_errors ();
     }
-  else
-    inumber = 0;
 
   if (tmp_data)
     XFree (tmp_data);
 
-  if (X_LONG_MAX - inumber < 1)
-    inumber = 0;
-
-  inumber += 1;
   buffer = dpyinfo->motif_drag_atom_name;
 
-  /* FIXME: this interns a unique atom for every Emacs session.
-     Eventually the atoms simply pile up.  It may be worth
-     implementing the Motif atoms table logic here.  */
-  sprintf (buffer, "_EMACS_ATOM_%lu", inumber);
-  atom = XInternAtom (dpyinfo->display, buffer, False);
+  if (atom)
+    {
+      sprintf (buffer, "_EMACS_ATOM_%lu", i + 1);
+      XSetSelectionOwner (dpyinfo->display, atom,
+                         FRAME_X_WINDOW (source_frame),
+                         dpyinfo->last_user_time);
+
+      /* The selection's last-change time is newer than our
+        last_user_time, so create a new selection instead.  */
+      if (XGetSelectionOwner (dpyinfo->display, atom)
+         != FRAME_X_WINDOW (source_frame))
+       atom = None;
+    }
+
+  while (!atom)
+    {
+      sprintf (buffer, "_EMACS_ATOM_%lu", nitems + 1);
+      atom = XInternAtom (dpyinfo->display, buffer, False);
+
+      XSetSelectionOwner (dpyinfo->display, atom,
+                         FRAME_X_WINDOW (source_frame),
+                         dpyinfo->last_user_time);
+
+      XChangeProperty (dpyinfo->display, dpyinfo->root_window,
+                      dpyinfo->Xatom_EMACS_DRAG_ATOM, XA_ATOM, 32,
+                      (rc == Success && (actual_format != 32
+                                         || actual_type != XA_ATOM)
+                       ? PropModeReplace : PropModeAppend),
+                      (unsigned char *) &atom, 1);
 
-  XChangeProperty (dpyinfo->display, dpyinfo->root_window,
-                  dpyinfo->Xatom_EMACS_DRAG_ATOM, XA_CARDINAL, 32,
-                  PropModeReplace, (unsigned char *) &inumber, 1);
+      actual_format = 32;
+      actual_type = XA_ATOM;
+      rc = Success;
+      nitems += 1;
+
+      /* The selection's last-change time is newer than our
+        last_user_time, so create a new selection (again).  */
+      if (XGetSelectionOwner (dpyinfo->display, atom)
+         != FRAME_X_WINDOW (source_frame))
+       atom = None;
+    }
+
+  dpyinfo->motif_drag_atom_time = dpyinfo->last_user_time;
 
   XUngrabServer (dpyinfo->display);
   return atom;
@@ -2238,7 +2294,7 @@ xm_get_drag_atom (struct x_display_info *dpyinfo)
   if (dpyinfo->motif_drag_atom != None)
     atom = dpyinfo->motif_drag_atom;
   else
-    atom = xm_get_drag_atom_1 (dpyinfo);
+    atom = xm_get_drag_atom_1 (dpyinfo, x_dnd_frame);
 
   dpyinfo->motif_drag_atom = atom;
   return atom;
@@ -2250,18 +2306,11 @@ xm_setup_drag_info (struct x_display_info *dpyinfo,
 {
   Atom atom;
   xm_drag_initiator_info drag_initiator_info;
-  int idx, rc;
+  int idx;
 
   atom = xm_get_drag_atom (dpyinfo);
 
-  x_catch_errors (dpyinfo->display);
-  XSetSelectionOwner (dpyinfo->display, atom,
-                     FRAME_X_WINDOW (source_frame),
-                     dpyinfo->last_user_time);
-  rc = x_had_errors_p (dpyinfo->display);
-  x_uncatch_errors_after_check ();
-
-  if (rc)
+  if (atom == None)
     return;
 
   XSETCAR (x_dnd_selection_alias_cell,
@@ -2284,6 +2333,7 @@ xm_setup_drag_info (struct x_display_info *dpyinfo,
                                    &drag_initiator_info);
 
       x_dnd_motif_setup_p = true;
+      x_dnd_motif_atom = atom;
     }
 }
 
@@ -4423,7 +4473,7 @@ x_dnd_cleanup_drag_and_drop (void *frame)
                                   XM_DROP_ACTION_DROP_CANCEL);
          dmsg.x = 0;
          dmsg.y = 0;
-         dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f));
+         dmsg.index_atom = x_dnd_motif_atom;
          dmsg.source_window = FRAME_X_WINDOW (f);
 
          x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f,
@@ -11491,7 +11541,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
                                               XM_DROP_ACTION_DROP_CANCEL);
                      dmsg.x = 0;
                      dmsg.y = 0;
-                     dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO 
(f));
+                     dmsg.index_atom = x_dnd_motif_atom;
                      dmsg.source_window = FRAME_X_WINDOW (f);
 
                      x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f,
@@ -11529,7 +11579,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
              /* Delete the Motif drag initiator info if it was set up.  */
              if (x_dnd_motif_setup_p)
                XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-                                xm_get_drag_atom (FRAME_DISPLAY_INFO (f)));
+                                x_dnd_motif_atom);
 
 
              /* Remove any type list set as well.  */
@@ -11584,7 +11634,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
                                               XM_DROP_ACTION_DROP_CANCEL);
                      dmsg.x = 0;
                      dmsg.y = 0;
-                     dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO 
(f));
+                     dmsg.index_atom = x_dnd_motif_atom;
                      dmsg.source_window = FRAME_X_WINDOW (f);
 
                      x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f,
@@ -11621,7 +11671,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
              /* Delete the Motif drag initiator info if it was set up.  */
              if (x_dnd_motif_setup_p)
                XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-                                xm_get_drag_atom (FRAME_DISPLAY_INFO (f)));
+                                x_dnd_motif_atom);
 
 
              /* Remove any type list set as well.  */
@@ -11665,7 +11715,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
   /* Delete the Motif drag initiator info if it was set up.  */
   if (x_dnd_motif_setup_p)
     XDeleteProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-                    xm_get_drag_atom (FRAME_DISPLAY_INFO (f)));
+                    x_dnd_motif_atom);
 
   /* Remove any type list set as well.  */
   if (x_dnd_init_type_lists && x_dnd_n_targets > 3)
@@ -15728,7 +15778,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
              emsg.zero = 0;
              emsg.timestamp = timestamp;
              emsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
-             emsg.index_atom = xm_get_drag_atom (dpyinfo);
+             emsg.index_atom = x_dnd_motif_atom;
 
              if (x_dnd_motif_setup_p)
                xm_send_top_level_enter_message (dpyinfo, FRAME_X_WINDOW 
(x_dnd_frame),
@@ -15800,7 +15850,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
                                   XM_DROP_ACTION_DROP_CANCEL);
          dsmsg.x = 0;
          dsmsg.y = 0;
-         dsmsg.index_atom = xm_get_drag_atom (dpyinfo);
+         dsmsg.index_atom = x_dnd_motif_atom;
          dsmsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
 
          x_dnd_send_xm_leave_for_drop (dpyinfo, x_dnd_frame,
@@ -16558,6 +16608,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       {
         const XSelectionClearEvent *eventp = &event->xselectionclear;
 
+       if (eventp->selection == dpyinfo->motif_drag_atom
+           && dpyinfo->motif_drag_atom_time <= eventp->time)
+         dpyinfo->motif_drag_atom = None;
+
         inev.sie.kind = SELECTION_CLEAR_EVENT;
         SELECTION_EVENT_DPYINFO (&inev.sie) = dpyinfo;
         SELECTION_EVENT_SELECTION (&inev.sie) = eventp->selection;
@@ -16600,7 +16654,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
        if (x_dnd_waiting_for_finish
            && x_dnd_waiting_for_motif_finish == 2
            && dpyinfo == x_dnd_waiting_for_motif_finish_display
-           && eventp->selection == xm_get_drag_atom (dpyinfo)
+           && eventp->selection == x_dnd_motif_atom
            && (eventp->target == dpyinfo->Xatom_XmTRANSFER_SUCCESS
                || eventp->target == dpyinfo->Xatom_XmTRANSFER_FAILURE))
          {
@@ -18009,7 +18063,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    emsg.zero = 0;
                    emsg.timestamp = event->xbutton.time;
                    emsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
-                   emsg.index_atom = xm_get_drag_atom (dpyinfo);
+                   emsg.index_atom = x_dnd_motif_atom;
 
                    if (x_dnd_motif_setup_p)
                      xm_send_top_level_enter_message (dpyinfo, FRAME_X_WINDOW 
(x_dnd_frame),
@@ -18623,7 +18677,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                dmsg.timestamp = event->xbutton.time;
                                dmsg.x = event->xbutton.x_root;
                                dmsg.y = event->xbutton.y_root;
-                               dmsg.index_atom = xm_get_drag_atom (dpyinfo);
+                               dmsg.index_atom = x_dnd_motif_atom;
                                dmsg.source_window = FRAME_X_WINDOW 
(x_dnd_frame);
 
                                if (!XM_DRAG_STYLE_IS_DROP_ONLY 
(drag_receiver_info.protocol_style))
@@ -19734,7 +19788,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          emsg.zero = 0;
                          emsg.timestamp = xev->time;
                          emsg.source_window = FRAME_X_WINDOW (x_dnd_frame);
-                         emsg.index_atom = xm_get_drag_atom (dpyinfo);
+                         emsg.index_atom = x_dnd_motif_atom;
 
                          if (x_dnd_motif_setup_p)
                            xm_send_top_level_enter_message (dpyinfo, 
FRAME_X_WINDOW (x_dnd_frame),
@@ -20030,7 +20084,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                         instances of Emacs try to drag
                                         into the same window at the same
                                         time.  */
-                                     dmsg.index_atom = xm_get_drag_atom 
(dpyinfo);
+                                     dmsg.index_atom = x_dnd_motif_atom;
                                      dmsg.source_window = FRAME_X_WINDOW 
(x_dnd_frame);
 
                                      if (!XM_DRAG_STYLE_IS_DROP_ONLY 
(drag_receiver_info.protocol_style))
@@ -22921,7 +22975,7 @@ x_connection_closed (Display *dpy, const char 
*error_message, bool ioerror)
                                           XM_DROP_ACTION_DROP_CANCEL);
                  dmsg.x = 0;
                  dmsg.y = 0;
-                 dmsg.index_atom = xm_get_drag_atom (FRAME_DISPLAY_INFO (f));
+                 dmsg.index_atom = x_dnd_motif_atom;
                  dmsg.source_window = FRAME_X_WINDOW (f);
 
                  x_dnd_send_xm_leave_for_drop (FRAME_DISPLAY_INFO (f), f,
diff --git a/src/xterm.h b/src/xterm.h
index 9df1feae5b..119382c73c 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -573,6 +573,9 @@ struct x_display_info
   char motif_drag_atom_name[sizeof "_EMACS_ATOM_%lu" - 3
                            + INT_STRLEN_BOUND (unsigned long)];
 
+  /* When it was owned.  */
+  Time motif_drag_atom_time;
+
   /* Extended window manager hints, Atoms supported by the window manager and
      atoms for setting the window type.  */
   Atom Xatom_net_supported, Xatom_net_supporting_wm_check;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]