aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjixiuf <jixiuf@qq.com>2020-09-19 23:42:41 +0800
committerjixiuf <jixiuf@qq.com>2020-11-12 22:55:43 +0800
commitb992934e88be04e3357d858cc6ade9a19e2dfe6c (patch)
tree8222e434681f6566b462d55faa275ea18235e2ef
parent8ebc7e91224ae9774bbd1f4a543cd71eebd8e442 (diff)
Support osc 52 Manipulate Selection Data.
https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
-rw-r--r--README.md20
-rw-r--r--elisp.c7
-rw-r--r--elisp.h3
-rw-r--r--vterm-module.c63
-rw-r--r--vterm-module.h9
-rw-r--r--vterm.el41
6 files changed, 141 insertions, 2 deletions
diff --git a/README.md b/README.md
index e6777b9..52d5e6f 100644
--- a/README.md
+++ b/README.md
@@ -331,6 +331,26 @@ The variable `vterm-use-vterm-prompt-detection-method` determines whether to use
the vterm prompt tracking, if false it use the regexp in
`vterm-copy-prompt-regexp` to search for the prompt.
+## `vterm-enable-manipulate-selection-data-by-osc52`
+
+Vterm support copy text to emacs kill ring and system clipboard by using OSC 52.
+See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html for more info about OSC 52.
+For example: send 'blabla' to kill ring: printf "\033]52;c;$(printf "%s" "blabla" | base64)\a"
+
+tmux can share its copy buffer to terminals bysupporting osc52(like iterm2 xterm),
+you can enable this feature for tmux by :
+set -g set-clipboard on #osc 52 copy paste share with iterm
+set -ga terminal-overrides ',xterm*:XT:Ms=\E]52;%p1%s;%p2%s\007'
+set -ga terminal-overrides ',screen*:XT:Ms=\E]52;%p1%s;%p2%s\007'
+
+The clipboard querying/clearing functionality offered by OSC 52 is not implemented here,
+And for security reason, this feature is disabled by default."
+
+This feature need the new way of handling strings with a struct `VTermStringFragment`
+in libvterm. You'd better compile emacs-libvterm with `cmake -DUSE_SYSTEM_LIBVTERM=no ..`.
+If you don't do that, when the content you want to copied is too long, it would be truncated
+by bug of libvterm.
+
## `vterm-buffer-name-string`
When `vterm-buffer-name-string` is not nil, vterm renames automatically its own
diff --git a/elisp.c b/elisp.c
index 43f0ef9..8b4dfed 100644
--- a/elisp.c
+++ b/elisp.c
@@ -54,6 +54,7 @@ emacs_value Fvterm_invalidate;
emacs_value Feq;
emacs_value Fvterm_get_color;
emacs_value Fvterm_eval;
+emacs_value Fvterm_selection;
/* Set the function cell of the symbol named NAME to SFUN using
the 'fset' function. */
@@ -194,3 +195,9 @@ void vterm_invalidate(emacs_env *env) {
emacs_value vterm_eval(emacs_env *env, emacs_value string) {
return env->funcall(env, Fvterm_eval, 1, (emacs_value[]){string});
}
+
+emacs_value vterm_selection(emacs_env *env, emacs_value selection_target,
+ emacs_value selection_data) {
+ return env->funcall(env, Fvterm_selection, 2,
+ (emacs_value[]){selection_target, selection_data});
+}
diff --git a/elisp.h b/elisp.h
index d59f182..2fd64f4 100644
--- a/elisp.h
+++ b/elisp.h
@@ -57,6 +57,7 @@ extern emacs_value Fvterm_invalidate;
extern emacs_value Feq;
extern emacs_value Fvterm_get_color;
extern emacs_value Fvterm_eval;
+extern emacs_value Fvterm_selection;
// Utils
void bind_function(emacs_env *env, const char *name, emacs_value Sfun);
@@ -90,5 +91,7 @@ void set_directory(emacs_env *env, emacs_value string);
void vterm_invalidate(emacs_env *env);
emacs_value vterm_get_color(emacs_env *env, int index);
emacs_value vterm_eval(emacs_env *env, emacs_value string);
+emacs_value vterm_selection(emacs_env *env, emacs_value selection_target,
+ emacs_value selection_data);
#endif /* ELISP_H */
diff --git a/vterm-module.c b/vterm-module.c
index c99ed02..c976e95 100644
--- a/vterm-module.c
+++ b/vterm-module.c
@@ -578,6 +578,15 @@ static void term_redraw(Term *term, emacs_env *env) {
free(term->elisp_code);
term->elisp_code = NULL;
}
+ if (term->selection_data) {
+ emacs_value selection_target = env->make_string(
+ env, &term->selection_target[0], strlen(&term->selection_target[0]));
+ emacs_value selection_data = env->make_string(env, term->selection_data,
+ strlen(term->selection_data));
+ vterm_selection(env, selection_target, selection_data);
+ free(term->selection_data);
+ term->selection_data = NULL;
+ }
term->is_invalidated = false;
}
@@ -938,6 +947,10 @@ void term_finalize(void *object) {
free(term->cmd_buffer);
term->cmd_buffer = NULL;
}
+ if (term->selection_data) {
+ free(term->selection_data);
+ term->selection_data = NULL;
+ }
for (int i = 0; i < term->lines_len; i++) {
if (term->lines[i] != NULL) {
@@ -995,6 +1008,37 @@ static int handle_osc_cmd_51(Term *term, char subCmd, char *buffer) {
}
return 0;
}
+static int handle_osc_cmd_52(Term *term, char *buffer) {
+ /* OSC 52 ; Pc ; Pd BEL */
+ /* Manipulate Selection Data */
+ /* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html */
+ /* test by printf "\033]52;c;$(printf "%s" "blabla" | base64)\a" */
+
+ for (int i = 0; i < SELECTION_TARGET_MAX; i++) { /* reset Pc */
+ term->selection_target[i] = 0;
+ }
+ int selection_target_idx = 0;
+ size_t cmdlen = strlen(buffer);
+
+ for (int i = 0; i < cmdlen; i++) {
+ /* OSC 52 ; Pc ; Pd BEL */
+ if (buffer[i] == ';') { /* find the second ";" */
+ term->selection_data = malloc(cmdlen - i);
+ strcpy(term->selection_data, &buffer[i + 1]);
+ break;
+ }
+ if (selection_target_idx < SELECTION_TARGET_MAX) {
+ /* c , p , q , s , 0 , 1 , 2 , 3 , 4 , 5 , 6 , and 7 */
+ /* for clipboard, primary, secondary, select, or cut buffers 0 through 7
+ * respectively */
+ term->selection_target[selection_target_idx] = buffer[i];
+ selection_target_idx++;
+ } else { /* len of Pc should not >12 just ignore this cmd,am I wrong? */
+ return 0;
+ }
+ }
+ return 1;
+}
static int handle_osc_cmd(Term *term, int cmd, char *buffer) {
if (cmd == 51) {
char subCmd = '0';
@@ -1002,7 +1046,10 @@ static int handle_osc_cmd(Term *term, int cmd, char *buffer) {
return 0;
}
subCmd = buffer[0];
+ /* ++ skip the subcmd char */
return handle_osc_cmd_51(term, subCmd, ++buffer);
+ } else if (cmd == 52) {
+ return handle_osc_cmd_52(term, buffer);
}
return 0;
}
@@ -1019,10 +1066,13 @@ static int osc_callback(const char *command, size_t cmdlen, void *user) {
} else if (cmdlen > 4 && buffer[0] == '5' && buffer[1] == '1' &&
buffer[2] == ';' && buffer[3] == 'E') {
return handle_osc_cmd_51(term, 'E', &buffer[4]);
+ } else if (cmdlen > 4 && buffer[0] == '5' && buffer[1] == '2' &&
+ buffer[2] == ';') {
+ /* OSC 52 ; Pc ; Pd BEL */
+ return handle_osc_cmd_52(term, &buffer[3]);
}
return 0;
}
-
static VTermParserCallbacks parser_callbacks = {
.text = NULL,
.control = NULL,
@@ -1042,10 +1092,16 @@ static int osc_callback(int cmd, VTermStringFragment frag, void *user) {
/* "51;E" executes elisp code */
/* The elisp code is executed in term_redraw */
+ /* "52;[cpqs01234567];data" Manipulate Selection Data */
+ /* I think libvterm has bug ,sometimes when the data is long enough ,the final
+ * fragment is missed */
+ /* printf "\033]52;c;$(printf "%s" $(ruby -e 'print "x"*999999')|base64)\a"
+ */
+
Term *term = (Term *)user;
if (frag.initial) {
- /* drop old fragment,because this is a initial fragment */
+ /* drop old fragment,because this is a initial fragment */
if (term->cmd_buffer) {
free(term->cmd_buffer);
term->cmd_buffer = NULL;
@@ -1128,6 +1184,7 @@ emacs_value Fvterm_new(emacs_env *env, ptrdiff_t nargs, emacs_value args[],
term->directory_changed = false;
term->elisp_code = NULL;
term->elisp_code_changed = false;
+ term->selection_data = NULL;
term->cmd_buffer = NULL;
@@ -1337,6 +1394,8 @@ int emacs_module_init(struct emacs_runtime *ert) {
Fvterm_get_color =
env->make_global_ref(env, env->intern(env, "vterm--get-color"));
Fvterm_eval = env->make_global_ref(env, env->intern(env, "vterm--eval"));
+ Fvterm_selection =
+ env->make_global_ref(env, env->intern(env, "vterm--selection"));
// Exported functions
emacs_value fun;
diff --git a/vterm-module.h b/vterm-module.h
index 2b58541..6330827 100644
--- a/vterm-module.h
+++ b/vterm-module.h
@@ -38,6 +38,10 @@ enum {
VTERM_PROP_CURSOR_NOT_VISIBLE = 5,
};
+/* c , p , q , s , 0 , 1 , 2 , 3 , 4 , 5 , 6 , and 7 */
+/* clipboard, primary, secondary, select, or cut buffers 0 through 7 */
+#define SELECTION_TARGET_MAX 12
+
typedef struct Cursor {
int row, col;
int cursor_type;
@@ -75,6 +79,11 @@ typedef struct Term {
char *elisp_code;
bool elisp_code_changed;
+ /* c , p , q , s , 0 , 1 , 2 , 3 , 4 , 5 , 6 , and 7 */
+ /* clipboard, primary, secondary, select, or cut buffers 0 through 7 */
+ char selection_target[SELECTION_TARGET_MAX];
+ char *selection_data;
+
/* the size of dirs almost = window height, value = directory of that line */
LineInfo **lines;
int lines_len;
diff --git a/vterm.el b/vterm.el
index f1cd663..2c7a216 100644
--- a/vterm.el
+++ b/vterm.el
@@ -269,6 +269,22 @@ information on the how to configure the shell."
:type 'string
:group 'vterm)
+(defcustom vterm-enable-manipulate-selection-data-by-osc52 nil
+ "Support OSC 52 MANIPULATE SELECTION DATA.
+
+Support copy text to emacs kill ring and system clipboard by using OSC 52.
+For example: send base64 encoded 'foo' to kill ring: echo -en '\e]52;c;Zm9v\a',
+tmux can share its copy buffer to terminals by supporting osc52(like iterm2 xterm),
+you can enable this feature for tmux by :
+set -g set-clipboard on #osc 52 copy paste share with iterm
+set -ga terminal-overrides ',xterm*:XT:Ms=\E]52;%p1%s;%p2%s\007'
+set -ga terminal-overrides ',screen*:XT:Ms=\E]52;%p1%s;%p2%s\007'
+
+The clipboard querying/clearing functionality offered by OSC 52 is not implemented here,
+And for security reason, this feature is disabled by default."
+ :type 'boolean
+ :group 'vterm)
+
;; TODO: Improve doc string, it should not point to the readme but it should
;; be self-contained.
(defcustom vterm-eval-cmds '(("find-file" find-file)
@@ -922,6 +938,31 @@ Argument BUFFER the terminal buffer."
(when (cl-member (selected-window) windows :test #'eq)
(set-window-hscroll (selected-window) 0))))))))
+(defun vterm--selection (targets data)
+ "OSC 52 Manipulate Selection Data.
+Search Manipulate Selection Data in
+ https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ."
+ (when vterm-enable-manipulate-selection-data-by-osc52
+ (unless (or (string-equal data "?")
+ (string-empty-p data))
+ (let ((decoded-data (decode-coding-string
+ (base64-decode-string data) locale-coding-system))
+ (select-enable-clipboard select-enable-clipboard)
+ (select-enable-primary select-enable-primary))
+ ;; https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ ;; c , p , q , s , 0 , 1 , 2 , 3 , 4 , 5 , 6 , and 7
+ ;; clipboard, primary, secondary, select, or cut buffers 0 through 7
+ (unless (string-empty-p targets)
+ (setq select-enable-clipboard nil)
+ (setq select-enable-primary nil))
+ (when (cl-find ?c targets)
+ (setq select-enable-clipboard t))
+ (when (cl-find ?p targets)
+ (setq select-enable-primary t))
+
+ (kill-new decoded-data)
+ (message "kill-ring is updated by vterm OSC 52(Manipulate Selection Data)")))))
+
;;; Entry Points
;;;###autoload