Sigrid Solveig HaflínudóttirMerge remote-tracking branch 'github/master'ed44a595 years agoHistory
3884 lines · 128.1 KB · C
RawDownload
#include "base.h"#include "field.h"#include "gbuffer.h"#include "osc_out.h"#include "oso.h"#include "sim.h"#include "sysmisc.h"#include "term_util.h"#include "vmio.h"#include <getopt.h>#include <locale.h> #define SOKOL_IMPL#include "sokol_time.h"#undef SOKOL_IMPL #ifdef FEAT_PORTMIDI#include <portmidi.h>#endif #if NCURSES_VERSION_PATCH < 20081122int _nc_has_mouse(void);#define has_mouse _nc_has_mouse#endif #define TIME_DEBUG 0#if TIME_DEBUGstatic int spin_track_timeout = 0;#endif #define staticni ORCA_NOINLINE static staticni void usage(void) { // clang-format offfprintf(stderr,"Usage: orca [options] [file]\n\n""General options:\n""    --undo-limit <number>  Set the maximum number of undo steps.\n""                           If you plan to work with large files,\n""                           set this to a low number.\n""                           Default: 100\n""    --initial-size <nxn>   When creating a new grid file, use these\n""                           starting dimensions.\n""    --bpm <number>         Set the tempo (beats per minute).\n""                           Default: 120\n""    --seed <number>        Set the seed for the random function.\n""                           Default: 1\n""    -h or --help           Print this message and exit.\n""\n""OSC/MIDI options:\n""    --strict-timing\n""        Attempt to reduce timing jitter of outgoing MIDI and OSC\n""        messages. Uses more CPU time. May have no effect.\n""\n""    --osc-midi-bidule <path>\n""        Set MIDI to be sent via OSC formatted for Plogue Bidule.\n""        The path argument is the path of the Plogue OSC MIDI device.\n""        Example: /OSC_MIDI_0/MIDI\n");} // clang-format on typedef enum {  Glyph_class_unknown,  Glyph_class_grid,  Glyph_class_comment,  Glyph_class_uppercase,  Glyph_class_lowercase,  Glyph_class_movement,  Glyph_class_numeric,  Glyph_class_bang,} Glyph_class; static Glyph_class glyph_class_of(Glyph glyph) {  if (glyph == '.')    return Glyph_class_grid;  if (glyph >= '0' && glyph <= '9')    return Glyph_class_numeric;  switch (glyph) {  case 'N':  case 'n':  case 'E':  case 'e':  case 'S':  case 's':  case 'W':  case 'w':    return Glyph_class_movement;  case '!':  case ':':  case ';':  case '=':  case '%':  case '?':  case '$':    return Glyph_class_lowercase;  case '*':    return Glyph_class_bang;  case '#':    return Glyph_class_comment;  }  if (glyph >= 'A' && glyph <= 'Z')    return Glyph_class_uppercase;  if (glyph >= 'a' && glyph <= 'z')    return Glyph_class_lowercase;  return Glyph_class_unknown;} static attr_t term_attrs_of_cell(Glyph g, Mark m) {  Glyph_class gclass = glyph_class_of(g);  attr_t attr = A_normal;  switch (gclass) {  case Glyph_class_unknown:    attr = A_bold | fg_bg(C_red, C_natural);    break;  case Glyph_class_grid:    attr = A_bold | fg_bg(C_black, C_natural);    break;  case Glyph_class_comment:    attr = A_dim | Cdef_normal;    break;  case Glyph_class_uppercase:    attr = A_normal | fg_bg(C_black, C_cyan);    break;  case Glyph_class_lowercase:  case Glyph_class_movement:  case Glyph_class_numeric:    attr = A_bold | Cdef_normal;    break;  case Glyph_class_bang:    attr = A_bold | Cdef_normal;    break;  }  if (gclass != Glyph_class_comment) {    if ((m & (Mark_flag_lock | Mark_flag_input)) ==        (Mark_flag_lock | Mark_flag_input)) {      // Standard locking input      attr = A_normal | Cdef_normal;    } else if ((m & Mark_flag_input) == Mark_flag_input) {      // Non-locking input      attr = A_normal | Cdef_normal;    } else if (m & Mark_flag_lock) {      // Locked only      attr = A_dim | Cdef_normal;    }  }  if (m & Mark_flag_output) {    attr = A_reverse;  }  if (m & Mark_flag_haste_input) {    attr = A_bold | fg_bg(C_cyan, C_natural);  }  return attr;} typedef enum {  Ged_input_mode_normal = 0,  Ged_input_mode_append,  Ged_input_mode_selresize,  Ged_input_mode_slide,} Ged_input_mode; typedef struct {  Usz y, x, h, w;} Ged_cursor; void ged_cursor_init(Ged_cursor *tc) {  tc->x = tc->y = 0;  tc->w = tc->h = 1;} static void ged_cursor_move_relative(Ged_cursor *tc, Usz field_h, Usz field_w,                                     Isz delta_y, Isz delta_x) {  Isz y0 = (Isz)tc->y + delta_y;  Isz x0 = (Isz)tc->x + delta_x;  if (y0 >= (Isz)field_h)    y0 = (Isz)field_h - 1;  if (y0 < 0)    y0 = 0;  if (x0 >= (Isz)field_w)    x0 = (Isz)field_w - 1;  if (x0 < 0)    x0 = 0;  tc->y = (Usz)y0;  tc->x = (Usz)x0;} staticni void draw_grid_cursor(WINDOW *win, int draw_y, int draw_x, int draw_h,                               int draw_w, Glyph const *gbuffer, Usz field_h,                               Usz field_w, int scroll_y, int scroll_x,                               Usz cursor_y, Usz cursor_x, Usz cursor_h,                               Usz cursor_w, Ged_input_mode input_mode,                               bool is_playing) {  (void)input_mode;  if (cursor_y >= field_h || cursor_x >= field_w)    return;  if (scroll_y < 0) {    draw_y += -scroll_y;    scroll_y = 0;  }  if (scroll_x < 0) {    draw_x += -scroll_x;    scroll_x = 0;  }  Usz offset_y = (Usz)scroll_y;  Usz offset_x = (Usz)scroll_x;  if (offset_y >= field_h || offset_x >= field_w)    return;  if (draw_y >= draw_h || draw_x >= draw_w)    return;  attr_t const curs_attr = A_reverse | A_bold | fg_bg(C_yellow, C_natural);  if (offset_y <= cursor_y && offset_x <= cursor_x) {    Usz cdraw_y = cursor_y - offset_y + (Usz)draw_y;    Usz cdraw_x = cursor_x - offset_x + (Usz)draw_x;    if (cdraw_y < (Usz)draw_h && cdraw_x < (Usz)draw_w) {      Glyph beneath = gbuffer[cursor_y * field_w + cursor_x];      char displayed;      if (beneath == '.') {        displayed = is_playing ? '@' : '~';      } else {        displayed = beneath;      }      chtype ch = (chtype)displayed | curs_attr;      wmove(win, (int)cdraw_y, (int)cdraw_x);      waddchnstr(win, &ch, 1);    }  }   // Early out for selection area that won't have any visual effect  if (cursor_h <= 1 && cursor_w <= 1)    return;   // Now mutate visually selected area under grid to have the selection color  // attributes. (This will rewrite the attributes on the cursor character we  // wrote above, but if it was the only character that would have been  // changed, we already early-outed.)  //  // We'll do this by reading back the characters on the grid from the curses  // window buffer, changing the attributes, then writing it back. This is  // easier than pulling the glyphs from the gbuffer, since we already did the  // ruler calculations to turn . into +, and we don't need special behavior  // for any other attributes (e.g. we don't show a special state for selected  // uppercase characters.)  //  // First, confine cursor selection to the grid field/gbuffer that actually  // exists, in case the cursor selection exceeds the area of the field.  Usz sel_rows = field_h - cursor_y;  if (cursor_h < sel_rows)    sel_rows = cursor_h;  Usz sel_cols = field_w - cursor_x;  if (cursor_w < sel_cols)    sel_cols = cursor_w;  // Now, confine the selection area to what's visible on screen. Kind of  // tricky since we have to handle it being partially visible from any edge on  // any axis, and we have to be mindful overflow.  Usz vis_sel_y;  Usz vis_sel_x;  if (offset_y > cursor_y) {    vis_sel_y = 0;    Usz sub_y = offset_y - cursor_y;    if (sub_y > sel_rows)      sel_rows = 0;    else      sel_rows -= sub_y;  } else {    vis_sel_y = cursor_y - offset_y;  }  if (offset_x > cursor_x) {    vis_sel_x = 0;    Usz sub_x = offset_x - cursor_x;    if (sub_x > sel_cols)      sel_cols = 0;    else      sel_cols -= sub_x;  } else {    vis_sel_x = cursor_x - offset_x;  }  vis_sel_y += (Usz)draw_y;  vis_sel_x += (Usz)draw_x;  if (vis_sel_y >= (Usz)draw_h || vis_sel_x >= (Usz)draw_w)    return;  Usz vis_sel_h = (Usz)draw_h - vis_sel_y;  Usz vis_sel_w = (Usz)draw_w - vis_sel_x;  if (sel_rows < vis_sel_h)    vis_sel_h = sel_rows;  if (sel_cols < vis_sel_w)    vis_sel_w = sel_cols;  if (vis_sel_w == 0 || vis_sel_h == 0)    return;  enum { Bufcount = 4096 };  chtype chbuffer[Bufcount];  if (Bufcount < vis_sel_w)    vis_sel_w = Bufcount;  for (Usz iy = 0; iy < vis_sel_h; ++iy) {    int at_y = (int)(vis_sel_y + iy);    int num = mvwinchnstr(win, at_y, (int)vis_sel_x, chbuffer, (int)vis_sel_w);    for (int ix = 0; ix < num; ++ix) {      chbuffer[ix] = (chtype)((chbuffer[ix] & (A_CHARTEXT | A_ALTCHARSET)) |                              (chtype)curs_attr);    }    waddchnstr(win, chbuffer, (int)num);  }} typedef struct Undo_node {  Field field;  Usz tick_num;  struct Undo_node *prev, *next;} Undo_node; typedef struct {  Undo_node *first, *last;  Usz count, limit;} Undo_history; static void undo_history_init(Undo_history *hist, Usz limit) {  *hist = (Undo_history){0};  hist->limit = limit;}static void undo_history_deinit(Undo_history *hist) {  Undo_node *a = hist->first;  while (a) {    Undo_node *b = a->next;    field_deinit(&a->field);    free(a);    a = b;  }} staticni bool undo_history_push(Undo_history *hist, Field *field,                                Usz tick_num) {  if (hist->limit == 0)    return false;  Undo_node *new_node;  if (hist->count == hist->limit) {    new_node = hist->first;    if (new_node == hist->last) {      hist->first = NULL;      hist->last = NULL;    } else {      hist->first = new_node->next;      hist->first->prev = NULL;    }  } else {    new_node = malloc(sizeof(Undo_node));    if (!new_node)      return false;    ++hist->count;    field_init(&new_node->field);  }  field_copy(field, &new_node->field);  new_node->tick_num = tick_num;  if (hist->last) {    hist->last->next = new_node;    new_node->prev = hist->last;  } else {    hist->first = new_node;    hist->last = new_node;    new_node->prev = NULL;  }  new_node->next = NULL;  hist->last = new_node;  return true;} staticni void undo_history_pop(Undo_history *hist, Field *out_field,                               Usz *out_tick_num) {  Undo_node *last = hist->last;  if (!last)    return;  field_copy(&last->field, out_field);  *out_tick_num = last->tick_num;  if (hist->first == last) {    hist->first = NULL;    hist->last = NULL;  } else {    Undo_node *new_last = last->prev;    new_last->next = NULL;    hist->last = new_last;  }  field_deinit(&last->field);  free(last);  --hist->count;} staticni void undo_history_apply(Undo_history *hist, Field *out_field,                                 Usz *out_tick_num) {  Undo_node *last = hist->last;  if (!last)    return;  field_copy(&last->field, out_field);  *out_tick_num = last->tick_num;} static Usz undo_history_count(Undo_history *hist) { return hist->count; } staticni void print_activity_indicator(WINDOW *win, Usz activity_counter) {  // 7 segments that can each light up as Colors different colors.  // This gives us Colors^Segments total configurations.  enum { Segments = 7, Colors = 4 };  Usz states = 1; // calculate Colors^Segments  for (Usz i = 0; i < Segments; ++i)    states *= Colors;  // Wrap the counter to the range of displayable configurations.  Usz val = activity_counter % states;  chtype lamps[Colors];#if 1 // Appearance where segments are always lit  lamps[0] = ACS_HLINE | fg_bg(C_black, C_natural) | A_bold;  lamps[1] = ACS_HLINE | fg_bg(C_white, C_natural) | A_normal;  lamps[2] = ACS_HLINE | A_bold;  lamps[3] = lamps[1];#elif 0 // Brighter appearance where segments are always lit  lamps[0] = ACS_HLINE | fg_bg(C_black, C_natural) | A_bold;  lamps[1] = ACS_HLINE | A_normal;  lamps[2] = ACS_HLINE | A_bold;  lamps[3] = lamps[1];#else   // Appearance where segments can turn off completely  lamps[0] = ' ';  lamps[1] = ACS_HLINE | fg_bg(C_black, C_natural) | A_bold;  lamps[2] = ACS_HLINE | A_normal;  lamps[3] = lamps[1];#endif  chtype buffer[Segments];  for (Usz i = 0; i < Segments; ++i) {    // Instead of a left-to-right, straightforward ascending least-to-most    // significant digits display, we'll display it as a spiral.    Usz j = i % 2 ? (6 - i / 2) : (i / 2);    buffer[j] = lamps[val % Colors];    val = val / Colors;  }  waddchnstr(win, buffer, Segments);  // If you want to see what various combinations of colors and attributes look  // like in different terminals.#if 0  waddch(win, 'a' | fg_bg(C_black, C_natural) | A_dim);  waddch(win, 'b' | fg_bg(C_black, C_natural) | A_normal);  waddch(win, 'c' | fg_bg(C_black, C_natural) | A_bold);  waddch(win, 'd' | A_dim);  waddch(win, 'e' | A_normal);  waddch(win, 'f' | A_bold);  waddch(win, 'g' | fg_bg(C_white, C_natural) | A_dim);  waddch(win, 'h' | fg_bg(C_white, C_natural) | A_normal);  waddch(win, 'i' | fg_bg(C_white, C_natural) | A_bold);#endif} staticni void advance_faketab(WINDOW *win, int offset_x, int tabstop) {  if (tabstop < 1)    return;  int y, x, h, w;  getyx(win, y, x);  getmaxyx(win, h, w);  (void)h;  x = ((x + tabstop - 1) / tabstop) * tabstop + offset_x % tabstop;  if (w < 1)    w = 1;  if (x >= w)    x = w - 1;  wmove(win, y, x);} staticni void draw_hud(WINDOW *win, int win_y, int win_x, int height, int width,                       char const *filename, Usz field_h, Usz field_w,                       Usz ruler_spacing_y, Usz ruler_spacing_x, Usz tick_num,                       Usz bpm, Ged_cursor const *ged_cursor,                       Ged_input_mode input_mode, Usz activity_counter) {  (void)height;  (void)width;  enum { Tabstop = 8 };  wmove(win, win_y, win_x);  wprintw(win, "%zux%zu", field_w, field_h);  advance_faketab(win, win_x, Tabstop);  wprintw(win, "%zu/%zu", ruler_spacing_x, ruler_spacing_y);  advance_faketab(win, win_x, Tabstop);  wprintw(win, "%zuf", tick_num);  advance_faketab(win, win_x, Tabstop);  wprintw(win, "%zu", bpm);  advance_faketab(win, win_x, Tabstop);  print_activity_indicator(win, activity_counter);  wmove(win, win_y + 1, win_x);  wprintw(win, "%zu,%zu", ged_cursor->x, ged_cursor->y);  advance_faketab(win, win_x, Tabstop);  wprintw(win, "%zu:%zu", ged_cursor->w, ged_cursor->h);  advance_faketab(win, win_x, Tabstop);  switch (input_mode) {  case Ged_input_mode_normal:    wattrset(win, A_normal);    waddstr(win, "insert");    break;  case Ged_input_mode_append:    wattrset(win, A_bold);    waddstr(win, "append");    break;  case Ged_input_mode_selresize:    wattrset(win, A_bold);    waddstr(win, "select");    break;  case Ged_input_mode_slide:    wattrset(win, A_reverse);    waddstr(win, "slide");    break;  }  advance_faketab(win, win_x, Tabstop);

Lines 1–500 of 3884.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t