From 23329f713a934f3dbae5c6cfb1c807489fe3c265 Mon Sep 17 00:00:00 2001 From: cancel Date: Fri, 3 Jan 2020 23:47:22 +0900 Subject: [PATCH] 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. --- base.h | 21 ++++++++++++++ cboard.c | 48 ++++++++++++++++++++++++++++++++ cboard.h | 15 ++++++++++ tool | 2 +- tui_main.c | 81 ++++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 cboard.c create mode 100644 cboard.h diff --git a/base.h b/base.h index bc14456..a72010e 100644 --- a/base.h +++ b/base.h @@ -92,3 +92,24 @@ ORCA_FORCE_STATIC_INLINE Usz orca_round_up_power2(Usz x) { #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; +} diff --git a/cboard.c b/cboard.c new file mode 100644 index 0000000..c1f0b0c --- /dev/null +++ b/cboard.c @@ -0,0 +1,48 @@ +#include "cboard.h" +#include "gbuffer.h" +#include + +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; +} diff --git a/cboard.h b/cboard.h new file mode 100644 index 0000000..5e12e87 --- /dev/null +++ b/cboard.h @@ -0,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); diff --git a/tool b/tool index 11ad50a..85e3089 100755 --- a/tool +++ b/tool @@ -293,7 +293,7 @@ build_target() { 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 diff --git a/tui_main.c b/tui_main.c index 3fcbac2..e00cfa0 100644 --- 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 @@ static Glyph_class glyph_class_of(Glyph glyph) { 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 @@ Bracketed_paste_sequence bracketed_paste_sequence_getch_ungetch(WINDOW* win) { 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 @@ int main(int argc, char** argv) { 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 @@ int main(int argc, char** argv) { 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);