ref: 96fd8182ad24e3eb429c971664cf39e7f5f2e1f2
parent: 8e2a36562564e33f7c0b38c0ccb8893a02e4ad77
author: cancel <cancel@cancel.fm>
date: Fri Dec 7 16:59:06 EST 2018
Add yielding based on timer
--- a/tui_main.c
+++ b/tui_main.c
@@ -468,6 +468,8 @@
Usz tick_num;
Usz ruler_spacing_y, ruler_spacing_x;
Tui_input_mode input_mode;
+ Usz bpm;
+ double accum_secs;
bool needs_remarking;
bool is_draw_dirty;
bool is_playing;
@@ -488,6 +490,8 @@
a->ruler_spacing_y = 8;
a->ruler_spacing_x = 8;
a->input_mode = Tui_input_mode_normal;
+ a->bpm = 120;
+ a->accum_secs = 0.0;
a->needs_remarking = true;
a->is_draw_dirty = false;
a->is_playing = false;
@@ -508,10 +512,45 @@
return a->is_draw_dirty || a->needs_remarking;
}
-void app_force_draw_dirty(App_state* a) {
- a->is_draw_dirty = true;
+double app_secs_to_deadline(App_state const* a) {
+ if (a->is_playing) {
+ double secs_span = 60.0 / (double)a->bpm;
+ double rem = secs_span - a->accum_secs;
+ if (rem < 0.0)
+ rem = 0.0;
+ return rem;
+ } else {
+ return 1.0;
+ }
}
+void app_apply_delta_secs(App_state* a, double secs) {
+ if (a->is_playing) {
+ a->accum_secs += secs;
+ }
+}
+
+void app_do_stuff(App_state* a) {
+ double secs_span = 60.0 / (double)a->bpm;
+ while (a->accum_secs > secs_span) {
+ a->accum_secs -= secs_span;
+ undo_history_push(&a->undo_hist, &a->field, a->tick_num);
+ orca_run(a->field.buffer, a->markmap_r.buffer, a->field.height,
+ a->field.width, a->tick_num, &a->bank, &a->oevent_list,
+ a->piano_bits);
+ ++a->tick_num;
+ a->piano_bits = ORCA_PIANO_BITS_NONE;
+ a->needs_remarking = true;
+ a->is_draw_dirty = true;
+ }
+}
+
+static double ms_to_sec(double ms) {
+ return ms / 1000.0;
+}
+
+void app_force_draw_dirty(App_state* a) { a->is_draw_dirty = true; }
+
void app_draw(App_state* a, WINDOW* win) {
// We can predictavely step the next simulation tick and then use the
// resulting markmap buffer for better UI visualization. If we don't do
@@ -676,10 +715,10 @@
case App_input_cmd_toggle_play_pause:
if (a->is_playing) {
a->is_playing = false;
- nodelay(stdscr, FALSE);
+ // nodelay(stdscr, FALSE);
} else {
a->is_playing = true;
- nodelay(stdscr, TRUE);
+ // nodelay(stdscr, TRUE);
}
a->is_draw_dirty = true;
break;
@@ -815,55 +854,70 @@
}
WINDOW* cont_win = NULL;
- int cont_win_h = 0;
- int cont_win_w = 0;
+ int key = KEY_RESIZE;
+ wtimeout(stdscr, 0);
+ U64 last_time = 0;
+ // double accum_time = 0.0;
for (;;) {
- int term_height = getmaxy(stdscr);
- int term_width = getmaxx(stdscr);
- assert(term_height >= 0 && term_width >= 0);
- int content_y = 0;
- int content_x = 0;
- int content_h = term_height;
- int content_w = term_width;
- int margins_2 = margin_thickness * 2;
- if (margin_thickness > 0 && term_height > margins_2 &&
- term_width > margins_2) {
- content_y += margin_thickness;
- content_x += margin_thickness;
- content_h -= margins_2;
- content_w -= margins_2;
- }
- if (cont_win == NULL || cont_win_h != content_h ||
- cont_win_w != content_w) {
- if (cont_win) {
- delwin(cont_win);
- }
- wclear(stdscr);
- cont_win = derwin(stdscr, content_h, content_w, content_y, content_x);
- cont_win_h = content_h;
- cont_win_w = content_w;
- app_force_draw_dirty(&app_state);
- }
-
- if (app_is_draw_dirty(&app_state)) {
- app_draw(&app_state, cont_win);
- }
-
- int key;
- // ncurses gives us ERR if there was no user input. We'll sleep for 0
- // seconds, so that we'll yield CPU time to the OS instead of looping as
- // fast as possible. This avoids battery drain/excessive CPU usage. There
- // are better ways to do this that waste less CPU, but they require doing a
- // little more work on each individual platform (Linux, Mac, etc.)
- for (;;) {
- key = wgetch(stdscr);
- if (key != ERR)
- break;
- sleep(0);
- }
-
switch (key) {
+ case ERR: {
+ U64 diff = stm_laptime(&last_time);
+ app_apply_delta_secs(&app_state, stm_sec(diff));
+ app_do_stuff(&app_state);
+ if (app_is_draw_dirty(&app_state)) {
+ app_draw(&app_state, cont_win);
+ }
+ diff = stm_laptime(&last_time);
+ app_apply_delta_secs(&app_state, stm_sec(diff));
+ double secs_to_d = app_secs_to_deadline(&app_state);
+ // fprintf(stderr, "to deadline: %f\n", secs_to_d);
+ if (secs_to_d < ms_to_sec(1.0)) {
+ wtimeout(stdscr, 0);
+ } else if (secs_to_d < ms_to_sec(3.0)) {
+ wtimeout(stdscr, 1);
+ } else if (secs_to_d < ms_to_sec(10.0)) {
+ wtimeout(stdscr, 5);
+ } else if (secs_to_d < ms_to_sec(50.0)) {
+ wtimeout(stdscr, 10);
+ } else {
+ wtimeout(stdscr, 20);
+ }
+ //struct timespec ts;
+ //ts.tv_sec = 0;
+ //// ts.tv_nsec = 1000 * 1000 * 1;
+ //ts.tv_nsec = 1;
+ //int ret = nanosleep(&ts, NULL);
+ //if (ret) {
+ // fprintf(stderr, "interrupted sleep: %d\n", ret);
+ //}
+ } break;
+ case KEY_RESIZE: {
+ int term_height = getmaxy(stdscr);
+ int term_width = getmaxx(stdscr);
+ assert(term_height >= 0 && term_width >= 0);
+ int content_y = 0;
+ int content_x = 0;
+ int content_h = term_height;
+ int content_w = term_width;
+ int margins_2 = margin_thickness * 2;
+ if (margin_thickness > 0 && term_height > margins_2 &&
+ term_width > margins_2) {
+ content_y += margin_thickness;
+ content_x += margin_thickness;
+ content_h -= margins_2;
+ content_w -= margins_2;
+ }
+ if (cont_win == NULL || getmaxy(cont_win) != content_h ||
+ getmaxx(cont_win) != content_w) {
+ if (cont_win) {
+ delwin(cont_win);
+ }
+ wclear(stdscr);
+ cont_win = derwin(stdscr, content_h, content_w, content_y, content_x);
+ app_force_draw_dirty(&app_state);
+ }
+ } break;
case AND_CTRL('q'):
goto quit;
case KEY_UP:
@@ -938,10 +992,7 @@
#endif
break;
}
-
- // ncurses gives us the special value KEY_RESIZE if the user didn't
- // actually type anything, but the terminal resized.
- // bool ignored_input = ch < CHAR_MIN || ch > CHAR_MAX || ch == KEY_RESIZE;
+ key = wgetch(stdscr);
}
quit:
if (cont_win) {