ref: 23329f713a934f3dbae5c6cfb1c807489fe3c265
parent: e3c916bece6f6906cbad9ad4d1e48c240200d2d0
author: cancel <cancel@cancel.fm>
date: Fri Jan 3 18:47:22 EST 2020
Add start of X clipboard support via xclip. Using popen instead of pipes right now, so stderr from xclip might be sent to terminal, which is bad.
--- a/base.h
+++ b/base.h
@@ -92,3 +92,24 @@
#endif
return x + 1;
}
+
+ORCA_OK_IF_UNUSED
+static bool is_valid_glyph(Glyph c) {
+ if (c >= '0' && c <= '9')
+ return true;
+ if (c >= 'A' && c <= 'Z')
+ return true;
+ if (c >= 'a' && c <= 'z')
+ return true;
+ switch (c) {
+ case '!':
+ case '.':
+ case '*':
+ case ':':
+ case ';':
+ case '=':
+ case '#':
+ return true;
+ }
+ return false;
+}
--- /dev/null
+++ b/cboard.c
@@ -1,0 +1,48 @@
+#include "cboard.h"
+#include "gbuffer.h"
+#include <stdio.h>
+
+Cboard_error cboard_copy(Glyph const* gbuffer, Usz field_height,
+ Usz field_width, Usz rect_y, Usz rect_x, Usz rect_h,
+ Usz rect_w) {
+ (void)field_height;
+ FILE* fp = popen("xclip -i -selection clipboard", "w");
+ if (!fp)
+ return Cboard_error_popen_failed;
+ for (Usz iy = 0; iy < rect_h; iy++) {
+ Glyph const* row = gbuffer + (rect_y + iy) * field_width + rect_x;
+ for (Usz ix = 0; ix < rect_w; ix++) {
+ fputc(row[ix], fp);
+ }
+ if (iy < rect_h + 1)
+ fputc('\n', fp);
+ }
+ int status = pclose(fp);
+ return status ? Cboard_error_process_exit_error : Cboard_error_none;
+}
+
+Cboard_error cboard_paste(Glyph* gbuffer, Usz height, Usz width, Usz y, Usz x) {
+ FILE* fp = popen("xclip -o -selection clipboard", "r");
+ Usz start_x = x;
+ if (!fp)
+ return Cboard_error_popen_failed;
+ for (;;) {
+ int c = fgetc(fp);
+ if (c == EOF)
+ break;
+ if (c == '\r' || c == '\n') {
+ y++;
+ x = start_x;
+ continue;
+ }
+ if (c != ' ' && y < height && x < width) {
+ Glyph g = c <= CHAR_MAX && c >= CHAR_MIN && is_valid_glyph((Glyph)c)
+ ? (Glyph)c
+ : '.';
+ gbuffer_poke(gbuffer, height, width, y, x, g);
+ }
+ x++;
+ }
+ int status = pclose(fp);
+ return status ? Cboard_error_process_exit_error : Cboard_error_none;
+}
--- /dev/null
+++ b/cboard.h
@@ -1,0 +1,15 @@
+#pragma once
+#include "base.h"
+
+typedef enum {
+ Cboard_error_none = 0,
+ Cboard_error_unavailable,
+ Cboard_error_popen_failed,
+ Cboard_error_process_exit_error,
+} Cboard_error;
+
+Cboard_error cboard_copy(Glyph const* gbuffer, Usz field_height,
+ Usz field_width, Usz rect_y, Usz rect_x, Usz rect_h,
+ Usz rect_w);
+
+Cboard_error cboard_paste(Glyph* gbuffer, Usz height, Usz width, Usz y, Usz x);
--- a/tool
+++ b/tool
@@ -293,7 +293,7 @@
out_exe=cli
;;
orca|tui)
- add source_files osc_out.c term_util.c tui_main.c
+ add source_files osc_out.c term_util.c cboard.c tui_main.c
add cc_flags -D_XOPEN_SOURCE_EXTENDED=1
# thirdparty headers (like sokol_time.h) should get -isystem for their
# include dir so that any warnings they generate with our warning flags
--- a/tui_main.c
+++ b/tui_main.c
@@ -1,5 +1,6 @@
#include "bank.h"
#include "base.h"
+#include "cboard.h"
#include "field.h"
#include "gbuffer.h"
#include "osc_out.h"
@@ -119,26 +120,6 @@
return Glyph_class_unknown;
}
-static bool is_valid_glyph(Glyph c) {
- if (c >= '0' && c <= '9')
- return true;
- if (c >= 'A' && c <= 'Z')
- return true;
- if (c >= 'a' && c <= 'z')
- return true;
- switch (c) {
- case '!':
- case '.':
- case '*':
- case ':':
- case ';':
- case '=':
- case '#':
- return true;
- }
- return false;
-}
-
static attr_t term_attrs_of_cell(Glyph g, Mark m) {
Glyph_class gclass = glyph_class_of(g);
attr_t attr = A_normal;
@@ -2182,6 +2163,36 @@
return Bracketed_paste_sequence_none;
}
+void try_send_to_gui_clipboard(Ged const* a, bool* io_use_gui_clipboard) {
+ if (!*io_use_gui_clipboard)
+ return;
+ // If we want to use grid directly
+#if 0
+ Usz curs_y, curs_x, curs_h, curs_w;
+ if (!ged_try_selection_clipped_to_field(a, &curs_y, &curs_x, &curs_h,
+ &curs_w))
+ return;
+ Cboard_error cberr =
+ cboard_copy(a->clipboard_field.buffer, a->clipboard_field.height,
+ a->clipboard_field.width, curs_y, curs_x, curs_h, curs_w);
+#endif
+ Usz cb_h = a->clipboard_field.height, cb_w = a->clipboard_field.width;
+ if (cb_h < 1 || cb_w < 1)
+ return;
+ Cboard_error cberr =
+ cboard_copy(a->clipboard_field.buffer, cb_h, cb_w, 0, 0, cb_h, cb_w);
+ if (cberr) {
+ *io_use_gui_clipboard = false;
+ switch (cberr) {
+ case Cboard_error_none:
+ case Cboard_error_unavailable:
+ case Cboard_error_popen_failed:
+ case Cboard_error_process_exit_error:
+ break;
+ }
+ }
+}
+
int main(int argc, char** argv) {
static struct option tui_options[] = {
{"margins", required_argument, 0, Argopt_margins},
@@ -2211,6 +2222,7 @@
bool should_autosize_grid = true;
int init_grid_dim_y = 25;
int init_grid_dim_x = 57;
+ bool use_gui_cboard = true;
Midi_mode midi_mode;
midi_mode_init_null(&midi_mode);
@@ -2969,12 +2981,39 @@
break;
case CTRL_PLUS('x'):
ged_input_cmd(&ged_state, Ged_input_cmd_cut);
+ try_send_to_gui_clipboard(&ged_state, &use_gui_cboard);
break;
case CTRL_PLUS('c'):
ged_input_cmd(&ged_state, Ged_input_cmd_copy);
+ try_send_to_gui_clipboard(&ged_state, &use_gui_cboard);
break;
case CTRL_PLUS('v'):
- ged_input_cmd(&ged_state, Ged_input_cmd_paste);
+ if (use_gui_cboard) {
+ undo_history_push(&ged_state.undo_hist, &ged_state.field,
+ ged_state.tick_num);
+ Cboard_error cberr =
+ cboard_paste(ged_state.field.buffer, ged_state.field.height,
+ ged_state.field.width, ged_state.ged_cursor.y,
+ ged_state.ged_cursor.x);
+ if (cberr) {
+ undo_history_pop(&ged_state.undo_hist, &ged_state.field,
+ &ged_state.tick_num);
+ switch (cberr) {
+ case Cboard_error_none:
+ break;
+ case Cboard_error_unavailable:
+ case Cboard_error_popen_failed:
+ case Cboard_error_process_exit_error:
+ break;
+ }
+ use_gui_cboard = false;
+ ged_input_cmd(&ged_state, Ged_input_cmd_paste);
+ }
+ ged_state.needs_remarking = true;
+ ged_state.is_draw_dirty = true;
+ } else {
+ ged_input_cmd(&ged_state, Ged_input_cmd_paste);
+ }
break;
case '\'':
ged_input_cmd(&ged_state, Ged_input_cmd_toggle_selresize_mode);