diff --git a/term_util.c b/term_util.c index 5c173c6..87953ef 100644 --- a/term_util.c +++ b/term_util.c @@ -213,6 +213,53 @@ Qmsg* qmsg_push(int height, int width) { return qm; } +void qmsg_printf_push(char const* title, char const* fmt, ...) { + int titlewidth = title ? (int)strlen(title) : 0; + va_list ap; + va_start(ap, fmt); + int msgbytes = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + char* buffer = malloc((Usz)msgbytes + 1); + if (!buffer) + exit(1); + va_start(ap, fmt); + vsnprintf(buffer, (Usz)msgbytes + 1, fmt, ap); + va_end(ap); + int lines = 1; + int curlinewidth = 0; + int maxlinewidth = 0; + for (int i = 0; i < msgbytes; i++) { + if (buffer[i] == '\n') { + buffer[i] = '\0'; // This is terrifying :) + lines++; + if (curlinewidth > maxlinewidth) + maxlinewidth = curlinewidth; + curlinewidth = 0; + } else { + curlinewidth++; + } + } + if (curlinewidth > maxlinewidth) + maxlinewidth = curlinewidth; + int width = titlewidth > maxlinewidth ? titlewidth + 1 : maxlinewidth + 1; + Qmsg* msg = qmsg_push(lines, width); // no wrapping yet, no real wcwidth, etc + WINDOW* msgw = qmsg_window(msg); + int i = 0; + int offset = 0; + for (;;) { + if (offset == msgbytes + 1) + break; + int numbytes = (int)strlen(buffer + offset); + wmove(msgw, i, 1); + waddstr(msgw, buffer + offset); + offset += numbytes + 1; + i++; + } + free(buffer); + if (title) + qmsg_set_title(msg, title); +} + bool qmsg_drive(Qmsg* qm, int key) { (void)qm; switch (key) { @@ -236,6 +283,7 @@ Qmenu* qmenu_create(int id) { qm->id = id; return qm; } +void qmenu_destroy(Qmenu* qm) { qmenu_free(qm); } int qmenu_id(Qmenu const* qm) { return qm->id; } void qmenu_set_title(Qmenu* qm, char const* title) { qblock_set_title(&qm->qblock, title); diff --git a/term_util.h b/term_util.h index 3eefbb9..979ddb2 100644 --- a/term_util.h +++ b/term_util.h @@ -119,10 +119,18 @@ void qblock_set_title(Qblock* qb, char const* title); Qmsg* qmsg_push(int height, int width); WINDOW* qmsg_window(Qmsg* qm); void qmsg_set_title(Qmsg* qm, char const* title); +#ifdef __GNUC__ +__attribute__((format(printf, 2, 3))) +#endif +void qmsg_printf_push(char const* title, char const* fmt, ...); bool qmsg_drive(Qmsg* qm, int key); Qmsg* qmsg_of(Qblock* qb); Qmenu* qmenu_create(int id); +// Useful if menu creation needs to be aborted part-way. Otherwise, no need to +// call -- pushing the qmenu to the qnav stack transfers ownership. (Still +// working on this design, not sure yet.) +void qmenu_destroy(Qmenu* qm); int qmenu_id(Qmenu const* qm); void qmenu_set_title(Qmenu* qm, char const* title); void qmenu_add_choice(Qmenu* qm, char const* text, int id); diff --git a/tui_main.c b/tui_main.c index 0cf9bd2..e53f24b 100644 --- a/tui_main.c +++ b/tui_main.c @@ -1859,6 +1859,9 @@ enum { Set_grid_dims_form_id, Autofit_menu_id, Confirm_new_file_menu_id, +#ifdef FEAT_PORTMIDI + Portmidi_output_device_menu_id, +#endif }; enum { Open_name_text_line_id = 1, @@ -1892,6 +1895,9 @@ enum { Main_menu_set_grid_dims, Main_menu_autofit_grid, Main_menu_about, +#ifdef FEAT_PORTMIDI + Main_menu_choose_portmidi_output, +#endif }; void push_main_menu(void) { @@ -1906,6 +1912,10 @@ void push_main_menu(void) { qmenu_add_choice(qm, "Set Grid Size...", Main_menu_set_grid_dims); qmenu_add_choice(qm, "Auto-fit Grid", Main_menu_autofit_grid); qmenu_add_spacer(qm); +#ifdef FEAT_PORTMIDI + qmenu_add_choice(qm, "PortMIDI Output", Main_menu_choose_portmidi_output); + qmenu_add_spacer(qm); +#endif qmenu_add_choice(qm, "Controls...", Main_menu_controls); qmenu_add_choice(qm, "Operators...", Main_menu_opers_guide); qmenu_add_choice(qm, "About...", Main_menu_about); @@ -2162,6 +2172,38 @@ void push_set_grid_dims_form(Usz init_height, Usz init_width) { qform_push_to_nav(qf); } +#ifdef FEAT_PORTMIDI +void push_portmidi_output_device_menu(void) { + Qmenu* qm = qmenu_create(Portmidi_output_device_menu_id); + qmenu_set_title(qm, "PortMidi Device Selection"); + PmError e = portmidi_init_if_necessary(); + if (e) { + qmenu_destroy(qm); + qmsg_printf_push("PortMidi Error", + "PortMidi error during initialization:\n%s", + Pm_GetErrorText(e)); + return; + } + int num = Pm_CountDevices(); + int output_devices = 0; + for (int i = 0; i < num; ++i) { + PmDeviceInfo const* info = Pm_GetDeviceInfo(i); + if (!info || !info->output) + continue; + qmenu_add_choice(qm, info->name, i); + // printf("ID: %-4d Name: %s\n", i, info->name); + ++output_devices; + } + if (output_devices == 0) { + qmenu_destroy(qm); + qmsg_printf_push("No PortMidi Devices", + "No PortMidi output devices found."); + return; + } + qmenu_push_to_nav(qm); +} +#endif + // // Misc utils // @@ -2849,6 +2891,11 @@ int main(int argc, char** argv) { case Main_menu_autofit_grid: push_autofit_menu(); break; +#ifdef FEAT_PORTMIDI + case Main_menu_choose_portmidi_output: + push_portmidi_output_device_menu(); + break; +#endif } } else if (qmenu_id(qm) == Autofit_menu_id) { Usz new_field_h, new_field_w;