From affef6455cdcbb879b5063859deec3641312b0fd Mon Sep 17 00:00:00 2001
From: Philipp Stephani <phst@google.com>
Date: Wed, 8 Apr 2015 18:53:47 +0200
Subject: [PATCH] Implement OSC-52 functionality for setting the X selection

---
 lisp/term/xterm.el | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 89 insertions(+), 4 deletions(-)

diff --git a/lisp/term/xterm.el b/lisp/term/xterm.el
index 519f691..f4d1a84 100644
--- a/lisp/term/xterm.el
+++ b/lisp/term/xterm.el
@@ -37,7 +37,8 @@ If a list, assume that the listed features are supported, without checking.
 
 The relevant features are:
   modifyOtherKeys  -- if supported, more key bindings work (e.g., \"\\C-,\")
-  reportBackground -- if supported, Xterm reports its background color"
+  reportBackground -- if supported, Xterm reports its background color
+  setSelection     -- if supported, Xterm saves yanked text to the X selection"
   :version "24.1"
   :group 'xterm
   :type '(choice (const :tag "No" nil)
@@ -45,7 +46,24 @@ The relevant features are:
                  ;; NOTE: If you add entries here, make sure to update
                  ;; `terminal-init-xterm' as well.
                  (set (const :tag "modifyOtherKeys support" modifyOtherKeys)
-                      (const :tag "report background" reportBackground))))
+                      (const :tag "report background" reportBackground)
+                      (const :tag "set X selection" setSelection))))
+
+(defcustom xterm-max-cut-length 100000
+  "Maximum number of bytes to cut into xterm using the OSC 52 sequence.
+
+The OSC 52 sequence requires a terminator byte.  Some terminals will ignore or
+mistreat a terminated sequence that is longer than a certain size, usually to
+protect users from runaway sequences.
+
+This variable allows you to tweak the maximum number of bytes that will be sent
+using the OSC 52 sequence.
+
+If you select a region larger than this size, it won't be copied to your system
+clipboard.  Since clipboard data is base 64 encoded, the actual number of
+string bytes that can be copied is 3/4 of this value."
+  :group 'xterm
+  :type 'integer)
 
 (defconst xterm-paste-ending-sequence "\e[201~"
   "Characters send by the terminal to end a bracketed paste.")
@@ -620,7 +638,13 @@ The relevant features are:
         ;; introduced) or higher, initialize the
         ;; modifyOtherKeys support.
         (when (>= version 216)
-          (terminal-init-xterm-modify-other-keys))))))
+          (terminal-init-xterm-modify-other-keys))
+        ;; In version 203 support for accessing the X selection was
+        ;; added.  Hterm reports itself as version 256 and supports it
+        ;; as well.  gnome-terminal doesn't and is excluded by this
+        ;; test.
+        (when (>= version 203)
+          (terminal-init-xterm-activate-set-selection))))))
 
 (defun xterm--query (query handlers)
   "Send QUERY string to the terminal and watch for a response.
@@ -699,7 +723,10 @@ We run the first FUNCTION whose STRING matches the input events."
                     '(("\e]11;" .  xterm--report-background-handler))))
 
     (when (memq 'modifyOtherKeys xterm-extra-capabilities)
-      (terminal-init-xterm-modify-other-keys)))
+      (terminal-init-xterm-modify-other-keys))
+
+    (when (memq 'setSelection xterm-extra-capabilities)
+      (terminal-init-xterm-activate-set-selection)))
 
   ;; Unconditionally enable bracketed paste mode: terminals that don't
   ;; support it just ignore the sequence.
@@ -719,6 +746,64 @@ We run the first FUNCTION whose STRING matches the input events."
   (push "\e[?2004l" (terminal-parameter nil 'tty-mode-reset-strings))
   (push "\e[?2004h" (terminal-parameter nil 'tty-mode-set-strings)))
 
+(defun terminal-init-xterm-activate-set-selection ()
+  "Terminal initialization for `gui-set-selection'."
+  ;; All text terminals are represented by the nil GUI type.  We need
+  ;; to detect XTerm again in `xterm--set-selection' using the
+  ;; terminal parameters.
+  (gui-method-define gui-set-selection nil #'xterm--set-selection))
+
+(defun xterm--set-selection (type data)
+  "Copy DATA to the X selection using the OSC 52 escape sequence.
+
+TYPE specifies which selection to set; it must be either
+`PRIMARY' or `CLIPBOARD'.  DATA must be a string.
+
+This can be used as a `gui-set-selection' method for
+xterm-compatible terminal emulators.  Then your system clipboard
+will be updated whenever you copy a region of text in Emacs.
+
+If the resulting OSC 52 sequence would be longer than
+`xterm-max-cut-length', then the TEXT is not sent to the system
+clipboard.
+
+This function either sends a raw OSC 52 sequence or wraps the OSC
+52 in a Device Control String sequence.  This way, it will work
+on a bare terminal emulators as well as inside the screen
+program.  When inside the screen program, this function also
+chops long DCS sequences into multiple smaller ones to avoid
+hitting screen's max DCS length."
+  (let* ((init-function (terminal-parameter nil 'terminal-initted))
+         (xterm (eq init-function 'terminal-init-xterm))
+         (screen (eq init-function 'terminal-init-screen)))
+    ;; Only do something if the current terminal is actually an XTerm
+    ;; or screen.
+    (when (or xterm screen)
+      (let* ((bytes (encode-coding-string data 'utf-8-unix))
+             (base-64 (if screen
+                          (replace-regexp-in-string
+                           "\n" "\e\\\eP"
+                           (base64-encode-string bytes)
+                           :fixedcase :literal)
+                        (base64-encode-string bytes :no-line-break)))
+             (length (string-bytes base-64)))
+        (if (> length xterm-max-cut-length)
+            (progn
+              (warn "Selection too long to send to terminal: %d bytes" length)
+              (sit-for 2))
+          (send-string-to-terminal
+           (concat
+            (when screen "\eP")
+            "\e]52;"
+            (cond
+             ((eq type 'PRIMARY) "p")
+             ((eq type 'CLIPBOARD) "c")
+             (t (error "Invalid type %S" type)))
+            ";"
+            base-64
+            "\a"
+            (when screen "\e\\"))))))))
+
 ;; Set up colors, for those versions of xterm that support it.
 (defvar xterm-standard-colors
   ;; The names in the comments taken from XTerm-col.ad in the xterm
-- 
2.3.5

