From 78face694ee2d734f51ecaa3702b1849e1f34346 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Mon, 12 Feb 2024 15:36:55 +0100 Subject: UI: add some icons; Update sokol --- source/saw/main.c | 324 +++++++++++++++++++++++++++++++++++------------ source/sokol/sokol_app.h | 243 ++++++++++++++++------------------- 2 files changed, 356 insertions(+), 211 deletions(-) diff --git a/source/saw/main.c b/source/saw/main.c index c793517..865b0ab 100644 --- a/source/saw/main.c +++ b/source/saw/main.c @@ -4,6 +4,7 @@ #include "../kit/mersenne_twister_64.h" #include "../kit/secure_random.h" #include "../kit/file.h" +#include "../kit/threads.h" #if defined(__EMSCRIPTEN__) # include @@ -181,6 +182,12 @@ static saw_compose_t saw_compose = { .duplicate_input = 0, }; +// ================================================================ +// +// Audio +// +// ================================================================ + #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" @@ -309,6 +316,10 @@ static f64 saw_oscillator(i32 type, f64 frequency, f64 phase, static void saw_audio(ma_device *device, void *void_out_, void const *void_in_, ma_uint32 frame_count) { + // FIXME + // Render to audio buffer separately. + // + saw_playback(frame_count); f32 *out = (f32 *) void_out_; @@ -361,6 +372,76 @@ static void saw_audio(ma_device *device, void *void_out_, # pragma GCC diagnostic pop #endif +// ================================================================ +// +// UI +// +// ================================================================ + +static void saw_ui_header(i64 x0, u64 y0, u64 width, i64 height) { + i64 frame_height = sapp_height(); + + i64 border = 12; + + nvgFontSize(saw_nvg, height - border * 2); + nvgFontFaceId(saw_nvg, saw_font_icons); + + i64 x = x0 + border; + i64 h = frame_height - y0 - height + height - border; + + char backward_fast[] = "\uf049"; + char play[] = "\uf04b"; + char stop[] = "\uf04d"; + char anchor[] = "\uf13d"; + char hand_pointer[] = "\uf25a"; + char eraser[] = "\uf12d"; + char panning[] = "\uf047"; + char clone[] = "\uf24d"; + + u8 color[] = { 220, 220, 220, 255 }; + +#define ICON_(i_) \ + do { \ + b8 has_cursor = saw_mouse_x >= x && saw_mouse_x < x + height && \ + saw_mouse_y >= frame_height - y0 - height && \ + saw_mouse_y < frame_height - y0; \ + if (has_cursor) \ + nvgFillColor(saw_nvg, nvgRGBA(255, 255, 220, 255)); \ + nvgText(saw_nvg, x, h, (i_), (i_) + (sizeof(i_) - 1)); \ + if (has_cursor) \ + nvgFillColor(saw_nvg, \ + nvgRGBA(color[0], color[1], color[2], color[3])); \ + x += height; \ + } while (0) + + nvgFillColor(saw_nvg, + nvgRGBA(color[0], color[1], color[2], color[3])); + + ICON_(backward_fast); + + if (saw_playback_on) + ICON_(stop); + else + ICON_(play); + + ICON_(anchor); + ICON_(hand_pointer); + + color[0] = 220; + color[1] = 220; + color[2] = 220; + color[3] = 160; + + nvgFillColor(saw_nvg, + nvgRGBA(color[0], color[1], color[2], color[3])); + + ICON_(eraser); + ICON_(panning); + ICON_(clone); + +#undef ICON_ +} + static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { i64 frame_height = sapp_height(); @@ -374,12 +455,11 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { nvgBeginPath(saw_nvg); nvgRect(saw_nvg, x0, frame_height - y0 - height + border, width, - frame_height - y0 - height + track_height / 5 - border * 2); + track_height / 5 - border * 2); nvgRect(saw_nvg, x0, frame_height - y0 - height + border + (track_height * 4) / 5, - width, - frame_height - y0 - height + track_height / 5 - border * 2); + width, track_height / 5 - border * 2); nvgFillColor(saw_nvg, nvgRGBA(180, 140, 120, 160)); nvgFill(saw_nvg); @@ -688,10 +768,10 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { // Playback indicator // - i32 x = x0 + saw_compose.offset_x - border * 2 + + i64 x = x0 + saw_compose.offset_x - border * 2 + (saw_playback_frame * grid_scale + SAMPLE_RATE / 2) / SAMPLE_RATE; - i32 w = border * 4; + i64 w = border * 4; if (x >= x0 - border * 2 && x < x0 + width) { nvgBeginPath(saw_nvg); @@ -736,10 +816,10 @@ static void saw_ui_compose(i64 x0, i64 y0, i64 width, i64 height) { saw_mouse_x < x0 + width && saw_mouse_y >= frame_height - y0 - height && saw_mouse_y < frame_height - y0) { - i32 dx = x0 + saw_compose.offset_x; - i32 s = grid_scale / grid_rate; - i32 x = dx + ((saw_mouse_x - dx + s / 2) / s) * s; - i32 w = border * 4; + i64 dx = x0 + saw_compose.offset_x; + i64 s = grid_scale / grid_rate; + i64 x = dx + ((saw_mouse_x - dx + s / 2) / s) * s; + i64 w = border * 4; nvgBeginPath(saw_nvg); nvgRect(saw_nvg, x - w / 2, frame_height - y0 - height, w, @@ -772,7 +852,7 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0, #endif } - for (i32 input_index = TRACK_INPUT_INSTRUMENT; + for (i64 input_index = TRACK_INPUT_INSTRUMENT; input_index <= TRACK_INPUT_RELEASE; ++input_index) { // FIXME // Implement Unison and Spread. @@ -980,16 +1060,16 @@ static void saw_ui_track(saw_track_t *track, i64 x0, i64 y0, static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i64 height, str_t title) { - i32 frame_height = sapp_height(); + i64 frame_height = sapp_height(); - i32 text_height = 35; - i32 header_height = 35; - i32 pianokey_height = 35; - i32 pianokey_width = 100; - i32 border = 2; + i64 text_height = 35; + i64 header_height = 35; + i64 pianokey_height = 35; + i64 pianokey_width = 100; + i64 border = 2; - i32 sheet_offset = 40; - i32 sheet_scale = (40 * SAMPLE_RATE) / (10000 * roll->rate); + i64 sheet_offset = 40; + i64 sheet_scale = (40 * SAMPLE_RATE) / (10000 * roll->rate); // Title text // @@ -1017,10 +1097,28 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i64 w = width - pianokey_width - sheet_offset; i64 h = header_height / 5; + char repeat[] = "\uf363"; + + nvgFontSize(saw_nvg, header_height - 4); + nvgFontFaceId(saw_nvg, saw_font_icons); + nvgFillColor(saw_nvg, nvgRGBA(180, 80, 40, 200)); + nvgText(saw_nvg, x - header_height * 2 + 2, y + header_height - 2, + repeat, repeat + (sizeof repeat - 1)); + + i64 w0 = (roll->loop_duration * roll->rate * sheet_scale + + SAMPLE_RATE / 2) / + SAMPLE_RATE; + nvgBeginPath(saw_nvg); - nvgRect(saw_nvg, x, y, w, h); - nvgRect(saw_nvg, x, y + h * 4, w, h); - nvgFillColor(saw_nvg, nvgRGBA(80, 60, 50, 160)); + nvgRect(saw_nvg, x, y, w0, h); + nvgRect(saw_nvg, x, y + h * 4, w0, h); + nvgFillColor(saw_nvg, nvgRGBA(180, 80, 40, 160)); + nvgFill(saw_nvg); + + nvgBeginPath(saw_nvg); + nvgRect(saw_nvg, x + w0, y, w - w0, h); + nvgRect(saw_nvg, x + w0, y + h * 4, w - w0, h); + nvgFillColor(saw_nvg, nvgRGBA(80, 80, 80, 160)); nvgFill(saw_nvg); if (saw_mouse_x >= x && saw_mouse_y >= y && saw_mouse_x < x + w && @@ -1029,7 +1127,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, roll->loop_input = 1; if (roll->loop_input && saw_lbutton_down) { - i32 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset - + i64 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset - roll->offset_x + sheet_scale / 2) / sheet_scale; @@ -1049,12 +1147,12 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, b8 hover_any = 0; - for (i32 pitch = 0; pitch < PITCH_COUNT; pitch++) { - i32 x = x0 + border; - i32 y = frame_height - y0 - (pitch + 1) * pianokey_height + + for (i64 pitch = 0; pitch < PITCH_COUNT; pitch++) { + i64 x = x0 + border; + i64 y = frame_height - y0 - (pitch + 1) * pianokey_height + border + roll->offset_y; - i32 w = pianokey_width - border * 2; - i32 h = pianokey_height - border * 2; + i64 w = pianokey_width - border * 2; + i64 h = pianokey_height - border * 2; if (y > frame_height - y0 - pianokey_height) continue; @@ -1118,8 +1216,8 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, // Draw music sheet // - for (i32 pitch = 0; pitch < PITCH_COUNT; pitch++) { - i32 y = frame_height - y0 - (pitch + 1) * pianokey_height + + for (i64 pitch = 0; pitch < PITCH_COUNT; pitch++) { + i64 y = frame_height - y0 - (pitch + 1) * pianokey_height + roll->offset_y; if (y > frame_height - y0 - pianokey_height) @@ -1127,19 +1225,19 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (y < frame_height - y0 - height + text_height + header_height) break; - i32 h = pianokey_height; + i64 h = pianokey_height; - for (i32 t = 0; t < (roll->duration * roll->rate) / SAMPLE_RATE; + for (i64 t = 0; t < (roll->duration * roll->rate) / SAMPLE_RATE; t++) { - i32 x = x0 + pianokey_width + sheet_offset + t * sheet_scale + + i64 x = x0 + pianokey_width + sheet_offset + t * sheet_scale + roll->offset_x; if (x >= x0 + width - sheet_scale - border) break; - i32 note = -1; + i64 note = -1; - for (i32 n = 0; n < SHEET_SIZE; n++) { + for (i64 n = 0; n < SHEET_SIZE; n++) { saw_roll_note_t *p = roll->notes + n; if (p->enabled && p->pitch == pitch && t >= (p->time * roll->rate + SAMPLE_RATE / 2) / @@ -1161,7 +1259,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (x < x0 + pianokey_width + sheet_offset) continue; - i32 w = sheet_scale; + i64 w = sheet_scale; nvgBeginPath(saw_nvg); nvgRect(saw_nvg, x + border, y + border, w - border * 2, @@ -1187,7 +1285,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (!roll->grid_input && !turned_off && has_cursor && saw_lbutton_click) - for (i32 n = 0; n < SHEET_SIZE; n++) + for (i64 n = 0; n < SHEET_SIZE; n++) if (!roll->notes[n].enabled) { roll->notes[n] = (saw_roll_note_t) { .enabled = 1, @@ -1208,12 +1306,12 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, // Draw notes // - for (i32 n = 0; n < SHEET_SIZE; n++) { + for (i64 n = 0; n < SHEET_SIZE; n++) { saw_roll_note_t *note = roll->notes + n; if (!note->enabled) continue; - i32 y = frame_height - y0 - (note->pitch + 1) * pianokey_height + + i64 y = frame_height - y0 - (note->pitch + 1) * pianokey_height + roll->offset_y; if (y + pianokey_height > frame_height - y0) @@ -1228,7 +1326,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, i64 w = (note->duration * roll->rate * sheet_scale + SAMPLE_RATE / 2) / SAMPLE_RATE; - i32 h = pianokey_height; + i64 h = pianokey_height; b8 has_cursor = (roll->grid_input && roll->grid_note == n) || (saw_mouse_x >= x && saw_mouse_x < x + w && @@ -1253,10 +1351,10 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, frame >= roll->time + note->time && frame < roll->time + note->time + note->duration; - i32 underflow = (x0 + pianokey_width + sheet_offset + + i64 underflow = (x0 + pianokey_width + sheet_offset + sheet_scale - 1 - x) / sheet_scale; - i32 overflow = (x + w + sheet_scale + 1 - x0 - width) / + i64 overflow = (x + w + sheet_scale + 1 - x0 - width) / sheet_scale; if (underflow > 0) { @@ -1288,7 +1386,7 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, if (roll->grid_input) { if (saw_lbutton_down) { - i32 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset - + i64 t = (saw_mouse_x - x0 - pianokey_width - sheet_offset - roll->offset_x) / sheet_scale; @@ -1344,12 +1442,12 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, frame -= ((frame - roll->time) / roll->loop_duration) * roll->loop_duration; - i32 x = x0 + pianokey_width + sheet_offset + roll->offset_x - + i64 x = x0 + pianokey_width + sheet_offset + roll->offset_x - border * 2 + ((frame - roll->time) * roll->rate * sheet_scale + SAMPLE_RATE / 2) / SAMPLE_RATE; - i32 w = border * 4; + i64 w = border * 4; if (x >= x0 + pianokey_width + sheet_offset - border * 2 && x < x0 + width) { @@ -1367,11 +1465,11 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, saw_mouse_x < x0 + width && saw_mouse_y >= frame_height - y0 - height && saw_mouse_y < frame_height - y0) { - i32 dx = x0 + pianokey_width + sheet_offset + roll->offset_x; - i32 x = dx + + i64 dx = x0 + pianokey_width + sheet_offset + roll->offset_x; + i64 x = dx + ((saw_mouse_x - dx + sheet_scale / 2) / sheet_scale) * sheet_scale; - i32 w = border * 4; + i64 w = border * 4; nvgBeginPath(saw_nvg); nvgRect(saw_nvg, x - w / 2, @@ -1382,6 +1480,12 @@ static void saw_ui_roll(saw_roll_t *roll, i64 x0, i64 y0, i64 width, } } +// ================================================================ +// +// Event handling +// +// ================================================================ + static void saw_init(void) { // Init RNG // @@ -1630,8 +1734,14 @@ static void saw_init(void) { } static void saw_frame(void) { - i32 frame_width = sapp_width(); - i32 frame_height = sapp_height(); + // FIXME + // - Adjust sleep depending on how much compute we need + + // Sleep to prevent high CPU load + thrd_sleep(&(struct timespec) { .tv_nsec = 10000000 }, NULL); + + i64 frame_width = sapp_width(); + i64 frame_height = sapp_height(); glViewport(0, 0, frame_width, frame_height); glClearColor(.15f, .12f, .10f, 1.f); @@ -1645,31 +1755,70 @@ static void saw_frame(void) { nvgBeginFrame(saw_nvg, frame_width, frame_height, sapp_dpi_scale()); - i32 track_width = frame_width / 5; - if (track_width < 330) - track_width = 330; - if (track_width > frame_width / 2) - track_width = frame_width / 2; + // Render UI + // + { + // Adjust UI layout + // + i64 track_width = frame_width / 5; + if (track_width < 330) + track_width = 330; + if (track_width > frame_width / 2) + track_width = frame_width / 2; + + i64 header_height = 80; + i64 roll_height = (frame_height * 3) / 5; + i64 compose_height = frame_height - roll_height - header_height; + i64 track_height = frame_height - header_height; + + // Render header + // - saw_ui_compose(0, (frame_height * 3) / 5, frame_width - track_width, - (frame_height * 2) / 5); + saw_ui_header(0, // x0 + frame_height - header_height, // y0 + frame_width, // width + header_height // height + ); - if (saw_current_track != -1) { - char buf[64]; - sprintf(buf, "Track %lld", saw_current_track + 1); + // Render compose view + // + saw_ui_compose(0, // x0 + roll_height, // y0 + frame_width - track_width, // width + compose_height // height + ); + + if (saw_current_track != -1) { + // Render track view + // - saw_ui_track(saw_tracks + saw_current_track, - frame_width - track_width, 0, track_width, - frame_height, kit_str(strlen(buf), buf)); - } + char buf[64]; + sprintf(buf, "Track %lld", saw_current_track + 1); - if (saw_current_roll != -1) { - char buf[64]; - sprintf(buf, "Sheet %lld", saw_current_roll + 1); + saw_ui_track(saw_tracks + saw_current_track, // track + frame_width - track_width, // x0 + 0, // y0 + track_width, // width + track_height, // height + kit_str(strlen(buf), buf) // label + ); + } + + if (saw_current_roll != -1) { + // Render roll view + // + + char buf[64]; + sprintf(buf, "Sheet %lld", saw_current_roll + 1); - saw_ui_roll(saw_rolls + saw_current_roll, 0, 0, - frame_width - track_width, (frame_height * 3) / 5, - kit_str(strlen(buf), buf)); + saw_ui_roll(saw_rolls + saw_current_roll, // roll + 0, // x0 + 0, // y0 + frame_width - track_width, // width + roll_height, // height + kit_str(strlen(buf), buf) // label + ); + } } nvgEndFrame(saw_nvg); @@ -1798,6 +1947,8 @@ static void saw_event(sapp_event const *event) { saw_mouse_y = event->mouse_y; for (i64 i = 0; i < ROLL_COUNT; i++) { + // Panning control + // if (saw_rolls[i].offset_x_input) saw_rolls[i].offset_x += event->mouse_dx; if (saw_rolls[i].offset_y_input) @@ -1805,11 +1956,16 @@ static void saw_event(sapp_event const *event) { } if (saw_compose.offset_input) { + // Panning control + // saw_compose.offset_x += event->mouse_dx; saw_compose.offset_y += event->mouse_dy; } if (saw_current_track != -1) { + // Value control + // + saw_track_t *track = saw_tracks + saw_current_track; track->value_buffer -= (event->modifiers & @@ -1820,6 +1976,8 @@ static void saw_event(sapp_event const *event) { ? event->mouse_dy : event->mouse_dy * 20; + // Change input value buffer for selected value. + // switch (track->value_input) { case TRACK_INPUT_INSTRUMENT: track->instrument = (i64) (track->value_buffer * .002 + @@ -1953,12 +2111,19 @@ static void saw_event(sapp_event const *event) { if (!event->key_repeat) { switch (event->key_code) { case SAPP_KEYCODE_SPACE: + // Playback control saw_playback_on = !saw_playback_on; break; - case SAPP_KEYCODE_ENTER: saw_playback_frame = 0; break; + case SAPP_KEYCODE_ENTER: + // Reset playback time + saw_playback_frame = 0; + break; case SAPP_KEYCODE_ESCAPE: + // Reset panning + // + if (saw_current_roll != -1) { saw_rolls[saw_current_roll].offset_x = 0; saw_rolls[saw_current_roll].offset_y = @@ -1970,7 +2135,10 @@ static void saw_event(sapp_event const *event) { break; - case SAPP_KEYCODE_D: saw_compose.duplicate_input = 1; break; + case SAPP_KEYCODE_D: + // Duplicate selected track + saw_compose.duplicate_input = 1; + break; default:; } @@ -1986,10 +2154,9 @@ char const *__lsan_default_suppressions() { return "leak:nvidia"; } -static void log_(const char *tag, uint32_t log_level, - uint32_t log_item_id, const char *message_or_null, - uint32_t line_nr, const char *filename_or_null, - void *user_data) { +static void log_(char const *tag, u32 log_level, u32 log_item_id, + char const *message_or_null, u32 line_nr, + char const *filename_or_null, void *user_data) { if (message_or_null != NULL) printf("%s", message_or_null); printf("\n"); @@ -2007,7 +2174,7 @@ sapp_desc sokol_main(i32 argc, char **argv) { else if (i > 0) printf("Unknown command line argument: \"%s\"\n", argv[i]); - if (print_version) { + if (print_version) printf("Saw v%d.%d.%d" #if VERSION_DEV "_dev" @@ -2017,11 +2184,12 @@ sapp_desc sokol_main(i32 argc, char **argv) { #endif " - Music sequencer standalone application.\n", VERSION_MAJOR, VERSION_MINOR, VERSION_BABY); - exit(0); - } fflush(stdout); + if (print_version) + exit(0); + return (sapp_desc) { .window_title = "Saw", .width = 1280, diff --git a/source/sokol/sokol_app.h b/source/sokol/sokol_app.h index fdaed1f..064a72f 100644 --- a/source/sokol/sokol_app.h +++ b/source/sokol/sokol_app.h @@ -537,7 +537,7 @@ Like clipboard support, drag'n'drop support must be explicitly enabled at startup in the sapp_desc struct. - sapp_desc sokol_main() { + sapp_desc sokol_main(void) { return (sapp_desc) { .enable_dragndrop = true, // default is false ... @@ -547,7 +547,7 @@ You can also adjust the maximum number of files that are accepted in a drop operation, and the maximum path length in bytes if needed: - sapp_desc sokol_main() { + sapp_desc sokol_main(void) { return (sapp_desc) { .enable_dragndrop = true, // default is false .max_dropped_files = 8, // default is 1 @@ -687,7 +687,7 @@ For instance on a Retina Mac, returning the following sapp_desc struct from sokol_main(): - sapp_desc sokol_main() { + sapp_desc sokol_main(void) { return (sapp_desc) { .width = 640, .height = 480, @@ -941,13 +941,46 @@ sapp_show_keyboard(false); - Note that on the web platform, the keyboard can only be shown from - inside an input handler. On such platforms, sapp_show_keyboard() - will only work as expected when it is called from inside the - sokol-app event callback function. When called from other places, - an internal flag will be set, and the onscreen keyboard will be - called at the next 'legal' opportunity (when the next input event - is handled). + Note that onscreen keyboard functionality is no longer supported + on the browser platform (the previous hacks and workarounds to make browser + keyboards work for on web applications that don't use HTML UIs + never really worked across browsers). + + INPUT EVENT BUBBLING ON THE WEB PLATFORM + ======================================== + By default, input event bubbling on the web platform is configured in + a way that makes the most sense for 'full-canvas' apps that cover the + entire browser client window area: + + - mouse, touch and wheel events do not bubble up, this prevents various + ugly side events, like: + - HTML text overlays being selected on double- or triple-click into + the canvas + - 'scroll bumping' even when the canvas covers the entire client area + - key_up/down events for 'character keys' *do* bubble up (otherwise + the browser will not generate UNICODE character events) + - all other key events *do not* bubble up by default (this prevents side effects + like F1 opening help, or F7 starting 'caret browsing') + - character events do no bubble up (although I haven't noticed any side effects + otherwise) + + Event bubbling can be enabled for input event categories during initialization + in the sapp_desc struct: + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc){ + //... + .html5_bubble_mouse_events = true, + .html5_bubble_touch_events = true, + .html5_bubble_wheel_events = true, + .html5_bubble_key_events = true, + .html5_bubble_char_events = true, + }; + } + + This basically opens the floodgates lets *all* input events bubble up to the browser. + To prevent individual events from bubbling, call sapp_consume_event() from within + the sokol_app.h event callback. OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) ====================================================== @@ -1101,10 +1134,7 @@ TEMP NOTE DUMP ============== - - onscreen keyboard support on Android requires Java :(, should we even bother? - sapp_desc needs a bool whether to initialize depth-stencil surface - - GL context initialization needs more control (at least what GL version to initialize) - - application icon - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy at the latest but should do it earlier, in onStop, as an app is "killable" after onStop on Android Honeycomb and later (it can't be done at the moment as the app may be started @@ -1545,7 +1575,6 @@ typedef struct sapp_allocator { _SAPP_LOGITEM_XMACRO(LINUX_GLX_NO_SUITABLE_GLXFBCONFIG, "failed to find a suitable GLXFBConfig") \ _SAPP_LOGITEM_XMACRO(LINUX_GLX_GET_VISUAL_FROM_FBCONFIG_FAILED, "glXGetVisualFromFBConfig failed") \ _SAPP_LOGITEM_XMACRO(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING, "GLX extensions ARB_create_context and ARB_create_context_profile missing") \ - _SAPP_LOGITEM_XMACRO(LINUX_GLX_ES_REQUIRED_EXTENSIONS_MISSING, "GLX extension EXT_create_context_es_profile missing") \ _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_CONTEXT_FAILED, "Failed to create GL context via glXCreateContextAttribsARB") \ _SAPP_LOGITEM_XMACRO(LINUX_GLX_CREATE_WINDOW_FAILED, "glXCreateWindow() failed") \ _SAPP_LOGITEM_XMACRO(LINUX_X11_CREATE_WINDOW_FAILED, "XCreateWindow() failed") \ @@ -1593,7 +1622,7 @@ typedef struct sapp_allocator { _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_DONE, "NativeActivity done") \ _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_ONCREATE, "NativeActivity onCreate") \ _SAPP_LOGITEM_XMACRO(ANDROID_CREATE_THREAD_PIPE_FAILED, "failed to create thread pipe") \ - _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity sucessfully created") \ + _SAPP_LOGITEM_XMACRO(ANDROID_NATIVE_ACTIVITY_CREATE_SUCCESS, "NativeActivity successfully created") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SURFACE_FAILED, "wgpu: failed to create surface for swapchain") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_SWAPCHAIN_FAILED, "wgpu: failed to create swapchain object") \ _SAPP_LOGITEM_XMACRO(WGPU_SWAPCHAIN_CREATE_DEPTH_STENCIL_TEXTURE_FAILED, "wgpu: failed to create depth-stencil texture for swapchain") \ @@ -1677,6 +1706,11 @@ typedef struct sapp_desc { bool html5_preserve_drawing_buffer; // HTML5 only: whether to preserve default framebuffer content between frames bool html5_premultiplied_alpha; // HTML5 only: whether the rendered pixels use premultiplied alpha convention bool html5_ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) + bool html5_bubble_mouse_events; // if true, mouse events will bubble up to the web page + bool html5_bubble_touch_events; // same for touch events + bool html5_bubble_wheel_events; // same for wheel events + bool html5_bubble_key_events; // if true, bubble up *all* key events to browser, not just key events that represent characters + bool html5_bubble_char_events; // if true, bubble up character events to browser bool ios_keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas } sapp_desc; @@ -1776,7 +1810,7 @@ SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); SOKOL_APP_API_DECL void sapp_request_quit(void); /* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ SOKOL_APP_API_DECL void sapp_cancel_quit(void); -/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ +/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUESTED) */ SOKOL_APP_API_DECL void sapp_quit(void); /* call from inside event callback to consume the current event (don't forward to platform) */ SOKOL_APP_API_DECL void sapp_consume_event(void); @@ -1898,17 +1932,6 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #define _SAPP_PIXELFORMAT_DEPTH (43) #define _SAPP_PIXELFORMAT_DEPTH_STENCIL (44) -#if defined(_SAPP_MACOS) || defined(_SAPP_IOS) - // this is ARC compatible - #if defined(__cplusplus) - #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } - #else - #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } - #endif -#else - #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } -#endif - // check if the config defines are alright #if defined(__APPLE__) // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting @@ -1956,11 +1979,11 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #elif defined(__linux__) || defined(__unix__) /* Linux */ #define _SAPP_LINUX (1) - #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) + #if defined(SOKOL_GLCORE33) #if !defined(SOKOL_FORCE_EGL) #define _SAPP_GLX (1) #endif - #else + #elif !defined(SOKOL_GLES3) #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33, SOKOL_GLES3") #endif #else @@ -2101,6 +2124,18 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } #include #endif +#if defined(_SAPP_APPLE) + // this is ARC compatible + #if defined(__cplusplus) + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = type(); } + #else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SAPP_CLEAR_ARC_STRUCT(type, item) { _sapp_clear(&item, sizeof(item)); } +#endif + + // ███████ ██████ █████ ███ ███ ███████ ████████ ██ ███ ███ ██ ███ ██ ██████ // ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ████ ████ ██ ████ ██ ██ // █████ ██████ ███████ ██ ████ ██ █████ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ███ @@ -2607,7 +2642,6 @@ typedef struct { #define GLX_SAMPLES 0x186a1 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define GLX_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 @@ -2725,8 +2759,6 @@ typedef struct { bool ARB_multisample; bool ARB_create_context; bool ARB_create_context_profile; - bool EXT_create_context_es_profile; - bool EXT_create_context_es2_profile; } _sapp_glx_t; #else @@ -3882,6 +3914,20 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { [_sapp.macos.window makeKeyAndOrderFront:nil]; _sapp_macos_update_dimensions(); [NSEvent setMouseCoalescingEnabled:NO]; + + // workaround for window not being focused during a long init callback + // for details see: https://github.com/floooh/sokol/pull/982 + // also see: https://gitlab.gnome.org/GNOME/gtk/-/issues/2342 + NSEvent *focusevent = [NSEvent otherEventWithType:NSEventTypeAppKitDefined + location:NSZeroPoint + modifierFlags:0x40 + timestamp:0 + windowNumber:0 + context:nil + subtype:NSEventSubtypeApplicationActivated + data1:0 + data2:0]; + [NSApp postEvent:focusevent atStart:YES]; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { @@ -4050,7 +4096,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) { } #endif -_SOKOL_PRIVATE void _sapp_macos_poll_input_events() { +_SOKOL_PRIVATE void _sapp_macos_poll_input_events(void) { /* NOTE: late event polling temporarily out-commented to check if this @@ -5118,7 +5164,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(user_data); - bool consume_event = false; + bool consume_event = !_sapp.desc.html5_bubble_mouse_events; _sapp.emsc.mouse_buttons = emsc_event->buttons; if (_sapp.mouse.locked) { _sapp.mouse.dx = (float) emsc_event->movementX; @@ -5179,7 +5225,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE } else { _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; } - consume_event = _sapp_call_event(&_sapp.event); + consume_event |= _sapp_call_event(&_sapp.event); } // mouse lock can only be activated in mouse button events (not in move, enter or leave) if (is_button_event) { @@ -5192,6 +5238,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(emsc_type); _SOKOL_UNUSED(user_data); + bool consume_event = !_sapp.desc.html5_bubble_wheel_events; _sapp.emsc.mouse_buttons = emsc_event->mouse.buttons; if (_sapp_events_enabled()) { _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); @@ -5206,12 +5253,10 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelE } _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; - _sapp_call_event(&_sapp.event); + consume_event |= _sapp_call_event(&_sapp.event); } _sapp_emsc_update_mouse_lock_state(); - // NOTE: wheel events are always consumed because they try to scroll the - // page which looks pretty bad - return true; + return consume_event; } static struct { @@ -5335,6 +5380,12 @@ _SOKOL_PRIVATE sapp_keycode _sapp_emsc_translate_key(const char* str) { return SAPP_KEYCODE_INVALID; } +// returns true if the key code is a 'character key', this is used to decide +// if a key event needs to bubble up to create a char event +_SOKOL_PRIVATE bool _sapp_emsc_is_char_key(sapp_keycode key_code) { + return key_code < SAPP_KEYCODE_WORLD_1; +} + _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(user_data); bool consume_event = false; @@ -5362,6 +5413,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard if (type == SAPP_EVENTTYPE_CHAR) { // NOTE: charCode doesn't appear to be supported on Android Chrome _sapp.event.char_code = emsc_event->charCode; + consume_event |= !_sapp.desc.html5_bubble_char_events; } else { if (0 != emsc_event->code[0]) { // This code path is for desktop browsers which send untranslated 'physical' key code strings @@ -5386,83 +5438,17 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard { send_keyup_followup = true; } - // Only forward alpha-numeric keys to the browser (can further be suppressed by sapp_consume_event()) - // NOTE: it should be possible to disable this behaviour via sapp_desc to give apps more - // controls over input event bubbling. - switch (_sapp.event.key_code) { - case SAPP_KEYCODE_WORLD_1: - case SAPP_KEYCODE_WORLD_2: - case SAPP_KEYCODE_ESCAPE: - case SAPP_KEYCODE_ENTER: - case SAPP_KEYCODE_TAB: - case SAPP_KEYCODE_BACKSPACE: - case SAPP_KEYCODE_INSERT: - case SAPP_KEYCODE_DELETE: - case SAPP_KEYCODE_RIGHT: - case SAPP_KEYCODE_LEFT: - case SAPP_KEYCODE_DOWN: - case SAPP_KEYCODE_UP: - case SAPP_KEYCODE_PAGE_UP: - case SAPP_KEYCODE_PAGE_DOWN: - case SAPP_KEYCODE_HOME: - case SAPP_KEYCODE_END: - case SAPP_KEYCODE_CAPS_LOCK: - case SAPP_KEYCODE_SCROLL_LOCK: - case SAPP_KEYCODE_NUM_LOCK: - case SAPP_KEYCODE_PRINT_SCREEN: - case SAPP_KEYCODE_PAUSE: - case SAPP_KEYCODE_F1: - case SAPP_KEYCODE_F2: - case SAPP_KEYCODE_F3: - case SAPP_KEYCODE_F4: - case SAPP_KEYCODE_F5: - case SAPP_KEYCODE_F6: - case SAPP_KEYCODE_F7: - case SAPP_KEYCODE_F8: - case SAPP_KEYCODE_F9: - case SAPP_KEYCODE_F10: - case SAPP_KEYCODE_F11: - case SAPP_KEYCODE_F12: - case SAPP_KEYCODE_F13: - case SAPP_KEYCODE_F14: - case SAPP_KEYCODE_F15: - case SAPP_KEYCODE_F16: - case SAPP_KEYCODE_F17: - case SAPP_KEYCODE_F18: - case SAPP_KEYCODE_F19: - case SAPP_KEYCODE_F20: - case SAPP_KEYCODE_F21: - case SAPP_KEYCODE_F22: - case SAPP_KEYCODE_F23: - case SAPP_KEYCODE_F24: - case SAPP_KEYCODE_F25: - case SAPP_KEYCODE_LEFT_SHIFT: - case SAPP_KEYCODE_LEFT_CONTROL: - case SAPP_KEYCODE_LEFT_ALT: - case SAPP_KEYCODE_LEFT_SUPER: - case SAPP_KEYCODE_RIGHT_SHIFT: - case SAPP_KEYCODE_RIGHT_CONTROL: - case SAPP_KEYCODE_RIGHT_ALT: - case SAPP_KEYCODE_RIGHT_SUPER: - case SAPP_KEYCODE_MENU: - // consume the event - consume_event = true; - break; - default: - // forward key to browser - consume_event = false; - break; + + // 'character key events' will always need to bubble up, otherwise the browser + // wouldn't be able to generate character events. + if (!_sapp_emsc_is_char_key(_sapp.event.key_code)) { + consume_event |= !_sapp.desc.html5_bubble_key_events; } } - if (_sapp_call_event(&_sapp.event)) { - // event was consumed via sapp_consume_event() - consume_event = true; - } + consume_event |= _sapp_call_event(&_sapp.event); if (send_keyup_followup) { _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; - if (_sapp_call_event(&_sapp.event)) { - consume_event = true; - } + consume_event |= _sapp_call_event(&_sapp.event); } } } @@ -5472,7 +5458,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { _SOKOL_UNUSED(user_data); - bool consume_event = false; + bool consume_event = !_sapp.desc.html5_bubble_touch_events; if (_sapp_events_enabled()) { sapp_event_type type; switch (emsc_type) { @@ -5507,7 +5493,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchE dst->pos_y = src->targetY * _sapp.dpi_scale; dst->changed = src->isChanged; } - consume_event = _sapp_call_event(&_sapp.event); + consume_event |= _sapp_call_event(&_sapp.event); } } return consume_event; @@ -5726,7 +5712,7 @@ _SOKOL_PRIVATE void _sapp_emsc_wgpu_request_adapter_cb(WGPURequestAdapterStatus WGPUDeviceDescriptor dev_desc; _sapp_clear(&dev_desc, sizeof(dev_desc)); - dev_desc.requiredFeaturesCount = cur_feature_index; + dev_desc.requiredFeatureCount = cur_feature_index; dev_desc.requiredFeatures = requiredFeatures, wgpuAdapterRequestDevice(adapter, &dev_desc, _sapp_emsc_wgpu_request_device_cb, 0); } @@ -5786,7 +5772,7 @@ _SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { #endif } -_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { +_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers(void) { emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); @@ -5804,6 +5790,9 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); emscripten_set_focus_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); emscripten_set_blur_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + if (!_sapp.desc.html5_canvas_resize) { + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + } sapp_js_remove_beforeunload_listener(); if (_sapp.clipboard.enabled) { sapp_js_remove_clipboard_listener(); @@ -5896,7 +5885,7 @@ int main(int argc, char* argv[]) { // ██████ ███████ ██ ██ ███████ ███████ ██ ███████ ██ ██ ███████ // // >>gl helpers -#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES3) +#if defined(SOKOL_GLCORE33) typedef struct { int red_bits; int green_bits; @@ -8253,8 +8242,8 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { for (int32_t i = 0; i < _sapp.event.num_touches; i++) { sapp_touchpoint* dst = &_sapp.event.touches[i]; dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); - dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; - dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; + dst->pos_x = (AMotionEvent_getX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; + dst->pos_y = (AMotionEvent_getY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; dst->android_tooltype = (sapp_android_tooltype) AMotionEvent_getToolType(e, (size_t)i); if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -9644,7 +9633,7 @@ _SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) } } -_SOKOL_PRIVATE void _sapp_glx_init() { +_SOKOL_PRIVATE void _sapp_glx_init(void) { const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; for (int i = 0; sonames[i]; i++) { _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); @@ -9711,8 +9700,6 @@ _SOKOL_PRIVATE void _sapp_glx_init() { _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; } _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); - _sapp.glx.EXT_create_context_es_profile = _sapp_glx_extsupported("GLX_EXT_create_context_es_profile", exts); - _sapp.glx.EXT_create_context_es2_profile = _sapp_glx_extsupported("GLX_EXT_create_context_es2_profile", exts); } _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { @@ -9721,7 +9708,7 @@ _SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { return value; } -_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig(void) { GLXFBConfig* native_configs; _sapp_gl_fbconfig* usable_configs; const _sapp_gl_fbconfig* closest; @@ -9816,22 +9803,12 @@ _SOKOL_PRIVATE void _sapp_glx_create_context(void) { if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { _SAPP_PANIC(LINUX_GLX_REQUIRED_EXTENSIONS_MISSING); } -#if defined(SOKOL_GLES3) - // At least one of extension strings must be exported - if (!(_sapp.glx.EXT_create_context_es_profile || _sapp.glx.EXT_create_context_es2_profile)) { - _SAPP_PANIC(LINUX_GLX_ES_REQUIRED_EXTENSIONS_MISSING); - } -#endif _sapp_x11_grab_error_handler(); const int attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, _sapp.desc.gl_major_version, GLX_CONTEXT_MINOR_VERSION_ARB, _sapp.desc.gl_minor_version, - #if defined(SOKOL_GLES3) - GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT, - #else GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, - #endif 0, 0 }; _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); -- cgit v1.2.3