Skip to content

Commit 245ef8c

Browse files
committed
Implement OSC52 support
When copying text, it will be available in the OS clipboard as well.
1 parent 3e26785 commit 245ef8c

13 files changed

Lines changed: 120 additions & 23 deletions

File tree

src/dged/command.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ struct command *lookup_command_by_hash(struct commands *commands,
4848

4949
int32_t execute_command(struct command *command, struct commands *commands,
5050
struct window *active_window, struct buffers *buffers,
51-
int argc, const char *argv[]) {
51+
struct display *display, int argc, const char *argv[]) {
5252

5353
return command->fn(
5454
(struct command_ctx){
5555
.buffers = buffers,
5656
.active_window = active_window,
5757
.userdata = command->userdata,
5858
.commands = commands,
59+
.display = display,
5960
.self = command,
6061
.saved_argv = {0},
6162
.saved_argc = 0,

src/dged/command.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
struct buffer;
1313
struct buffers;
1414
struct window;
15+
struct display;
1516

1617
/**
1718
* Execution context for a command
@@ -30,6 +31,11 @@ struct command_ctx {
3031
*/
3132
struct window *active_window;
3233

34+
/**
35+
* The display in use.
36+
*/
37+
struct display *display;
38+
3339
/**
3440
* A registry of available commands.
3541
*
@@ -145,6 +151,7 @@ void register_commands(struct commands *command_list, struct command *commands,
145151
* well.
146152
* @param[in] buffers The current list of buffers for context. Can be used for
147153
* example to create a buffer list.
154+
* @param [in] display The current display.
148155
* @param[in] argc Number of arguments to the command.
149156
* @param[in] argv The arguments to the command.
150157
*
@@ -153,7 +160,7 @@ void register_commands(struct commands *command_list, struct command *commands,
153160
*/
154161
int32_t execute_command(struct command *command, struct commands *commands,
155162
struct window *active_window, struct buffers *buffers,
156-
int argc, const char *argv[]);
163+
struct display *display, int argc, const char *argv[]);
157164

158165
/**
159166
* Hash the name of a command.

src/dged/display.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,3 +645,21 @@ void display_end_render(struct display *display) {
645645
end_update(display);
646646
flush_outbuf(display);
647647
}
648+
649+
void display_to_clipboard(struct display *display, struct s8 content) {
650+
putch(display, ESC);
651+
putch(display, ']');
652+
putch(display, '5');
653+
putch(display, '2');
654+
putch(display, ';');
655+
putch(display, 'c');
656+
putch(display, ';');
657+
658+
struct s8 b64 = s8base64enc(content);
659+
putchars(display, b64.s, b64.l);
660+
s8delete(b64);
661+
662+
putch(display, ESC);
663+
putch(display, '\\');
664+
flush_outbuf(display);
665+
}

src/dged/display.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <stddef.h>
33
#include <stdint.h>
44

5+
#include "s8.h"
6+
57
struct display;
68

79
struct render_command;
@@ -103,6 +105,11 @@ void display_render(struct display *display, struct command_list *command_list);
103105
*/
104106
void display_end_render(struct display *display);
105107

108+
/**
109+
* Put a string on the clipboard.
110+
*/
111+
void display_to_clipboard(struct display *display, struct s8 content);
112+
106113
/**
107114
* Create a new command list.
108115
*

src/dged/minibuffer.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@ int32_t minibuffer_execute(void) {
119119
argc += line.nbytes > 0 ? 1 : 0;
120120

121121
minibuffer_abort_prompt_internal(false);
122-
int32_t res = execute_command(c->self, c->commands, c->active_window,
123-
c->buffers, argc, (const char **)argv);
122+
int32_t res =
123+
execute_command(c->self, c->commands, c->active_window, c->buffers,
124+
c->display, argc, (const char **)argv);
124125

125126
free(l);
126127

src/dged/s8.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,35 @@ ssize_t s8find(struct s8 s, uint8_t c) {
222222

223223
return -1;
224224
}
225+
226+
static const char base64_enc_tbl[] =
227+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
228+
229+
struct s8 s8base64enc(struct s8 s) {
230+
struct s8 output = {.l = 0, .s = 0};
231+
size_t output_length = 4 * ((s.l + 2) / 3);
232+
output.s = calloc(1, output_length + 1);
233+
if (output.s == NULL) {
234+
return output;
235+
}
236+
237+
output.l = output_length;
238+
239+
for (size_t i = 0, j = 0; i < s.l;) {
240+
uint32_t octet_a = i < s.l ? s.s[i] : 0;
241+
++i;
242+
uint32_t octet_b = i < s.l ? s.s[i] : 0;
243+
++i;
244+
uint32_t octet_c = i < s.l ? s.s[i] : 0;
245+
++i;
246+
247+
uint32_t triple = (octet_a << 16) + (octet_b << 8) + octet_c;
248+
249+
output.s[j++] = base64_enc_tbl[(triple >> 18) & 0x3F];
250+
output.s[j++] = base64_enc_tbl[(triple >> 12) & 0x3F];
251+
output.s[j++] = (i > s.l + 1) ? '=' : base64_enc_tbl[(triple >> 6) & 0x3F];
252+
output.s[j++] = (i > s.l) ? '=' : base64_enc_tbl[triple & 0x3F];
253+
}
254+
255+
return output;
256+
}

src/dged/s8.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ struct s8 s8dup(struct s8 s);
3333
bool s8empty(struct s8 s);
3434
bool s8onlyws(struct s8 s);
3535

36+
struct s8 s8base64enc(struct s8 s);
37+
3638
#endif

src/main/cmds.c

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
#include "dged/buffers.h"
1515
#include "dged/command.h"
1616
#include "dged/display.h"
17+
#include "dged/location.h"
1718
#include "dged/minibuffer.h"
1819
#include "dged/path.h"
1920
#include "dged/s8.h"
2021
#include "dged/settings.h"
22+
#include "dged/text.h"
2123
#include "dged/vec.h"
2224
#include "dired.h"
2325
#if defined(SYNTAX_ENABLE)
@@ -109,7 +111,7 @@ int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]) {
109111
struct command *cmd = lookup_command(ctx.commands, argv[0]);
110112
if (cmd != NULL) {
111113
return execute_command(cmd, ctx.commands, ctx.active_window, ctx.buffers,
112-
argc - 1, argv + 1);
114+
ctx.display, argc - 1, argv + 1);
113115
} else {
114116
minibuffer_echo_timeout(4, "command %s not found", argv[0]);
115117
return 11;
@@ -168,7 +170,8 @@ int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
168170
disable_completion(minibuffer_buffer());
169171

170172
return execute_command(&do_switch_buffer_command, ctx.commands,
171-
ctx.active_window, ctx.buffers, argc, argv);
173+
ctx.active_window, ctx.buffers, ctx.display, argc,
174+
argv);
172175
}
173176

174177
int32_t do_kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
@@ -212,7 +215,8 @@ int32_t kill_buffer(struct command_ctx ctx, int argc, const char *argv[]) {
212215
disable_completion(minibuffer_buffer());
213216

214217
return execute_command(&do_switch_buffer_command, ctx.commands,
215-
ctx.active_window, ctx.buffers, argc, argv);
218+
ctx.active_window, ctx.buffers, ctx.display, argc,
219+
argv);
216220
}
217221

218222
static struct location draw_timer_value(struct buffer *buffer, double value,
@@ -389,7 +393,8 @@ int32_t buflist_visit_cmd(struct command_ctx ctx, int argc, const char **argv) {
389393
int32_t buflist_close_cmd(struct command_ctx ctx, int argc,
390394
const char *argv[]) {
391395
return execute_command(&do_switch_buffer_command, ctx.commands,
392-
ctx.active_window, ctx.buffers, argc, argv);
396+
ctx.active_window, ctx.buffers, ctx.display, argc,
397+
argv);
393398
}
394399

395400
void buflist_refresh(struct buffer *buffer, void *userdata) {
@@ -439,7 +444,7 @@ int32_t buflist_kill_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
439444
buffers_remove(ctx.buffers, bufname);
440445
free(bufname);
441446
execute_command(&buflist_refresh_command, ctx.commands, ctx.active_window,
442-
ctx.buffers, 0, NULL);
447+
ctx.buffers, ctx.display, 0, NULL);
443448
}
444449

445450
return 0;
@@ -468,7 +473,7 @@ int32_t buflist_save_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
468473
}
469474
free(bufname);
470475
execute_command(&buflist_refresh_command, ctx.commands, ctx.active_window,
471-
ctx.buffers, 0, NULL);
476+
ctx.buffers, ctx.display, 0, NULL);
472477
}
473478

474479
return 0;
@@ -572,7 +577,7 @@ static int32_t open_file(struct command_ctx ctx, const char *pth) {
572577
if (cmd != NULL) {
573578
const char *argv[] = {pth};
574579
return execute_command(cmd, ctx.commands, ctx.active_window, ctx.buffers,
575-
1, argv);
580+
ctx.display, 1, argv);
576581
}
577582

578583
minibuffer_echo_timeout(4, "dired is not supported");
@@ -777,14 +782,36 @@ BUFFER_VIEW_WRAPCMD(indent_alt)
777782
BUFFER_VIEW_WRAPCMD(unindent_line)
778783
BUFFER_VIEW_WRAPCMD(set_mark)
779784
BUFFER_VIEW_WRAPCMD(clear_mark)
780-
BUFFER_VIEW_WRAPCMD(copy)
781785
BUFFER_VIEW_WRAPCMD(cut)
782786
BUFFER_VIEW_WRAPCMD(paste)
783787
BUFFER_VIEW_WRAPCMD(paste_older)
784788
BUFFER_VIEW_WRAPCMD(goto_beginning)
785789
BUFFER_VIEW_WRAPCMD(goto_end)
786790
BUFFER_VIEW_WRAPCMD(sort_lines)
787791

792+
static int32_t copy_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
793+
(void)argc;
794+
(void)argv;
795+
struct buffer_view *bv = window_buffer_view(ctx.active_window);
796+
struct region reg = region_new(bv->dot, bv->mark);
797+
if (bv->mark_set && region_has_size(reg)) {
798+
/* send the copied text to the display. Display
799+
* in this case can mean X11/Wayland or terminal.
800+
* In the case of the terminal it uses OSC52.
801+
*/
802+
struct text_chunk txt = buffer_region(bv->buffer, reg);
803+
804+
display_to_clipboard(ctx.display,
805+
(struct s8){.s = txt.text, .l = txt.nbytes});
806+
if (txt.allocated) {
807+
free(txt.text);
808+
}
809+
}
810+
811+
buffer_view_copy(bv);
812+
return 0;
813+
}
814+
788815
static int32_t undo_cmd(struct command_ctx ctx, int argc, const char *argv[]) {
789816
(void)argc;
790817
(void)argv;

src/main/completion.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ static int32_t scroll_up_completions(struct command_ctx ctx, int argc,
125125
struct command *command = lookup_command(ctx.commands, "scroll-up");
126126
if (command != NULL) {
127127
return execute_command(command, ctx.commands, popup_window(), ctx.buffers,
128-
argc, argv);
128+
ctx.display, argc, argv);
129129
}
130130

131131
return 0;
@@ -140,7 +140,7 @@ static int32_t scroll_down_completions(struct command_ctx ctx, int argc,
140140
struct command *command = lookup_command(ctx.commands, "scroll-down");
141141
if (command != NULL) {
142142
return execute_command(command, ctx.commands, popup_window(), ctx.buffers,
143-
argc, argv);
143+
ctx.display, argc, argv);
144144
}
145145

146146
return 0;
@@ -155,7 +155,7 @@ static int32_t goto_first_completion(struct command_ctx ctx, int argc,
155155
struct command *command = lookup_command(ctx.commands, "goto-beginning");
156156
if (command != NULL) {
157157
return execute_command(command, ctx.commands, popup_window(), ctx.buffers,
158-
argc, argv);
158+
ctx.display, argc, argv);
159159
}
160160

161161
return 0;
@@ -170,7 +170,7 @@ static int32_t goto_last_completion(struct command_ctx ctx, int argc,
170170
struct command *command = lookup_command(ctx.commands, "goto-end");
171171
if (command != NULL) {
172172
return execute_command(command, ctx.commands, popup_window(), ctx.buffers,
173-
argc, argv);
173+
ctx.display, argc, argv);
174174
}
175175

176176
return 0;

src/main/dired.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ static int32_t dired_visit_cmd(struct command_ctx ctx, int argc,
388388
struct s8 path = canonicalize(new_path);
389389
const char *args[] = {s8ascstr(path)};
390390
int32_t res = execute_command(cmd, ctx.commands, ctx.active_window,
391-
ctx.buffers, 1, args);
391+
ctx.buffers, ctx.display, 1, args);
392392

393393
s8delete(new_path);
394394
s8delete(path);

0 commit comments

Comments
 (0)