shithub: orca

Download patch

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);