diff --git a/etc/NEWS b/etc/NEWS index bf363168906..c2900b0bc4c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2448,6 +2448,15 @@ be kept aligned with the buffer contents when the user switches This minor mode makes Emacs deactivate the mark in all buffers when the primary selection is obtained by another program. +--- +** On X, Emacs will try to preserve selection ownership when a frame is deleted. +This means that if you make Emacs the owner of a selection, such as by +selecting some text into the clipboard or primary selection, and then +delete the current frame, you will still be able to insert the +contents of that selection into other programs as long as another +frame is open on the same display. This behavior can be disabled by +setting the variable 'x-auto-preserve-selections' to nil. + +++ ** New predicate 'char-uppercase-p'. This returns non-nil if its argument its an uppercase character. diff --git a/lisp/cus-start.el b/lisp/cus-start.el index ca2fca4eb77..df919fd7155 100644 --- a/lisp/cus-start.el +++ b/lisp/cus-start.el @@ -834,6 +834,7 @@ since it could result in memory overflow and make Emacs crash." (x-scroll-event-delta-factor mouse float "29.1") (x-gtk-use-native-input keyboard boolean "29.1") (x-dnd-disable-motif-drag dnd boolean "29.1") + (x-auto-preserve-selections x boolean "29.1") ;; xselect.c (x-select-enable-clipboard-manager killing boolean "24.1") ;; xsettings.c @@ -874,6 +875,8 @@ since it could result in memory overflow and make Emacs crash." (equal "x-scroll-event-delta-factor" (symbol-name symbol)) (equal "x-dnd-disable-motif-drag" + (symbol-name symbol)) + (equal "x-auto-preserve-selections" (symbol-name symbol))) (featurep 'x)) ((string-match "\\`x-" (symbol-name symbol)) diff --git a/src/xselect.c b/src/xselect.c index 25a75aec917..baab2c5c18f 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -1091,20 +1091,23 @@ x_handle_selection_event (struct selection_input_event *event) void x_clear_frame_selections (struct frame *f) { - Lisp_Object frame; - Lisp_Object rest; + Lisp_Object frame, rest, lost; struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f); struct terminal *t = dpyinfo->terminal; XSETFRAME (frame, f); + lost = Qnil; /* Delete elements from the beginning of Vselection_alist. */ while (CONSP (t->Vselection_alist) && EQ (frame, XCAR (XCDR (XCDR (XCDR (XCAR (t->Vselection_alist))))))) { - /* Run the `x-lost-selection-functions' abnormal hook. */ - CALLN (Frun_hook_with_args, Qx_lost_selection_functions, - Fcar (Fcar (t->Vselection_alist))); + if (!x_auto_preserve_selections) + /* Run the `x-lost-selection-functions' abnormal hook. */ + CALLN (Frun_hook_with_args, Qx_lost_selection_functions, + Fcar (Fcar (t->Vselection_alist))); + else + lost = Fcons (Fcar (t->Vselection_alist), lost); tset_selection_alist (t, XCDR (t->Vselection_alist)); } @@ -1114,11 +1117,18 @@ x_clear_frame_selections (struct frame *f) if (CONSP (XCDR (rest)) && EQ (frame, XCAR (XCDR (XCDR (XCDR (XCAR (XCDR (rest)))))))) { - CALLN (Frun_hook_with_args, Qx_lost_selection_functions, - XCAR (XCAR (XCDR (rest)))); + if (!x_auto_preserve_selections) + CALLN (Frun_hook_with_args, Qx_lost_selection_functions, + XCAR (XCAR (XCDR (rest)))); + else + lost = Fcons (XCAR (XCDR (rest)), lost); + XSETCDR (rest, XCDR (XCDR (rest))); break; } + + if (x_auto_preserve_selections) + x_preserve_selections (dpyinfo, lost); } /* True if any properties for DISPLAY and WINDOW diff --git a/src/xterm.c b/src/xterm.c index 2b83efb2288..f86e4708ec5 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -27951,6 +27951,62 @@ x_uncatch_errors_for_lisp (struct x_display_info *dpyinfo) x_stop_ignoring_errors (dpyinfo); } +/* Preserve the selections in LOST in another frame on DPYINFO. LOST + is a list of local selections that were lost, due to their frame + being deleted. */ + +void +x_preserve_selections (struct x_display_info *dpyinfo, Lisp_Object lost) +{ + Lisp_Object tail, frame, new_owner, tem; + Time timestamp; + Window owner; + + new_owner = Qnil; + + FOR_EACH_FRAME (tail, frame) + { + if (FRAME_X_P (XFRAME (frame)) + && FRAME_DISPLAY_INFO (XFRAME (frame)) == dpyinfo) + { + new_owner = frame; + break; + } + } + + tail = lost; + + FOR_EACH_TAIL_SAFE (tail) + { + tem = XCAR (tail); + + /* The selection is really lost (since we cannot find a new + owner), so run the appropriate hooks. */ + if (NILP (new_owner)) + CALLN (Frun_hook_with_args, Qx_lost_selection_functions, + XCAR (tem)); + else + { + CONS_TO_INTEGER (XCAR (XCDR (XCDR (tem))), Time, timestamp); + + /* This shouldn't be able to signal any errors, despite the + call to `x_check_errors' inside. */ + x_own_selection (XCAR (tem), XCAR (XCDR (tem)), + new_owner, XCAR (XCDR (XCDR (XCDR (XCDR (tem))))), + timestamp); + + /* Now check if we still don't own that selection, which can + happen if another program set itself as the owner. */ + owner = XGetSelectionOwner (dpyinfo->display, + symbol_to_x_atom (dpyinfo, XCAR (tem))); + + if (owner != FRAME_X_WINDOW (XFRAME (new_owner))) + CALLN (Frun_hook_with_args, Qx_lost_selection_functions, + XCAR (tem)); + } + } +} + void syms_of_xterm (void) { @@ -28265,4 +28321,11 @@ reply from the X server, and signal any errors that occurred while executing the protocol request. Otherwise, errors will be silently ignored without waiting, which is generally faster. */); x_fast_protocol_requests = false; + + DEFVAR_BOOL ("x-auto-preserve-selections", x_auto_preserve_selections, + doc: /* Whether or not to transfer selection ownership when deleting a frame. +When non-nil, deleting a frame that is currently the owner of a +selection will cause its ownership to be transferred to another frame +on the same display. */); + x_auto_preserve_selections = true; } diff --git a/src/xterm.h b/src/xterm.h index a1ddf13463c..9b91ee45569 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1643,6 +1643,7 @@ extern void xic_set_statusarea (struct frame *); extern void xic_set_xfontset (struct frame *, const char *); extern bool x_defined_color (struct frame *, const char *, Emacs_Color *, bool, bool); +extern void x_preserve_selections (struct x_display_info *, Lisp_Object); #ifdef HAVE_X_I18N extern void free_frame_xic (struct frame *); # if defined HAVE_X_WINDOWS && defined USE_X_TOOLKIT