Subject: bug#5014: select-active-regions xplat clean-up

From: David De La Harpe Golden <[email-masked]>
Date: Sun, 15 Nov 2009 01:49:10 +0000

Severity: wishlist
Tags: patch

[ Resent from
  http://lists.gnu.org/archive/html/emacs-devel/2009-11/msg00453.html ]

Removes unnecessary X11isms from simple.el, lays some groundwork for 
non-X11 implementation. Introduces clipboard-active-regions to cover 
case where users want select-active-regions like behaviour only 
affecting the clipboard instead (whether on X11 or platforms without 
primary).

Index: lisp/simple.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/simple.el,v
retrieving revision 1.1029
diff -U 8 -r1.1029 simple.el
--- lisp/simple.el	14 Nov 2009 16:07:47 -0000	1.1029
+++ lisp/simple.el	15 Nov 2009 01:36:31 -0000
@@ -2777,16 +2777,71 @@
 kill ring for easy access via `yank-pop'.
 
 Note that the function should return a string only if a program other
 than Emacs has provided a string for pasting; if Emacs provided the
 most recent string, the function should return nil.  If it is
 difficult to tell whether Emacs or some other program provided the
 current string, it is probably good enough to return nil if the string
 is equal (according to `string=') to the last text Emacs provided.")
+
+(defvar interprogram-region-function nil
+  "Function to call to make the active region available to other programs.
+
+Certain window systems provide a facility for directly insertion of
+text currently selected in one program into another program. Such a
+facility is sometimes confused with, but is nowadays considered
+distinct from, a cut and paste clipboard type facility.  The canonical
+example is an X11 PRIMARY selection considered to be separate from and
+independent to the X11 CLIPBOARD selection, as they are treated in
+several X11 desktop environments.
+
+Emacs supports making its currently active region available to other
+programs through the system facility for making the currently selected
+text directly available, if any.  Emacs also supports using the system
+clipboard facility for doing so, as several non-X11 platforms have a
+clipboard analog but no primary selection analog and some users find
+it convenient to use the clipboard as a substitute for primary.
+
+If `select-active-regions' is non-nil, emacs will make the active
+region available to other programs through the system facility for
+making currently selected text directly available to other
+applications.
+
+If `clipboard-active-regions' is non-nil, emacs will make the active
+region available to other programs through the system clipboard
+facility.
+
+This variable holds the function that Emacs calls to make the active
+region available to other programs via the relevant facilities.  The
+function should be called with one or two arguments.
+
+Note that the function is not called every time the active region
+changes, that could be every point movement while the region is
+active:
+
+The first argument, TEXTORBUF, is either a string containing the text
+which should be made available, or a buffer.  If the argument is a
+buffer, the function should arrange for the the region between the
+mark and point in the buffer at such times as Emacs is expected to
+provide the currently selected text by the system be made available,
+and while emacs ownership of the relevant system facilities has not
+lapsed, not at time of function call.  This allows an acceptably efficient
+lazy implementation on X11. On some non-X11 window systems, it may be
+necessary to emulate lazy behaviour with an idle timer or other
+technique, as the window system may not ask emacs for the currently
+selected text but rather expect emacs to tell it the currently
+selected text.
+
+The second, optional, argument, FORCE, if non-nil means that emacs
+should update and acquire ownership of the relevant window system
+facilities.  If it is nil or not supplied, emacs should arrange to
+update the relevant window system facilities only if it currently
+already has ownership of them (emacs may have lost ownership to
+another application since the previous call to the function).")
 

 ;;;; The kill ring data structure.
 
 (defvar kill-ring nil
   "List of killed text sequences.
 Since the kill ring is supposed to interact nicely with cut-and-paste
@@ -3530,38 +3585,51 @@
 
 If you are using this in an editing command, you are most likely making
 a mistake; see the documentation of `set-mark'."
   (if (or force (not transient-mark-mode) mark-active mark-even-if-inactive)
       (marker-position (mark-marker))
     (signal 'mark-inactive nil)))
 
 (defcustom select-active-regions nil
-  "If non-nil, an active region automatically becomes the window selection."
+  "If non-nil, an active region automatically becomes the window system current selection.
+
+The `interprogram-region-function', if any, implements this customization.
+
+See also `clipboard-active-regions'."
   :type 'boolean
   :group 'killing
   :version "23.1")
 
-(declare-function x-selection-owner-p "xselect.c" (&optional selection))
+(defcustom clipboard-active-regions nil
+  "If non-nil, an active region automatically becomes the window system clipboard.
+
+The `interprogram-region-function', if any, implements this customization.
+
+See also `select-active-regions'."
+  :type 'boolean
+  :group 'killing
+  :version "23.2")
 
 ;; Many places set mark-active directly, and several of them failed to also
 ;; run deactivate-mark-hook.  This shorthand should simplify.
 (defsubst deactivate-mark (&optional force)
   "Deactivate the mark by setting `mark-active' to nil.
 Unless FORCE is non-nil, this function does nothing if Transient
 Mark mode is disabled.
 This function also runs `deactivate-mark-hook'."
   (when (or transient-mark-mode force)
-    ;; Copy the latest region into the primary selection, if desired.
-    (and select-active-regions
+    ;; Copy the latest region into the primary selection
+    ;; and/or clipboard, if desired, via interprogram-region-function
+    (and (or select-active-regions clipboard-active-regions)
 	 mark-active
-	 (display-selections-p)
-	 (x-selection-owner-p 'PRIMARY)
-	 (x-set-selection 'PRIMARY (buffer-substring-no-properties
-				    (region-beginning) (region-end))))
+         interprogram-region-function
+         (funcall interprogram-region-function
+                  (buffer-substring-no-properties
+                   (region-beginning) (region-end)) nil))
     (if (and (null force)
 	     (or (eq transient-mark-mode 'lambda)
 		 (and (eq (car-safe transient-mark-mode) 'only)
 		      (null (cdr transient-mark-mode)))))
 	;; When deactivating a temporary region, don't change
 	;; `mark-active' or run `deactivate-mark-hook'.
 	(setq transient-mark-mode nil)
       (if (eq (car-safe transient-mark-mode) 'only)
@@ -3570,19 +3638,20 @@
       (run-hooks 'deactivate-mark-hook))))
 
 (defun activate-mark ()
   "Activate the mark."
   (when (mark t)
     (setq mark-active t)
     (unless transient-mark-mode
       (setq transient-mark-mode 'lambda))
-    (when (and select-active-regions
-	       (display-selections-p))
-      (x-set-selection 'PRIMARY (current-buffer)))))
+    (when (and (or select-active-regions clipboard-active-regions)
+               interprogram-region-function)
+      (funcall interprogram-region-function
+               (current-buffer) t))))
 
 (defun set-mark (pos)
   "Set this buffer's mark to POS.  Don't use this function!
 That is to say, don't use this function unless you want
 the user to see that the mark has moved, and you want the previous
 mark position to be lost.
 
 Normally, when a new mark is set, the old one should go on the stack.
@@ -3595,19 +3664,19 @@
 store it in a Lisp variable.  Example:
 
    (let ((beg (point))) (forward-line 1) (delete-region beg (point)))."
 
   (if pos
       (progn
 	(setq mark-active t)
 	(run-hooks 'activate-mark-hook)
-	(when (and select-active-regions
-		   (display-selections-p))
-	  (x-set-selection 'PRIMARY (current-buffer)))
+	(when (and (or select-active-regions clipboard-active-regions)
+		   interprogram-region-function)
+	  (funcall interprogram-region-function (current-buffer) t))
 	(set-marker (mark-marker) pos (current-buffer)))
     ;; Normally we never clear mark-active except in Transient Mark mode.
     ;; But when we actually clear out the mark value too, we must
     ;; clear mark-active in any mode.
     (deactivate-mark t)
     (set-marker (mark-marker) nil)))
 
 (defcustom use-empty-active-region nil
Index: lisp/mouse.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/mouse.el,v
retrieving revision 1.363
diff -U 8 -r1.363 mouse.el
--- lisp/mouse.el	13 Nov 2009 22:19:53 -0000	1.363
+++ lisp/mouse.el	15 Nov 2009 01:36:31 -0000
@@ -1361,22 +1361,22 @@
 
 (defun mouse-yank-at-click (click arg)
   "Insert the last stretch of killed text at the position clicked on.
 Also move point to one end of the text thus inserted (normally the end),
 and set mark at the beginning.
 Prefix arguments are interpreted as with \\[yank].
 If `mouse-yank-at-point' is non-nil, insert at point
 regardless of where you click.
-If `select-active-regions' is non-nil, the mark is deactivated
-before inserting the text."
+If `select-active-regions' or `clipboard-active-regions' are non-nil,
+the mark is deactivated before inserting the text."
   (interactive "e\nP")
   ;; Give temporary modes such as isearch a chance to turn off.
   (run-hooks 'mouse-leave-buffer-hook)
-  (when select-active-regions
+  (when (or select-active-regions clipboard-active-regions)
     ;; Without this, confusing things happen upon e.g. inserting into
     ;; the middle of an active region.
     (deactivate-mark))
   (or mouse-yank-at-point (mouse-set-point click))
   (setq this-command 'yank)
   (setq mouse-selection-click-count 0)
   (yank arg))
 
Index: lisp/term/x-win.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/term/x-win.el,v
retrieving revision 1.245
diff -U 8 -r1.245 x-win.el
--- lisp/term/x-win.el	11 Nov 2009 05:49:14 -0000	1.245
+++ lisp/term/x-win.el	15 Nov 2009 01:36:31 -0000
@@ -1404,19 +1404,32 @@
       ;; This code is still a big improvement because now the user can
       ;; futz with the current selection and get emacs to pay attention
       ;; to the cut buffer again (previously as soon as clipboard or
       ;; primary had been set the cut buffer would essentially never be
       ;; checked again).
       (or clip-text primary-text cut-text)
       )))
 
+(declare-function x-selection-owner-p "xselect.c" (&optional selection))
+
+(defun x-region-handler (textorbuf &optional force)
+  "X implementation of `interprogram-region-function'"
+  (when (and select-active-regions
+             (or force (x-selection-owner-p 'PRIMARY)))
+    (x-set-selection 'PRIMARY textorbuf))
+  (when (and clipboard-active-regions
+             (or force (x-selection-owner-p 'CLIPBOARD)))
+    (x-set-selection 'CLIPBOARD textorbuf)))
+
 ;; Arrange for the kill and yank functions to set and check the clipboard.
 (setq interprogram-cut-function 'x-select-text)
 (setq interprogram-paste-function 'x-cut-buffer-or-selection-value)
+;; Arrange for the active region to be propagated to system if desired.
+(setq interprogram-region-function 'x-region-handler)
 
 (defun x-clipboard-yank ()
   "Insert the clipboard contents, or the last stretch of killed text."
   (interactive "*")
   (let ((clipboard-text (x-selection-value 'CLIPBOARD))
 	(x-select-enable-clipboard t))
     (if (and clipboard-text (> (length clipboard-text) 0))
 	(kill-new clipboard-text))