From 99fb0374e9352ebb61e7eea134784bd26f61a892 Mon Sep 17 00:00:00 2001 From: spl3g Date: Fri, 10 Oct 2025 22:19:33 +0300 Subject: Add mouse interactions --- src/main.c | 69 ++++++++++++--------- src/sounds.c | 3 +- src/ui.c | 199 ++++++++++++++++++++++++++++++++++++++++++----------------- src/ui.h | 23 ++++++- 4 files changed, 205 insertions(+), 89 deletions(-) diff --git a/src/main.c b/src/main.c index bd9d5e1..e44dd7d 100644 --- a/src/main.c +++ b/src/main.c @@ -9,28 +9,27 @@ #include #include "ui.h" +#include "sounds.h" #define CLAY_IMPLEMENTATION #include "clay/clay.h" #include "clay/renderers/clay_renderer_SDL3.c" -#include "sounds.h" +#define ARENA_IMPLEMENTATION +#include "arena.h" static const int SCREEN_FPS = 60; static const int SCREEN_TICKS_PER_FRAME = 1000 / SCREEN_FPS; static const int FONT_ID = 0; -typedef struct { - char key; - float freq; -} freq_map; - typedef struct { SDL_Window *window; Clay_SDL3RendererData renderer_data; + Arena frame_arena; - bool last_keys[12]; + KeyState *keys; + size_t keys_amount; snd_pcm_t *sound_device; message_queue msg_queue; @@ -78,7 +77,7 @@ int init_sounds(app_state *state) { .param_change = { .param_type = PARAM_VOLUME, - .value = 1, + .value = 0.2, }, }, { @@ -168,7 +167,6 @@ int init_ui(app_state *state) { state->renderer_data.fonts[FONT_ID] = font; - size_t totalMemorySize = Clay_MinMemorySize(); Clay_Arena clayMemory = (Clay_Arena) { .memory = SDL_malloc(totalMemorySize), @@ -177,18 +175,31 @@ int init_ui(app_state *state) { int width, height; SDL_GetWindowSize(state->window, &width, &height); - Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors }); + Clay_Initialize(clayMemory, (Clay_Dimensions) { (float) width, (float) height }, (Clay_ErrorHandler) { HandleClayErrors, 0}); Clay_SetMeasureTextFunction(SDL_MeasureText, state->renderer_data.fonts); return 0; } +KeyState keys[12] = { + {'Z', SDL_SCANCODE_Z, 0, 0}, {'S', SDL_SCANCODE_S, 0, 0}, + {'X', SDL_SCANCODE_X, 0, 0}, {'D', SDL_SCANCODE_D, 0, 0}, + {'C', SDL_SCANCODE_C, 0, 0}, + {'V', SDL_SCANCODE_V, 0, 0}, {'G', SDL_SCANCODE_G, 0, 0}, + {'B', SDL_SCANCODE_B, 0, 0}, {'H', SDL_SCANCODE_H, 0, 0}, + {'N', SDL_SCANCODE_N, 0, 0}, {'J', SDL_SCANCODE_J, 0, 0}, + {'M', SDL_SCANCODE_M, 0, 0}, +}; + SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { (void) argc; (void) argv; - + app_state *state = malloc(sizeof(app_state)); + memset(state, 0, sizeof(app_state)); *appstate = state; + state->keys = keys; + state->keys_amount = 12; if (init_ui(state) != 0) { printf("Couldn't initialize UI: %s", SDL_GetError()); @@ -204,12 +215,19 @@ SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { SDL_AppResult SDL_AppIterate(void *appstate) { app_state *state = appstate; + Arena *arena = &state->frame_arena; + arena_reset(arena); int start_tick = SDL_GetTicks(); + UIData *ui_data = arena_alloc(arena, sizeof(UIData)); + ui_data->msg_queue = &state->msg_queue; + ui_data->keys = state->keys; + ui_data->keys_amount = 12; + Clay_BeginLayout(); - draw_ui(state->last_keys, 12); + draw_ui(ui_data); Clay_RenderCommandArray render_commands = Clay_EndLayout(); @@ -230,16 +248,6 @@ SDL_AppResult SDL_AppIterate(void *appstate) { return SDL_APP_CONTINUE; } -SDL_Keycode keys[12] = { - SDL_SCANCODE_Z, SDL_SCANCODE_S, - SDL_SCANCODE_X, SDL_SCANCODE_D, - SDL_SCANCODE_C, - SDL_SCANCODE_V, SDL_SCANCODE_G, - SDL_SCANCODE_B, SDL_SCANCODE_H, - SDL_SCANCODE_N, SDL_SCANCODE_J, - SDL_SCANCODE_M, -}; - SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { app_state *state = appstate; @@ -249,11 +257,11 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { } case SDL_EVENT_KEY_DOWN: { for (int i = 0; i < 12; i++) { - if (event->key.scancode == keys[i]) { - if (state->last_keys[i]) { + if (event->key.scancode == state->keys[i].keycode) { + if (state->keys[i].keyboard_pressed) { break; } - state->last_keys[i] = true; + state->keys[i].keyboard_pressed = true; mqueue_push(&state->msg_queue, (synth_message){ .type = MSG_NOTE_ON, @@ -266,11 +274,11 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { case SDL_EVENT_KEY_UP: { for (int i = 0; i < 12; i++) { - if (event->key.scancode == keys[i]) { - if (!state->last_keys[i]) { + if (event->key.scancode == state->keys[i].keycode) { + if (!state->keys[i].keyboard_pressed) { break; } - state->last_keys[i] = false; + state->keys[i].keyboard_pressed = false; mqueue_push(&state->msg_queue, (synth_message){ .type = MSG_NOTE_OFF, .note = { .note_id = i }, @@ -293,6 +301,11 @@ SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) { Clay_SetPointerState((Clay_Vector2) { event->button.x, event->button.y }, event->button.button == SDL_BUTTON_LEFT); break; + case SDL_EVENT_MOUSE_BUTTON_UP: + if (event->button.button == SDL_BUTTON_LEFT) { + Clay_SetPointerState((Clay_Vector2) { event->button.x, event->button.y }, false); + } + break; case SDL_EVENT_MOUSE_WHEEL: Clay_UpdateScrollContainers(true, (Clay_Vector2) { event->wheel.x, event->wheel.y }, 0.01f); diff --git a/src/sounds.c b/src/sounds.c index 0086f4f..9c6711c 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -188,7 +188,7 @@ void post_process(synth_params *params, float *scratch_buffer, void prepare_output(float *scratch_buffer, short *output_buffer, size_t buffer_size) { for (size_t i = 0; i < buffer_size; i++) { - output_buffer[i] = scratch_buffer[i] * 0.2f * 32767; + output_buffer[i] = scratch_buffer[i] * 32767; } } @@ -218,7 +218,6 @@ void sound_loop_start(snd_pcm_t *pcm, message_queue *queue, case MSG_PARAM_CHANGE: { param_type type = msg.param_change.param_type; float value = msg.param_change.value; - printf("%d %f\n", type, value); set_param(params, type, value); break; } diff --git a/src/ui.c b/src/ui.c index 7c90099..12888e5 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,97 +1,180 @@ #include "ui.h" -void draw_white_key(size_t idx, bool pressed) { - Clay_Color fill_color; - Clay_Color border_color; - - if (pressed) { - fill_color = COLOR_BG; - border_color = COLOR_FG; - } else { - fill_color = COLOR_FG; - border_color = COLOR_FG; +void handle_key_press(Clay_ElementId element_id, Clay_PointerData pointer_info, intptr_t user_data) { + UIData *ui_data = (UIData *)user_data; + int idx = element_id.offset; + + bool pressed = ui_data->keys[idx].mouse_pressed; + + if (!pressed && (pointer_info.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME + || pointer_info.state == CLAY_POINTER_DATA_PRESSED)) { + mqueue_push(ui_data->msg_queue, (synth_message){ + .type = MSG_NOTE_ON, + .note = { + .note_id = idx, + }, + }); + ui_data->keys[idx].mouse_pressed = true; + } + + if (pressed && (pointer_info.state == CLAY_POINTER_DATA_RELEASED_THIS_FRAME + || pointer_info.state == CLAY_POINTER_DATA_RELEASED)) { + if (!ui_data->keys[idx].keyboard_pressed) { + mqueue_push(ui_data->msg_queue, (synth_message){ + .type = MSG_NOTE_OFF, + .note = { + .note_id = idx, + }, + }); + } + ui_data->keys[idx].mouse_pressed = false; } - CLAY(CLAY_IDI("white_key", idx), { - .layout = { - .sizing = {CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(100)}, - }, - .backgroundColor = fill_color, - .border = { .width = {1, 1, 1, 1, 0}, .color = border_color}, - }); } -void draw_black_key(size_t idx, bool pressed) { - Clay_Color fill_color; - Clay_Color border_color; +void draw_white_key(size_t idx, UIData *ui_data) { + CLAY(CLAY_IDI("key_container", idx)) { + bool mouse_pressed = ui_data->keys[idx].mouse_pressed; + bool keyboard_pressed = ui_data->keys[idx].keyboard_pressed; + bool hovered = Clay_Hovered(); - if (pressed) { - fill_color = COLOR_FG; - border_color = COLOR_BG; - } else { - fill_color = COLOR_BG; - border_color = COLOR_FG; - } - CLAY(CLAY_IDI("black_key", idx), { - .layout = { - .sizing = {CLAY_SIZING_FIXED(25), .height = CLAY_SIZING_FIXED(65)}, - }, - - .floating = { - .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, - .parentId = CLAY_IDI("white_key", idx - 1).id, - .attachPoints = { - .element = CLAY_ATTACH_POINT_CENTER_TOP, - .parent = CLAY_ATTACH_POINT_RIGHT_TOP, + if (!hovered && mouse_pressed && !keyboard_pressed) { + mqueue_push(ui_data->msg_queue, (synth_message){ + .type = MSG_NOTE_OFF, + .note = { + .note_id = idx, + }, + }); + + mouse_pressed = false; + ui_data->keys[idx].mouse_pressed = false; + } + + Clay_OnHover(handle_key_press, (intptr_t)ui_data); + + + Clay_Color fill_color; + Clay_Color border_color; + + if (mouse_pressed || keyboard_pressed) { + fill_color = COLOR_BG; + border_color = COLOR_FG; + } else if (hovered) { + fill_color = COLOR_FG_INTER; + border_color = COLOR_FG_INTER; + } else { + fill_color = COLOR_FG; + border_color = COLOR_FG; + } + CLAY(CLAY_IDI("white_key", idx), { + .layout = { + .sizing = {CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(100)}, }, - .offset = { - .y = -1, + .backgroundColor = fill_color, + .border = { .width = {1, 1, 1, 1, 0}, .color = border_color}, + }); + } +} + +void draw_black_key(size_t idx, UIData *ui_data) { + CLAY(CLAY_IDI("key_container", idx), { + .floating = { + .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, + .parentId = CLAY_IDI("white_key", idx - 1).id, + .attachPoints = { + .element = CLAY_ATTACH_POINT_CENTER_TOP, + .parent = CLAY_ATTACH_POINT_RIGHT_TOP, + }, + .offset = { + .y = -1, + }, }, - }, + }) { + bool mouse_pressed = ui_data->keys[idx].mouse_pressed; + bool keyboard_pressed = ui_data->keys[idx].keyboard_pressed; + bool hovered = Clay_Hovered(); + + if (!hovered && mouse_pressed && !keyboard_pressed) { + mqueue_push(ui_data->msg_queue, (synth_message){ + .type = MSG_NOTE_OFF, + .note = { + .note_id = idx, + }, + }); + + mouse_pressed = false; + ui_data->keys[idx].mouse_pressed = false; + } - .backgroundColor = fill_color, - .border = { .width = {1, 1, 0, 1, 0}, .color = border_color}, - }); + Clay_OnHover(handle_key_press, (intptr_t)ui_data); + + Clay_Color fill_color; + Clay_Color border_color; + + if (keyboard_pressed || mouse_pressed) { + fill_color = COLOR_FG; + border_color = COLOR_BG; + } else if (hovered) { + fill_color = COLOR_BG_INTER; + border_color = COLOR_FG; + } else { + fill_color = COLOR_BG; + border_color = COLOR_FG; + } + CLAY(CLAY_IDI("black_key", idx), { + .layout = { + .sizing = {CLAY_SIZING_FIXED(25), .height = CLAY_SIZING_FIXED(65)}, + }, + + .backgroundColor = fill_color, + .border = { .width = {1, 1, 0, 1, 0}, .color = border_color}, + }); + } } -void draw_keyboard(bool *pressed_keys, size_t keys_amount) { +void draw_keyboard(UIData *ui_data) { CLAY(CLAY_ID("keyboard"), { .layout = { .layoutDirection = CLAY_LEFT_TO_RIGHT, }, }) { - for (size_t i = 0; i < keys_amount; i++) { - bool pressed = pressed_keys[i]; + for (size_t i = 0; i < ui_data->keys_amount; i++) { size_t key_idx = i % 12; if (key_idx <= 4) { if (i % 2 == 0) { - draw_white_key(i, pressed); + draw_white_key(i, ui_data); } else { - draw_black_key(i, pressed); + draw_black_key(i, ui_data); } } else { if (i % 2 == 0) { - draw_black_key(i, pressed); + draw_black_key(i, ui_data); } else { - draw_white_key(i, pressed); + draw_white_key(i, ui_data); } } } } } -void draw_ui(bool *pressed_keys, size_t keys_amount) { - CLAY(CLAY_ID("OuterContainer"), { +void draw_screen() {} +void draw_nob() {} + +void draw_ui(UIData *ui_data) { + CLAY(CLAY_ID("outer_container"), { .layout = { .sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, .padding = CLAY_PADDING_ALL(16), .childGap = 16, - .childAlignment = { - .x = CLAY_ALIGN_X_CENTER, - .y = CLAY_ALIGN_Y_CENTER, - }, + .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, }, .backgroundColor = COLOR_BG, }) { - draw_keyboard(pressed_keys, keys_amount); + /* CLAY(CLAY_ID("app_container"), { */ + /* .layout = { */ + /* .sizing = {CLAY_SIZING_PERCENT(0.75), CLAY_SIZING_PERCENT(0.75)} */ + /* }, */ + /* }) { */ + draw_keyboard(ui_data); + /* }; */ }; } diff --git a/src/ui.h b/src/ui.h index f190ad2..9651c41 100644 --- a/src/ui.h +++ b/src/ui.h @@ -3,11 +3,32 @@ #include #include +#include + +#include +#include + #include "clay/clay.h" +#include "messages.h" static const Clay_Color COLOR_BG = (Clay_Color){45, 53, 59, 255}; +static const Clay_Color COLOR_BG_INTER = (Clay_Color){52, 63, 68, 255}; static const Clay_Color COLOR_FG = (Clay_Color){211, 198, 170, 255}; +static const Clay_Color COLOR_FG_INTER = (Clay_Color){227, 212, 181, 255}; + +typedef struct { + char letter; + SDL_Keycode keycode; + bool mouse_pressed; + bool keyboard_pressed; +} KeyState; + +typedef struct { + message_queue *msg_queue; + KeyState *keys; + size_t keys_amount; +} UIData; -void draw_ui(bool *pressed_keys, size_t keys_amount); +void draw_ui(UIData *ui_data); #endif // UI_H_ -- cgit v1.2.3