type alias refactor

This commit is contained in:
2026-03-04 16:26:17 -05:00
parent 75ac2883f7
commit fb358e3c4b
19 changed files with 614 additions and 603 deletions

View File

@@ -1,24 +1,23 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "base/base_core.h"
struct AudioEngine;
struct AudioDeviceInfo {
char name[128];
int32_t id; // index into engine's device list
S32 id; // index into engine's device list
};
AudioEngine *audio_create(void *hwnd);
void audio_destroy(AudioEngine *engine);
void audio_refresh_devices(AudioEngine *engine);
int32_t audio_get_device_count(AudioEngine *engine);
AudioDeviceInfo*audio_get_device(AudioEngine *engine, int32_t index);
S32 audio_get_device_count(AudioEngine *engine);
AudioDeviceInfo*audio_get_device(AudioEngine *engine, S32 index);
bool audio_open_device(AudioEngine *engine, int32_t index);
B32 audio_open_device(AudioEngine *engine, S32 index);
void audio_close_device(AudioEngine *engine);
void audio_play_test_tone(AudioEngine *engine);
bool audio_is_test_tone_playing(AudioEngine *engine);
void audio_update(AudioEngine *engine, float dt);
B32 audio_is_test_tone_playing(AudioEngine *engine);
void audio_update(AudioEngine *engine, F32 dt);

View File

@@ -74,21 +74,21 @@ struct ASIOChannelInfo {
struct ASIOBufferInfo {
ASIOBool isInput;
long channelNum;
void *buffers[2]; // double buffer
void *buffers[2]; // F64 buffer
};
struct ASIOTimeCode {
double speed;
F64 speed;
ASIOSamples timeCodeSamples;
unsigned long flags;
char future[64];
};
struct AsioTimeInfo {
double speed;
F64 speed;
ASIOTimeStamp systemTime;
ASIOSamples samplePosition;
double sampleRate;
F64 sampleRate;
unsigned long flags;
char reserved[12];
};
@@ -101,8 +101,8 @@ struct ASIOTime {
struct ASIOCallbacks {
void (*bufferSwitch)(long doubleBufferIndex, ASIOBool directProcess);
void (*sampleRateDidChange)(double sRate);
long (*asioMessage)(long selector, long value, void *message, double *opt);
void (*sampleRateDidChange)(F64 sRate);
long (*asioMessage)(long selector, long value, void *message, F64 *opt);
ASIOTime *(*bufferSwitchTimeInfo)(ASIOTime *params, long doubleBufferIndex, ASIOBool directProcess);
};
@@ -134,9 +134,9 @@ public:
virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;
virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;
virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0;
virtual ASIOError canSampleRate(double sampleRate) = 0;
virtual ASIOError getSampleRate(double *sampleRate) = 0;
virtual ASIOError setSampleRate(double sampleRate) = 0;
virtual ASIOError canSampleRate(F64 sampleRate) = 0;
virtual ASIOError getSampleRate(F64 *sampleRate) = 0;
virtual ASIOError setSampleRate(F64 sampleRate) = 0;
virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;
virtual ASIOError setClockSource(long reference) = 0;
virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
@@ -162,25 +162,25 @@ struct AudioEngine {
// Device enumeration
AudioDeviceInfo devices[AUDIO_MAX_DEVICES];
AsioDriverInfo drivers[AUDIO_MAX_DEVICES];
int32_t device_count;
S32 device_count;
// Active driver
IASIO *driver;
int32_t active_device_index; // -1 = none
S32 active_device_index; // -1 = none
// Buffer state
ASIOBufferInfo buffer_infos[AUDIO_MAX_CHANNELS];
long num_output_channels;
long num_input_channels;
long buffer_size;
double sample_rate;
F64 sample_rate;
ASIOSampleType output_sample_type;
ASIOCallbacks callbacks;
// Test tone state (accessed from callback thread)
volatile LONG test_tone_active;
volatile LONG test_tone_samples_remaining;
double test_tone_phase; // written only from callback thread
F64 test_tone_phase; // written only from callback thread
};
////////////////////////////////
@@ -191,29 +191,29 @@ static AudioEngine *g_audio_engine = nullptr;
////////////////////////////////
// Sample writing helper
static void write_sample(void *dest, ASIOSampleType type, double value) {
static void write_sample(void *dest, ASIOSampleType type, F64 value) {
// Clamp to [-1, 1]
if (value > 1.0) value = 1.0;
if (value < -1.0) value = -1.0;
switch (type) {
case ASIOSTInt16LSB: {
int16_t s = (int16_t)(value * 32767.0);
S16 s = (S16)(value * 32767.0);
memcpy(dest, &s, 2);
} break;
case ASIOSTInt24LSB: {
int32_t s = (int32_t)(value * 8388607.0);
uint8_t *d = (uint8_t *)dest;
d[0] = (uint8_t)(s & 0xFF);
d[1] = (uint8_t)((s >> 8) & 0xFF);
d[2] = (uint8_t)((s >> 16) & 0xFF);
S32 s = (S32)(value * 8388607.0);
U8 *d = (U8 *)dest;
d[0] = (U8)(s & 0xFF);
d[1] = (U8)((s >> 8) & 0xFF);
d[2] = (U8)((s >> 16) & 0xFF);
} break;
case ASIOSTInt32LSB: {
int32_t s = (int32_t)(value * 2147483647.0);
S32 s = (S32)(value * 2147483647.0);
memcpy(dest, &s, 4);
} break;
case ASIOSTFloat32LSB: {
float f = (float)value;
F32 f = (F32)value;
memcpy(dest, &f, 4);
} break;
case ASIOSTFloat64LSB: {
@@ -221,7 +221,7 @@ static void write_sample(void *dest, ASIOSampleType type, double value) {
} break;
default: {
// Unsupported type — write silence (4 bytes of zero)
uint32_t z = 0;
U32 z = 0;
memcpy(dest, &z, 4);
} break;
}
@@ -249,7 +249,7 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) {
long buf_size = engine->buffer_size;
ASIOSampleType type = engine->output_sample_type;
int bytes_per_sample = sample_type_size(type);
S32 bytes_per_sample = sample_type_size(type);
LONG tone_active = InterlockedCompareExchange(&engine->test_tone_active, 0, 0);
@@ -266,13 +266,13 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) {
}
if (tone_active) {
double phase = engine->test_tone_phase;
double phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate;
F64 phase = engine->test_tone_phase;
F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate;
LONG remaining = InterlockedCompareExchange(&engine->test_tone_samples_remaining, 0, 0);
long samples_to_gen = (remaining < buf_size) ? (long)remaining : buf_size;
for (long s = 0; s < buf_size; s++) {
double sample_val = 0.0;
F64 sample_val = 0.0;
if (s < samples_to_gen) {
sample_val = sin(phase) * 0.5; // -6dB to avoid clipping
phase += phase_inc;
@@ -283,7 +283,7 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) {
for (long ch = 0; ch < engine->num_output_channels; ch++) {
void *buf = engine->buffer_infos[ch].buffers[doubleBufferIndex];
if (!buf) continue;
uint8_t *dest = (uint8_t *)buf + (size_t)(s * bytes_per_sample);
U8 *dest = (U8 *)buf + (size_t)(s * bytes_per_sample);
write_sample(dest, type, sample_val);
}
}
@@ -298,14 +298,14 @@ static void asio_buffer_switch(long doubleBufferIndex, ASIOBool directProcess) {
}
}
static void asio_sample_rate_changed(double sRate) {
static void asio_sample_rate_changed(F64 sRate) {
AudioEngine *engine = g_audio_engine;
if (engine) {
engine->sample_rate = sRate;
}
}
static long asio_message(long selector, long value, void *message, double *opt) {
static long asio_message(long selector, long value, void *message, F64 *opt) {
(void)value;
(void)message;
(void)opt;
@@ -377,7 +377,7 @@ static void enumerate_asio_drivers(AudioEngine *engine) {
continue;
}
int32_t idx = engine->device_count++;
S32 idx = engine->device_count++;
strncpy_s(engine->devices[idx].name, sizeof(engine->devices[idx].name), subkey_name, _TRUNCATE);
engine->devices[idx].id = idx;
engine->drivers[idx].clsid = clsid;
@@ -415,17 +415,17 @@ void audio_refresh_devices(AudioEngine *engine) {
enumerate_asio_drivers(engine);
}
int32_t audio_get_device_count(AudioEngine *engine) {
S32 audio_get_device_count(AudioEngine *engine) {
return engine->device_count;
}
AudioDeviceInfo *audio_get_device(AudioEngine *engine, int32_t index) {
AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index) {
if (index < 0 || index >= engine->device_count)
return nullptr;
return &engine->devices[index];
}
bool audio_open_device(AudioEngine *engine, int32_t index) {
B32 audio_open_device(AudioEngine *engine, S32 index) {
// Close any existing device first
audio_close_device(engine);
@@ -462,7 +462,7 @@ bool audio_open_device(AudioEngine *engine, int32_t index) {
}
// Query sample rate
double sample_rate = 0;
F64 sample_rate = 0;
if (driver->getSampleRate(&sample_rate) != ASE_OK || sample_rate <= 0) {
// Try setting a common rate
if (driver->setSampleRate(44100.0) == ASE_OK) {
@@ -556,11 +556,11 @@ void audio_play_test_tone(AudioEngine *engine) {
InterlockedExchange(&engine->test_tone_active, 1);
}
bool audio_is_test_tone_playing(AudioEngine *engine) {
B32 audio_is_test_tone_playing(AudioEngine *engine) {
return InterlockedCompareExchange(&engine->test_tone_active, 0, 0) != 0;
}
void audio_update(AudioEngine *engine, float dt) {
void audio_update(AudioEngine *engine, F32 dt) {
(void)engine;
(void)dt;
// Currently no per-frame work needed on the main thread.

View File

@@ -20,18 +20,18 @@ struct CoreAudioDeviceInfo {
struct AudioEngine {
AudioDeviceInfo devices[AUDIO_MAX_DEVICES];
CoreAudioDeviceInfo ca_devices[AUDIO_MAX_DEVICES];
int32_t device_count;
S32 device_count;
AUGraph graph;
AudioUnit output_unit;
int32_t active_device_index;
double sample_rate;
int32_t num_channels;
S32 active_device_index;
F64 sample_rate;
S32 num_channels;
// Test tone state (accessed from audio render thread)
_Atomic int32_t test_tone_active;
_Atomic int32_t test_tone_samples_remaining;
double test_tone_phase;
_Atomic S32 test_tone_active;
_Atomic S32 test_tone_samples_remaining;
F64 test_tone_phase;
};
////////////////////////////////
@@ -55,7 +55,7 @@ static OSStatus audio_render_callback(void *inRefCon,
return noErr;
}
int32_t tone_active = atomic_load(&engine->test_tone_active);
S32 tone_active = atomic_load(&engine->test_tone_active);
if (!tone_active) {
for (UInt32 buf = 0; buf < ioData->mNumberBuffers; buf++)
@@ -63,18 +63,18 @@ static OSStatus audio_render_callback(void *inRefCon,
return noErr;
}
double phase = engine->test_tone_phase;
double phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate;
int32_t remaining = atomic_load(&engine->test_tone_samples_remaining);
int32_t samples_to_gen = (remaining < (int32_t)inNumberFrames) ? remaining : (int32_t)inNumberFrames;
F64 phase = engine->test_tone_phase;
F64 phase_inc = 2.0 * AUDIO_PI * AUDIO_TEST_TONE_HZ / engine->sample_rate;
S32 remaining = atomic_load(&engine->test_tone_samples_remaining);
S32 samples_to_gen = (remaining < (S32)inNumberFrames) ? remaining : (S32)inNumberFrames;
// CoreAudio with kAudioFormatFlagIsNonInterleaved: each buffer = one channel
for (UInt32 buf = 0; buf < ioData->mNumberBuffers; buf++) {
float *out = (float *)ioData->mBuffers[buf].mData;
double p = phase;
F32 *out = (F32 *)ioData->mBuffers[buf].mData;
F64 p = phase;
for (UInt32 s = 0; s < inNumberFrames; s++) {
if ((int32_t)s < samples_to_gen) {
out[s] = (float)(sin(p) * 0.5); // -6dB
if ((S32)s < samples_to_gen) {
out[s] = (F32)(sin(p) * 0.5); // -6dB
p += phase_inc;
if (p >= 2.0 * AUDIO_PI) p -= 2.0 * AUDIO_PI;
} else {
@@ -88,7 +88,7 @@ static OSStatus audio_render_callback(void *inRefCon,
while (phase >= 2.0 * AUDIO_PI) phase -= 2.0 * AUDIO_PI;
engine->test_tone_phase = phase;
int32_t new_remaining = atomic_fetch_sub(&engine->test_tone_samples_remaining, samples_to_gen);
S32 new_remaining = atomic_fetch_sub(&engine->test_tone_samples_remaining, samples_to_gen);
if (new_remaining <= samples_to_gen) {
atomic_store(&engine->test_tone_active, 0);
atomic_store(&engine->test_tone_samples_remaining, 0);
@@ -113,7 +113,7 @@ static void enumerate_output_devices(AudioEngine *engine) {
if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &data_size) != noErr)
return;
int device_count = (int)(data_size / sizeof(AudioDeviceID));
S32 device_count = (S32)(data_size / sizeof(AudioDeviceID));
if (device_count <= 0) return;
AudioDeviceID *device_ids = (AudioDeviceID *)malloc(data_size);
@@ -122,7 +122,7 @@ static void enumerate_output_devices(AudioEngine *engine) {
return;
}
for (int i = 0; i < device_count && engine->device_count < AUDIO_MAX_DEVICES; i++) {
for (S32 i = 0; i < device_count && engine->device_count < AUDIO_MAX_DEVICES; i++) {
// Check if device has output channels
AudioObjectPropertyAddress stream_prop = {
kAudioDevicePropertyStreamConfiguration,
@@ -140,9 +140,9 @@ static void enumerate_output_devices(AudioEngine *engine) {
continue;
}
int output_channels = 0;
S32 output_channels = 0;
for (UInt32 b = 0; b < buf_list->mNumberBuffers; b++)
output_channels += (int)buf_list->mBuffers[b].mNumberChannels;
output_channels += (S32)buf_list->mBuffers[b].mNumberChannels;
free(buf_list);
if (output_channels == 0) continue;
@@ -159,7 +159,7 @@ static void enumerate_output_devices(AudioEngine *engine) {
if (AudioObjectGetPropertyData(device_ids[i], &name_prop, 0, nullptr, &name_size, &name_ref) != noErr)
continue;
int32_t idx = engine->device_count++;
S32 idx = engine->device_count++;
CFStringGetCString(name_ref, engine->devices[idx].name, sizeof(engine->devices[idx].name),
kCFStringEncodingUTF8);
CFRelease(name_ref);
@@ -198,16 +198,16 @@ void audio_refresh_devices(AudioEngine *engine) {
enumerate_output_devices(engine);
}
int32_t audio_get_device_count(AudioEngine *engine) {
S32 audio_get_device_count(AudioEngine *engine) {
return engine->device_count;
}
AudioDeviceInfo *audio_get_device(AudioEngine *engine, int32_t index) {
AudioDeviceInfo *audio_get_device(AudioEngine *engine, S32 index) {
if (index < 0 || index >= engine->device_count) return nullptr;
return &engine->devices[index];
}
bool audio_open_device(AudioEngine *engine, int32_t index) {
B32 audio_open_device(AudioEngine *engine, S32 index) {
audio_close_device(engine);
if (index < 0 || index >= engine->device_count) return false;
@@ -328,16 +328,16 @@ void audio_play_test_tone(AudioEngine *engine) {
if (!engine->graph) return;
engine->test_tone_phase = 0.0;
int32_t total_samples = (int32_t)(engine->sample_rate * AUDIO_TEST_TONE_SEC);
S32 total_samples = (S32)(engine->sample_rate * AUDIO_TEST_TONE_SEC);
atomic_store(&engine->test_tone_samples_remaining, total_samples);
atomic_store(&engine->test_tone_active, 1);
}
bool audio_is_test_tone_playing(AudioEngine *engine) {
B32 audio_is_test_tone_playing(AudioEngine *engine) {
return atomic_load(&engine->test_tone_active) != 0;
}
void audio_update(AudioEngine *engine, float dt) {
void audio_update(AudioEngine *engine, F32 dt) {
(void)engine;
(void)dt;
}

View File

@@ -17,6 +17,8 @@
#endif
#define local_persist static
#define trvke true
////////////////////////////////
// Base types

View File

@@ -5,7 +5,7 @@ Str8 str8_pushf(Arena *arena, const char *fmt, ...) {
va_list args, args2;
va_start(args, fmt);
va_copy(args2, args);
int len = vsnprintf(nullptr, 0, fmt, args);
S32 len = vsnprintf(nullptr, 0, fmt, args);
va_end(args);
char *buf = push_array(arena, char, len + 1);

View File

@@ -70,9 +70,9 @@ struct AppState {
B32 show_log;
B32 show_midi_devices;
#ifdef __APPLE__
uint64_t freq_numer;
uint64_t freq_denom;
uint64_t last_time;
U64 freq_numer;
U64 freq_denom;
U64 last_time;
#else
LARGE_INTEGER freq;
LARGE_INTEGER last_time;
@@ -88,11 +88,11 @@ struct AppState {
// Demo widget state
B32 demo_checkbox_a;
B32 demo_checkbox_b;
int32_t demo_radio_sel;
int32_t demo_dropdown_sel;
S32 demo_radio_sel;
S32 demo_dropdown_sel;
char demo_text_a[128];
char demo_text_b[128];
int32_t demo_button_count;
S32 demo_button_count;
// Modal / window demo state
B32 show_settings_window;
@@ -163,22 +163,22 @@ struct AppState {
#define PIANO_BLACK_W 11.0f
#define PIANO_BLACK_H_PCT 0.6f
static bool piano_is_black_key(int note) {
int n = note % 12;
static B32 piano_is_black_key(S32 note) {
S32 n = note % 12;
return n == 1 || n == 3 || n == 6 || n == 8 || n == 10;
}
// Velocity-based color: blue (vel 0) → green (mid) → red (vel 127)
static Clay_Color velocity_color(int32_t velocity) {
float t = (float)velocity / 127.0f;
float r, g, b;
static Clay_Color velocity_color(S32 velocity) {
F32 t = (F32)velocity / 127.0f;
F32 r, g, b;
if (t < 0.5f) {
float s = t * 2.0f;
F32 s = t * 2.0f;
r = 40.0f + s * (76.0f - 40.0f);
g = 120.0f + s * (175.0f - 120.0f);
b = 220.0f + s * (80.0f - 220.0f);
} else {
float s = (t - 0.5f) * 2.0f;
F32 s = (t - 0.5f) * 2.0f;
r = 76.0f + s * (220.0f - 76.0f);
g = 175.0f + s * (50.0f - 175.0f);
b = 80.0f + s * (40.0f - 80.0f);
@@ -198,14 +198,14 @@ static void update_piano_input(AppState *app) {
// Find hovered piano key — check black keys first (they're on top)
S32 hovered_note = -1;
for (int note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (piano_is_black_key(note) && Clay_PointerOver(CLAY_IDI("PKey", note))) {
hovered_note = note;
break;
}
}
if (hovered_note == -1) {
for (int note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (!piano_is_black_key(note) && Clay_PointerOver(CLAY_IDI("PKey", note))) {
hovered_note = note;
break;
@@ -225,7 +225,7 @@ static void build_browser_panel(AppState *app) {
if (!app->show_browser) return;
Clay_Color bp_top = g_theme.bg_medium;
Clay_Color bp_bot = {(float)Max((int)bp_top.r-8,0), (float)Max((int)bp_top.g-8,0), (float)Max((int)bp_top.b-8,0), 255};
Clay_Color bp_bot = {(F32)Max((S32)bp_top.r-8,0), (F32)Max((S32)bp_top.g-8,0), (F32)Max((S32)bp_top.b-8,0), 255};
CustomGradientData *bp_grad = alloc_gradient(bp_top, bp_bot);
CLAY(CLAY_ID("BrowserPanel"),
@@ -262,7 +262,7 @@ static void build_browser_panel(AppState *app) {
static void build_main_panel(AppState *app) {
Clay_Color mp_top = g_theme.bg_light;
Clay_Color mp_bot = {(float)Max((int)mp_top.r-8,0), (float)Max((int)mp_top.g-8,0), (float)Max((int)mp_top.b-8,0), 255};
Clay_Color mp_bot = {(F32)Max((S32)mp_top.r-8,0), (F32)Max((S32)mp_top.g-8,0), (F32)Max((S32)mp_top.b-8,0), 255};
CustomGradientData *mp_grad = alloc_gradient(mp_top, mp_bot);
CLAY(CLAY_ID("MainPanel"),
@@ -469,14 +469,14 @@ static void build_main_panel(AppState *app) {
{
Clay_ScrollContainerData scroll_data = Clay_GetScrollContainerData(CLAY_ID("MainContent"));
if (scroll_data.found && scroll_data.contentDimensions.height > scroll_data.scrollContainerDimensions.height) {
float track_h = scroll_data.scrollContainerDimensions.height;
float content_h = scroll_data.contentDimensions.height;
float visible_ratio = track_h / content_h;
float thumb_h = Max(visible_ratio * track_h, uis(24));
float scroll_range = content_h - track_h;
float scroll_pct = scroll_range > 0 ? -scroll_data.scrollPosition->y / scroll_range : 0;
float thumb_y = scroll_pct * (track_h - thumb_h);
float bar_w = uis(8);
F32 track_h = scroll_data.scrollContainerDimensions.height;
F32 content_h = scroll_data.contentDimensions.height;
F32 visible_ratio = track_h / content_h;
F32 thumb_h = Max(visible_ratio * track_h, uis(24));
F32 scroll_range = content_h - track_h;
F32 scroll_pct = scroll_range > 0 ? -scroll_data.scrollPosition->y / scroll_range : 0;
F32 thumb_y = scroll_pct * (track_h - thumb_h);
F32 bar_w = uis(8);
// Handle scrollbar drag
Clay_ElementId thumb_id = CLAY_ID("MainScrollThumb");
@@ -493,8 +493,8 @@ static void build_main_panel(AppState *app) {
} else if (mouse_clicked && track_hovered && !thumb_hovered) {
// Click on track: jump scroll position so thumb centers on click
Clay_BoundingBox track_bb = Clay_GetElementData(track_id).boundingBox;
float click_rel = input.mouse_pos.y - track_bb.y;
float target_pct = (click_rel - thumb_h / 2) / (track_h - thumb_h);
F32 click_rel = input.mouse_pos.y - track_bb.y;
F32 target_pct = (click_rel - thumb_h / 2) / (track_h - thumb_h);
if (target_pct < 0) target_pct = 0;
if (target_pct > 1) target_pct = 1;
scroll_data.scrollPosition->y = -target_pct * scroll_range;
@@ -509,9 +509,9 @@ static void build_main_panel(AppState *app) {
}
if (app->scrollbar_dragging) {
float dy = input.mouse_pos.y - app->scrollbar_drag_start_y;
float scroll_per_px = scroll_range / (track_h - thumb_h);
float new_scroll = app->scrollbar_drag_start_scroll - dy * scroll_per_px;
F32 dy = input.mouse_pos.y - app->scrollbar_drag_start_y;
F32 scroll_per_px = scroll_range / (track_h - thumb_h);
F32 new_scroll = app->scrollbar_drag_start_scroll - dy * scroll_per_px;
if (new_scroll > 0) new_scroll = 0;
if (new_scroll < -scroll_range) new_scroll = -scroll_range;
scroll_data.scrollPosition->y = new_scroll;
@@ -521,9 +521,9 @@ static void build_main_panel(AppState *app) {
Clay_Color thumb_color = g_theme.scrollbar_grab;
if (app->scrollbar_dragging || thumb_hovered) {
thumb_color = Clay_Color{
(float)Min((int)thumb_color.r + 30, 255),
(float)Min((int)thumb_color.g + 30, 255),
(float)Min((int)thumb_color.b + 30, 255),
(F32)Min((S32)thumb_color.r + 30, 255),
(F32)Min((S32)thumb_color.g + 30, 255),
(F32)Min((S32)thumb_color.b + 30, 255),
thumb_color.a
};
}
@@ -562,7 +562,7 @@ static void build_right_panel(AppState *app) {
static const char *right_tabs[] = { "Properties", "MIDI Devices" };
Clay_Color rp_top = g_theme.bg_medium;
Clay_Color rp_bot = {(float)Max((int)rp_top.r-8,0), (float)Max((int)rp_top.g-8,0), (float)Max((int)rp_top.b-8,0), 255};
Clay_Color rp_bot = {(F32)Max((S32)rp_top.r-8,0), (F32)Max((S32)rp_top.g-8,0), (F32)Max((S32)rp_top.b-8,0), 255};
CustomGradientData *rp_grad = alloc_gradient(rp_top, rp_bot);
CLAY(CLAY_ID("RightPanel"),
@@ -627,7 +627,7 @@ static void build_right_panel(AppState *app) {
}
static char device_bufs[64][128];
int32_t device_count = midi_get_device_count(midi);
S32 device_count = midi_get_device_count(midi);
// --- Inputs section ---
CLAY_TEXT(CLAY_STRING("Inputs"), &g_text_config_dim);
@@ -642,12 +642,12 @@ static void build_right_panel(AppState *app) {
box_text_config.wrapMode = CLAY_TEXT_WRAP_NONE;
B32 has_inputs = 0;
for (int32_t i = 0; i < device_count && i < 64; i++) {
for (S32 i = 0; i < device_count && i < 64; i++) {
MidiDeviceInfo *dev = midi_get_device(midi, i);
if (!dev->is_input) continue;
has_inputs = 1;
int len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name);
S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name);
Clay_String device_str = { .isStaticallyAllocated = false, .length = len, .chars = device_bufs[i] };
// Velocity-based color: blue (vel 0) → green (mid) → red (vel 127)
@@ -662,10 +662,10 @@ static void build_right_panel(AppState *app) {
}
// Box text: note name when held, "OFF" when releasing, "---" when idle
int nlen;
S32 nlen;
if (dev->active) {
int pitch = dev->note % 12;
int octave = (dev->note / 12) - 1;
S32 pitch = dev->note % 12;
S32 octave = (dev->note / 12) - 1;
nlen = snprintf(note_bufs[i], sizeof(note_bufs[i]), "%s%d", note_names[pitch], octave);
} else if (dev->releasing) {
nlen = snprintf(note_bufs[i], sizeof(note_bufs[i]), "OFF");
@@ -682,7 +682,7 @@ static void build_right_panel(AppState *app) {
if (dev->releasing) box_txt = &box_text_dark;
// Velocity text
int vlen;
S32 vlen;
if (dev->active)
vlen = snprintf(vel_bufs[i], sizeof(vel_bufs[i]), "%d", dev->velocity);
else
@@ -723,12 +723,12 @@ static void build_right_panel(AppState *app) {
CLAY_TEXT(CLAY_STRING("Outputs"), &g_text_config_dim);
B32 has_outputs = 0;
for (int32_t i = 0; i < device_count && i < 64; i++) {
for (S32 i = 0; i < device_count && i < 64; i++) {
MidiDeviceInfo *dev = midi_get_device(midi, i);
if (dev->is_input) continue;
has_outputs = 1;
int len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name);
S32 len = snprintf(device_bufs[i], sizeof(device_bufs[i]), "%s", dev->name);
Clay_String device_str = { .isStaticallyAllocated = false, .length = len, .chars = device_bufs[i] };
CLAY(CLAY_IDI("MidiOut", i),
@@ -752,7 +752,7 @@ static void build_log_panel(AppState *app) {
if (!app->show_log) return;
Clay_Color lp_top = g_theme.bg_medium;
Clay_Color lp_bot = {(float)Max((int)lp_top.r-8,0), (float)Max((int)lp_top.g-8,0), (float)Max((int)lp_top.b-8,0), 255};
Clay_Color lp_bot = {(F32)Max((S32)lp_top.r-8,0), (F32)Max((S32)lp_top.g-8,0), (F32)Max((S32)lp_top.b-8,0), 255};
CustomGradientData *lp_grad = alloc_gradient(lp_top, lp_bot);
CLAY(CLAY_ID("LogPanel"),
@@ -808,7 +808,7 @@ static void build_log_panel(AppState *app) {
if (black_key_w < uis(8)) black_key_w = uis(8);
// White keys (grow to fill width and height)
for (int note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (piano_is_black_key(note)) continue;
B32 midi_held = midi_is_note_held(app->midi, note);
@@ -832,7 +832,7 @@ static void build_log_panel(AppState *app) {
}
// Black keys (floating, attached to left white key)
for (int note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
for (S32 note = PIANO_FIRST_NOTE; note <= PIANO_LAST_NOTE; note++) {
if (!piano_is_black_key(note)) continue;
Clay_ElementId parent_wkey = CLAY_IDI("PKey", note - 1);
@@ -923,9 +923,9 @@ static void settings_window_content(void *user_data) {
ui_label("SettingsLblAudio", "Audio Device");
static const char *audio_options[AUDIO_MAX_DEVICES + 1];
int32_t audio_count = audio_get_device_count(app->audio);
S32 audio_count = audio_get_device_count(app->audio);
audio_options[0] = "None";
for (int32_t i = 0; i < audio_count && i < AUDIO_MAX_DEVICES; i++) {
for (S32 i = 0; i < audio_count && i < AUDIO_MAX_DEVICES; i++) {
AudioDeviceInfo *dev = audio_get_device(app->audio, i);
audio_options[i + 1] = dev ? dev->name : "???";
}
@@ -954,8 +954,8 @@ static void settings_window_content(void *user_data) {
.layoutDirection = CLAY_LEFT_TO_RIGHT,
}
) {
bool device_open = (app->audio_device_sel > 0);
bool tone_playing = audio_is_test_tone_playing(app->audio);
B32 device_open = (app->audio_device_sel > 0);
B32 tone_playing = audio_is_test_tone_playing(app->audio);
if (device_open && !tone_playing) {
if (ui_button("BtnTestTone", "Play Test Tone")) {
@@ -1062,7 +1062,7 @@ static void update_panel_splitters(AppState *app) {
static void build_header_bar(AppState *app) {
Clay_Color bar_bg = g_theme.bg_dark;
Clay_Color border_bot = {(float)Max((int)bar_bg.r - 12, 0), (float)Max((int)bar_bg.g - 12, 0), (float)Max((int)bar_bg.b - 12, 0), 255};
Clay_Color border_bot = {(F32)Max((S32)bar_bg.r - 12, 0), (F32)Max((S32)bar_bg.g - 12, 0), (F32)Max((S32)bar_bg.b - 12, 0), 255};
static Clay_TextElementConfig header_btn_active_text = {};
header_btn_active_text.textColor = Clay_Color{255, 255, 255, 255};
@@ -1084,7 +1084,7 @@ static void build_header_bar(AppState *app) {
header_indicator_label.fontSize = FONT_SIZE_SMALL;
header_indicator_label.wrapMode = CLAY_TEXT_WRAP_NONE;
Clay_Color inset_bg = {(float)Max((int)bar_bg.r - 8, 0), (float)Max((int)bar_bg.g - 8, 0), (float)Max((int)bar_bg.b - 8, 0), 255};
Clay_Color inset_bg = {(F32)Max((S32)bar_bg.r - 8, 0), (F32)Max((S32)bar_bg.g - 8, 0), (F32)Max((S32)bar_bg.b - 8, 0), 255};
CLAY(CLAY_ID("HeaderBar"),
.layout = {
@@ -1297,7 +1297,7 @@ static void build_header_bar(AppState *app) {
static void build_mix_view(AppState *app) {
Clay_Color mv_top = g_theme.bg_medium;
Clay_Color mv_bot = {(float)Max((int)mv_top.r - 8, 0), (float)Max((int)mv_top.g - 8, 0), (float)Max((int)mv_top.b - 8, 0), 255};
Clay_Color mv_bot = {(F32)Max((S32)mv_top.r - 8, 0), (F32)Max((S32)mv_top.g - 8, 0), (F32)Max((S32)mv_top.b - 8, 0), 255};
CustomGradientData *mv_grad = alloc_gradient(mv_top, mv_bot);
CLAY(CLAY_ID("MixView"),
@@ -1314,7 +1314,7 @@ static void build_mix_view(AppState *app) {
static char fader_id_bufs[8][16];
static char pan_id_bufs[8][16];
for (int i = 0; i < 8; i++) {
for (S32 i = 0; i < 8; i++) {
snprintf(ch_label_bufs[i], sizeof(ch_label_bufs[i]), "Ch %d", i + 1);
snprintf(fader_id_bufs[i], sizeof(fader_id_bufs[i]), "MixFader%d", i);
snprintf(pan_id_bufs[i], sizeof(pan_id_bufs[i]), "MixPan%d", i);
@@ -1333,7 +1333,7 @@ static void build_mix_view(AppState *app) {
.border = { .color = g_theme.border, .width = { .right = 1 } },
) {
// Channel label
int llen = snprintf(ch_label_bufs[i], sizeof(ch_label_bufs[i]), "Ch %d", i + 1);
S32 llen = snprintf(ch_label_bufs[i], sizeof(ch_label_bufs[i]), "Ch %d", i + 1);
Clay_String ch_str = { .isStaticallyAllocated = false, .length = llen, .chars = ch_label_bufs[i] };
CLAY_TEXT(ch_str, &g_text_config_dim);
@@ -1367,9 +1367,9 @@ static void build_mix_view(AppState *app) {
// Master strip (slightly wider, accent-tinted)
{
Clay_Color master_bg = {
(float)Min((int)g_theme.bg_medium.r + 6, 255),
(float)Min((int)g_theme.bg_medium.g + 6, 255),
(float)Min((int)g_theme.bg_medium.b + 6, 255),
(F32)Min((S32)g_theme.bg_medium.r + 6, 255),
(F32)Min((S32)g_theme.bg_medium.g + 6, 255),
(F32)Min((S32)g_theme.bg_medium.b + 6, 255),
255
};
static F32 master_fader = 0.0f;
@@ -1420,7 +1420,7 @@ static void build_mix_view(AppState *app) {
static void build_patch_view(AppState *app) {
Clay_Color pv_top = g_theme.bg_medium;
Clay_Color pv_bot = {(float)Max((int)pv_top.r - 8, 0), (float)Max((int)pv_top.g - 8, 0), (float)Max((int)pv_top.b - 8, 0), 255};
Clay_Color pv_bot = {(F32)Max((S32)pv_top.r - 8, 0), (F32)Max((S32)pv_top.g - 8, 0), (F32)Max((S32)pv_top.b - 8, 0), 255};
CustomGradientData *pv_grad = alloc_gradient(pv_top, pv_bot);
CLAY(CLAY_ID("PatchView"),
@@ -1495,7 +1495,7 @@ static void build_patch_view(AppState *app) {
CLAY(CLAY_ID("IntOutputLabel"),
.layout = {
.sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() },
.padding = { (uint16_t)label_w, 0, 0, 0 },
.padding = { (U16)label_w, 0, 0, 0 },
}
) {
CLAY_TEXT(CLAY_STRING("OUTPUT >"), &matrix_axis_text);
@@ -1526,8 +1526,8 @@ static void build_patch_view(AppState *app) {
}
static char int_dst_bufs[MATRIX_OUTPUTS][8];
for (int d = 0; d < MATRIX_OUTPUTS; d++) {
int dlen;
for (S32 d = 0; d < MATRIX_OUTPUTS; d++) {
S32 dlen;
if (d == 0)
dlen = snprintf(int_dst_bufs[d], sizeof(int_dst_bufs[d]), "Mst");
else
@@ -1547,8 +1547,8 @@ static void build_patch_view(AppState *app) {
// Rows
static char int_src_bufs[MATRIX_INPUTS][8];
for (int s = 0; s < MATRIX_INPUTS; s++) {
int slen = snprintf(int_src_bufs[s], sizeof(int_src_bufs[s]), "Ch %d", s + 1);
for (S32 s = 0; s < MATRIX_INPUTS; s++) {
S32 slen = snprintf(int_src_bufs[s], sizeof(int_src_bufs[s]), "Ch %d", s + 1);
Clay_String src_str = { .isStaticallyAllocated = false, .length = slen, .chars = int_src_bufs[s] };
CLAY(CLAY_IDI("IntRow", s),
@@ -1567,8 +1567,8 @@ static void build_patch_view(AppState *app) {
CLAY_TEXT(src_str, &matrix_hdr_text);
}
for (int d = 0; d < MATRIX_OUTPUTS; d++) {
int cell_idx = s * MATRIX_OUTPUTS + d;
for (S32 d = 0; d < MATRIX_OUTPUTS; d++) {
S32 cell_idx = s * MATRIX_OUTPUTS + d;
Clay_ElementId cell_eid = CLAY_IDI("IntCell", cell_idx);
B32 cell_hovered = Clay_PointerOver(cell_eid);
@@ -1580,9 +1580,9 @@ static void build_patch_view(AppState *app) {
Clay_Color cell_bg;
if (is_feedback) {
cell_bg = Clay_Color{
(float)Max((int)g_theme.bg_dark.r - 10, 0),
(float)Max((int)g_theme.bg_dark.g - 10, 0),
(float)Max((int)g_theme.bg_dark.b - 10, 0), 255
(F32)Max((S32)g_theme.bg_dark.r - 10, 0),
(F32)Max((S32)g_theme.bg_dark.g - 10, 0),
(F32)Max((S32)g_theme.bg_dark.b - 10, 0), 255
};
} else if (active) {
cell_bg = g_theme.accent;
@@ -1592,17 +1592,17 @@ static void build_patch_view(AppState *app) {
cell_bg = g_theme.bg_dark;
} else {
cell_bg = Clay_Color{
(float)Min((int)g_theme.bg_dark.r + 6, 255),
(float)Min((int)g_theme.bg_dark.g + 6, 255),
(float)Min((int)g_theme.bg_dark.b + 6, 255), 255
(F32)Min((S32)g_theme.bg_dark.r + 6, 255),
(F32)Min((S32)g_theme.bg_dark.g + 6, 255),
(F32)Min((S32)g_theme.bg_dark.b + 6, 255), 255
};
}
if (d == 0 && !active && !cell_hovered && !is_feedback) {
cell_bg = Clay_Color{
(float)Min((int)cell_bg.r + 10, 255),
(float)Min((int)cell_bg.g + 10, 255),
(float)Min((int)cell_bg.b + 12, 255), 255
(F32)Min((S32)cell_bg.r + 10, 255),
(F32)Min((S32)cell_bg.g + 10, 255),
(F32)Min((S32)cell_bg.b + 12, 255), 255
};
}
@@ -1647,7 +1647,7 @@ static void build_patch_view(AppState *app) {
CLAY(CLAY_ID("HwOutputLabel"),
.layout = {
.sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() },
.padding = { (uint16_t)label_w, 0, 0, 0 },
.padding = { (U16)label_w, 0, 0, 0 },
}
) {
CLAY_TEXT(CLAY_STRING("HW OUTPUT >"), &matrix_axis_text);
@@ -1678,8 +1678,8 @@ static void build_patch_view(AppState *app) {
}
static char hw_dst_bufs[HW_OUTPUTS][8];
for (int d = 0; d < HW_OUTPUTS; d++) {
int dlen = snprintf(hw_dst_bufs[d], sizeof(hw_dst_bufs[d]), "%d", d + 1);
for (S32 d = 0; d < HW_OUTPUTS; d++) {
S32 dlen = snprintf(hw_dst_bufs[d], sizeof(hw_dst_bufs[d]), "%d", d + 1);
Clay_String dst_str = { .isStaticallyAllocated = false, .length = dlen, .chars = hw_dst_bufs[d] };
CLAY(CLAY_IDI("HwDstHdr", d),
@@ -1695,8 +1695,8 @@ static void build_patch_view(AppState *app) {
// Rows
static char hw_src_bufs[HW_OUTPUTS][8];
for (int s = 0; s < HW_OUTPUTS; s++) {
int slen = snprintf(hw_src_bufs[s], sizeof(hw_src_bufs[s]), "Out %d", s + 1);
for (S32 s = 0; s < HW_OUTPUTS; s++) {
S32 slen = snprintf(hw_src_bufs[s], sizeof(hw_src_bufs[s]), "Out %d", s + 1);
Clay_String src_str = { .isStaticallyAllocated = false, .length = slen, .chars = hw_src_bufs[s] };
CLAY(CLAY_IDI("HwRow", s),
@@ -1715,8 +1715,8 @@ static void build_patch_view(AppState *app) {
CLAY_TEXT(src_str, &matrix_hdr_text);
}
for (int d = 0; d < HW_OUTPUTS; d++) {
int cell_idx = s * HW_OUTPUTS + d;
for (S32 d = 0; d < HW_OUTPUTS; d++) {
S32 cell_idx = s * HW_OUTPUTS + d;
Clay_ElementId cell_eid = CLAY_IDI("HwCell", cell_idx);
B32 cell_hovered = Clay_PointerOver(cell_eid);
B32 active = app->hw_matrix[s][d];
@@ -1730,9 +1730,9 @@ static void build_patch_view(AppState *app) {
cell_bg = g_theme.bg_dark;
} else {
cell_bg = Clay_Color{
(float)Min((int)g_theme.bg_dark.r + 6, 255),
(float)Min((int)g_theme.bg_dark.g + 6, 255),
(float)Min((int)g_theme.bg_dark.b + 6, 255), 255
(F32)Min((S32)g_theme.bg_dark.r + 6, 255),
(F32)Min((S32)g_theme.bg_dark.g + 6, 255),
(F32)Min((S32)g_theme.bg_dark.b + 6, 255), 255
};
}
@@ -1870,7 +1870,7 @@ static void build_ui(AppState *app) {
static void do_frame(AppState *app) {
// Timing
#ifdef __APPLE__
uint64_t now = mach_absolute_time();
U64 now = mach_absolute_time();
F32 dt = (F32)(now - app->last_time) * (F32)app->freq_numer / ((F32)app->freq_denom * 1e9f);
app->last_time = now;
#else
@@ -2043,7 +2043,7 @@ int main(int argc, char **argv) {
app.log_height = 180.0f;
app.panel_drag = 0;
app.master_layout = 0;
for (int i = 0; i < 8; i++) { app.mix_faders[i] = 0.0f; app.mix_pans[i] = 0.0f; }
for (S32 i = 0; i < 8; i++) { app.mix_faders[i] = 0.0f; app.mix_pans[i] = 0.0f; }
app.mix_master_pan = 0.0f;
app.patch_tab = 0;
memset(app.patch_matrix, 0, sizeof(app.patch_matrix));
@@ -2061,11 +2061,12 @@ int main(int argc, char **argv) {
platform_set_frame_callback(window, frame_callback, &app);
while (platform_poll_events(window)) {
B32 running = 1;
while (running && platform_poll_events(window)) {
// Menu commands
int32_t menu_cmd = platform_poll_menu_command(window);
S32 menu_cmd = platform_poll_menu_command(window);
switch (menu_cmd) {
case MENU_FILE_EXIT: goto exit_app;
case MENU_FILE_EXIT: running = 0; break;
case MENU_VIEW_BROWSER: app.show_browser = !app.show_browser; break;
case MENU_VIEW_PROPERTIES:app.show_props = !app.show_props; break;
case MENU_VIEW_LOG: app.show_log = !app.show_log; break;
@@ -2073,10 +2074,9 @@ int main(int argc, char **argv) {
default: break;
}
do_frame(&app);
if (running) do_frame(&app);
}
exit_app:
audio_destroy(audio);
midi_destroy(midi);
ui_destroy(ui);

View File

@@ -1,32 +1,31 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "base/base_core.h"
struct MidiEngine;
struct MidiDeviceInfo {
char name[64];
int32_t id;
bool is_input;
bool active; // true when note(s) currently held
bool releasing; // true during release flash
int32_t velocity; // last note-on velocity (0-127)
int32_t note; // last MIDI note number (0-127)
S32 id;
B32 is_input;
B32 active; // true when note(s) currently held
B32 releasing; // true during release flash
S32 velocity; // last note-on velocity (0-127)
S32 note; // last MIDI note number (0-127)
};
MidiEngine *midi_create();
void midi_destroy(MidiEngine *engine);
void midi_refresh_devices(MidiEngine *engine);
int32_t midi_get_device_count(MidiEngine *engine);
MidiDeviceInfo *midi_get_device(MidiEngine *engine, int32_t index);
S32 midi_get_device_count(MidiEngine *engine);
MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index);
void midi_open_all_inputs(MidiEngine *engine);
void midi_close_all_inputs(MidiEngine *engine);
void midi_update(MidiEngine *engine, float dt);
bool midi_is_input_active(MidiEngine *engine, int32_t device_index);
void midi_update(MidiEngine *engine, F32 dt);
B32 midi_is_input_active(MidiEngine *engine, S32 device_index);
// Per-note state: returns true if note (0-127) is currently held on any input device
bool midi_is_note_held(MidiEngine *engine, int32_t note);
B32 midi_is_note_held(MidiEngine *engine, S32 note);
// Returns the last note-on velocity (0-127) for a given note, or 0 if not held
int32_t midi_get_note_velocity(MidiEngine *engine, int32_t note);
S32 midi_get_note_velocity(MidiEngine *engine, S32 note);

View File

@@ -9,28 +9,28 @@
struct MidiEngine {
MidiDeviceInfo devices[MIDI_MAX_DEVICES];
int32_t device_count;
S32 device_count;
MIDIClientRef client;
MIDIPortRef input_port;
// Map: source endpoint index -> our device array index
int32_t source_to_device[MIDI_MAX_DEVICES];
S32 source_to_device[MIDI_MAX_DEVICES];
// Set atomically from callback thread
_Atomic int32_t pending_note_on_vel[MIDI_MAX_DEVICES];
_Atomic int32_t pending_note_num[MIDI_MAX_DEVICES];
_Atomic int32_t pending_note_off[MIDI_MAX_DEVICES];
_Atomic int32_t held_note_count[MIDI_MAX_DEVICES];
_Atomic S32 pending_note_on_vel[MIDI_MAX_DEVICES];
_Atomic S32 pending_note_num[MIDI_MAX_DEVICES];
_Atomic S32 pending_note_off[MIDI_MAX_DEVICES];
_Atomic S32 held_note_count[MIDI_MAX_DEVICES];
// Per-note state (across all devices), set atomically from callback
_Atomic int32_t note_states[128]; // held count
_Atomic int32_t note_velocities[128]; // last note-on velocity
_Atomic S32 note_states[128]; // held count
_Atomic S32 note_velocities[128]; // last note-on velocity
// Main thread only
int32_t display_velocity[MIDI_MAX_DEVICES];
int32_t display_note[MIDI_MAX_DEVICES];
float release_timers[MIDI_MAX_DEVICES];
S32 display_velocity[MIDI_MAX_DEVICES];
S32 display_note[MIDI_MAX_DEVICES];
F32 release_timers[MIDI_MAX_DEVICES];
};
////////////////////////////////
@@ -40,51 +40,51 @@ static void midi_read_callback(const MIDIPacketList *pktlist, void *readProcRefC
(void)readProcRefCon;
MidiEngine *engine = (MidiEngine *)readProcRefCon;
int32_t device_idx = (int32_t)(intptr_t)srcConnRefCon;
S32 device_idx = (S32)(intptr_t)srcConnRefCon;
if (!engine || device_idx < 0 || device_idx >= MIDI_MAX_DEVICES) return;
const MIDIPacket *packet = &pktlist->packet[0];
for (UInt32 i = 0; i < pktlist->numPackets; i++) {
// Parse MIDI bytes
for (UInt16 j = 0; j < packet->length; ) {
uint8_t status = packet->data[j];
U8 status = packet->data[j];
// Skip non-status bytes (running status not handled for simplicity)
if (status < 0x80) { j++; continue; }
uint8_t kind = status & 0xF0;
U8 kind = status & 0xF0;
if (kind == 0x90 && j + 2 < packet->length) {
uint8_t note = packet->data[j + 1];
uint8_t velocity = packet->data[j + 2];
U8 note = packet->data[j + 1];
U8 velocity = packet->data[j + 2];
j += 3;
if (velocity > 0) {
atomic_store(&engine->pending_note_on_vel[device_idx], (int32_t)velocity);
atomic_store(&engine->pending_note_num[device_idx], (int32_t)(note + 1));
atomic_store(&engine->pending_note_on_vel[device_idx], (S32)velocity);
atomic_store(&engine->pending_note_num[device_idx], (S32)(note + 1));
atomic_fetch_add(&engine->held_note_count[device_idx], 1);
if (note < 128) {
atomic_fetch_add(&engine->note_states[note], 1);
atomic_store(&engine->note_velocities[note], (int32_t)velocity);
atomic_store(&engine->note_velocities[note], (S32)velocity);
}
} else {
// Note-on with velocity 0 = note-off
atomic_store(&engine->pending_note_off[device_idx], 1);
int32_t count = atomic_fetch_sub(&engine->held_note_count[device_idx], 1);
S32 count = atomic_fetch_sub(&engine->held_note_count[device_idx], 1);
if (count <= 1) atomic_store(&engine->held_note_count[device_idx], 0);
if (note < 128) {
int32_t c = atomic_fetch_sub(&engine->note_states[note], 1);
S32 c = atomic_fetch_sub(&engine->note_states[note], 1);
if (c <= 1) atomic_store(&engine->note_states[note], 0);
}
}
} else if (kind == 0x80 && j + 2 < packet->length) {
uint8_t note_off = packet->data[j + 1];
U8 note_off = packet->data[j + 1];
j += 3;
atomic_store(&engine->pending_note_off[device_idx], 1);
int32_t count = atomic_fetch_sub(&engine->held_note_count[device_idx], 1);
S32 count = atomic_fetch_sub(&engine->held_note_count[device_idx], 1);
if (count <= 1) atomic_store(&engine->held_note_count[device_idx], 0);
if (note_off < 128) {
int32_t c = atomic_fetch_sub(&engine->note_states[note_off], 1);
S32 c = atomic_fetch_sub(&engine->note_states[note_off], 1);
if (c <= 1) atomic_store(&engine->note_states[note_off], 0);
}
} else if (kind == 0xC0 || kind == 0xD0) {
@@ -123,7 +123,7 @@ static void enumerate_midi_devices(MidiEngine *engine) {
CFStringGetCString(name_ref, dev->name, sizeof(dev->name), kCFStringEncodingUTF8);
CFRelease(name_ref);
} else {
snprintf(dev->name, sizeof(dev->name), "MIDI Source %d", (int)i);
snprintf(dev->name, sizeof(dev->name), "MIDI Source %d", (S32)i);
}
dev->id = engine->device_count;
@@ -149,7 +149,7 @@ static void enumerate_midi_devices(MidiEngine *engine) {
CFStringGetCString(name_ref, dev->name, sizeof(dev->name), kCFStringEncodingUTF8);
CFRelease(name_ref);
} else {
snprintf(dev->name, sizeof(dev->name), "MIDI Dest %d", (int)i);
snprintf(dev->name, sizeof(dev->name), "MIDI Dest %d", (S32)i);
}
dev->id = engine->device_count;
@@ -183,9 +183,9 @@ void midi_destroy(MidiEngine *engine) {
void midi_open_all_inputs(MidiEngine *engine) {
ItemCount num_sources = MIDIGetNumberOfSources();
for (ItemCount i = 0; i < num_sources && (int32_t)i < MIDI_MAX_DEVICES; i++) {
for (ItemCount i = 0; i < num_sources && (S32)i < MIDI_MAX_DEVICES; i++) {
MIDIEndpointRef endpoint = MIDIGetSource(i);
int32_t dev_idx = engine->source_to_device[i];
S32 dev_idx = engine->source_to_device[i];
MIDIPortConnectSource(engine->input_port, endpoint, (void *)(intptr_t)dev_idx);
}
}
@@ -197,7 +197,7 @@ void midi_close_all_inputs(MidiEngine *engine) {
MIDIPortDisconnectSource(engine->input_port, endpoint);
}
for (int32_t i = 0; i < MIDI_MAX_DEVICES; i++) {
for (S32 i = 0; i < MIDI_MAX_DEVICES; i++) {
atomic_store(&engine->pending_note_on_vel[i], 0);
atomic_store(&engine->pending_note_num[i], 0);
atomic_store(&engine->pending_note_off[i], 0);
@@ -206,23 +206,23 @@ void midi_close_all_inputs(MidiEngine *engine) {
engine->display_note[i] = 0;
engine->release_timers[i] = 0.0f;
}
for (int32_t i = 0; i < 128; i++) {
for (S32 i = 0; i < 128; i++) {
atomic_store(&engine->note_states[i], 0);
atomic_store(&engine->note_velocities[i], 0);
}
}
void midi_update(MidiEngine *engine, float dt) {
for (int32_t i = 0; i < engine->device_count; i++) {
void midi_update(MidiEngine *engine, F32 dt) {
for (S32 i = 0; i < engine->device_count; i++) {
if (!engine->devices[i].is_input) continue;
int32_t vel = atomic_exchange(&engine->pending_note_on_vel[i], 0);
int32_t note_p1 = atomic_exchange(&engine->pending_note_num[i], 0);
S32 vel = atomic_exchange(&engine->pending_note_on_vel[i], 0);
S32 note_p1 = atomic_exchange(&engine->pending_note_num[i], 0);
if (vel > 0) engine->display_velocity[i] = vel;
if (note_p1 > 0) engine->display_note[i] = note_p1 - 1;
int32_t off = atomic_exchange(&engine->pending_note_off[i], 0);
int32_t held = atomic_load(&engine->held_note_count[i]);
S32 off = atomic_exchange(&engine->pending_note_off[i], 0);
S32 held = atomic_load(&engine->held_note_count[i]);
if (held > 0) {
engine->devices[i].active = true;
@@ -249,17 +249,17 @@ void midi_update(MidiEngine *engine, float dt) {
}
}
bool midi_is_input_active(MidiEngine *engine, int32_t device_index) {
B32 midi_is_input_active(MidiEngine *engine, S32 device_index) {
if (device_index < 0 || device_index >= engine->device_count) return false;
return engine->devices[device_index].active;
}
bool midi_is_note_held(MidiEngine *engine, int32_t note) {
B32 midi_is_note_held(MidiEngine *engine, S32 note) {
if (note < 0 || note > 127) return false;
return atomic_load(&engine->note_states[note]) > 0;
}
int32_t midi_get_note_velocity(MidiEngine *engine, int32_t note) {
S32 midi_get_note_velocity(MidiEngine *engine, S32 note) {
if (note < 0 || note > 127) return 0;
if (atomic_load(&engine->note_states[note]) <= 0) return 0;
return atomic_load(&engine->note_velocities[note]);
@@ -271,11 +271,11 @@ void midi_refresh_devices(MidiEngine *engine) {
midi_open_all_inputs(engine);
}
int32_t midi_get_device_count(MidiEngine *engine) {
S32 midi_get_device_count(MidiEngine *engine) {
return engine->device_count;
}
MidiDeviceInfo *midi_get_device(MidiEngine *engine, int32_t index) {
MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index) {
if (index < 0 || index >= engine->device_count) return nullptr;
return &engine->devices[index];
}

View File

@@ -8,7 +8,7 @@
struct MidiEngine {
MidiDeviceInfo devices[MIDI_MAX_DEVICES];
int32_t device_count;
S32 device_count;
HMIDIIN input_handles[MIDI_MAX_DEVICES];
@@ -23,9 +23,9 @@ struct MidiEngine {
volatile LONG note_velocities[128]; // last note-on velocity
// Main thread only
int32_t display_velocity[MIDI_MAX_DEVICES];
int32_t display_note[MIDI_MAX_DEVICES];
float release_timers[MIDI_MAX_DEVICES];
S32 display_velocity[MIDI_MAX_DEVICES];
S32 display_note[MIDI_MAX_DEVICES];
F32 release_timers[MIDI_MAX_DEVICES];
};
////////////////////////////////
@@ -41,7 +41,7 @@ static void CALLBACK midi_in_callback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwIn
if (wMsg != MIM_DATA) return;
if (!g_midi_engine) return;
int32_t idx = (int32_t)dwInstance;
S32 idx = (S32)dwInstance;
if (idx < 0 || idx >= MIDI_MAX_DEVICES) return;
BYTE status = (BYTE)(dwParam1 & 0xFF);
@@ -86,7 +86,7 @@ void midi_destroy(MidiEngine *engine) {
}
void midi_open_all_inputs(MidiEngine *engine) {
for (int32_t i = 0; i < engine->device_count; i++) {
for (S32 i = 0; i < engine->device_count; i++) {
MidiDeviceInfo *dev = &engine->devices[i];
if (!dev->is_input) continue;
if (engine->input_handles[i]) continue; // already open
@@ -103,7 +103,7 @@ void midi_open_all_inputs(MidiEngine *engine) {
}
void midi_close_all_inputs(MidiEngine *engine) {
for (int32_t i = 0; i < MIDI_MAX_DEVICES; i++) {
for (S32 i = 0; i < MIDI_MAX_DEVICES; i++) {
if (engine->input_handles[i]) {
midiInStop(engine->input_handles[i]);
midiInClose(engine->input_handles[i]);
@@ -117,24 +117,24 @@ void midi_close_all_inputs(MidiEngine *engine) {
engine->display_note[i] = 0;
engine->release_timers[i] = 0.0f;
}
for (int32_t i = 0; i < 128; i++) {
for (S32 i = 0; i < 128; i++) {
engine->note_states[i] = 0;
engine->note_velocities[i] = 0;
}
}
void midi_update(MidiEngine *engine, float dt) {
for (int32_t i = 0; i < engine->device_count; i++) {
void midi_update(MidiEngine *engine, F32 dt) {
for (S32 i = 0; i < engine->device_count; i++) {
if (!engine->devices[i].is_input) continue;
// Consume pending note-on velocity and note number
LONG vel = InterlockedExchange(&engine->pending_note_on_vel[i], 0);
LONG note_p1 = InterlockedExchange(&engine->pending_note_num[i], 0);
if (vel > 0) {
engine->display_velocity[i] = (int32_t)vel;
engine->display_velocity[i] = (S32)vel;
}
if (note_p1 > 0) {
engine->display_note[i] = (int32_t)(note_p1 - 1);
engine->display_note[i] = (S32)(note_p1 - 1);
}
// Consume pending note-off
@@ -170,21 +170,21 @@ void midi_update(MidiEngine *engine, float dt) {
}
}
bool midi_is_input_active(MidiEngine *engine, int32_t device_index) {
B32 midi_is_input_active(MidiEngine *engine, S32 device_index) {
if (device_index < 0 || device_index >= engine->device_count)
return false;
return engine->devices[device_index].active;
}
bool midi_is_note_held(MidiEngine *engine, int32_t note) {
B32 midi_is_note_held(MidiEngine *engine, S32 note) {
if (note < 0 || note > 127) return false;
return engine->note_states[note] > 0;
}
int32_t midi_get_note_velocity(MidiEngine *engine, int32_t note) {
S32 midi_get_note_velocity(MidiEngine *engine, S32 note) {
if (note < 0 || note > 127) return 0;
if (engine->note_states[note] <= 0) return 0;
return (int32_t)engine->note_velocities[note];
return (S32)engine->note_velocities[note];
}
void midi_refresh_devices(MidiEngine *engine) {
@@ -197,7 +197,7 @@ void midi_refresh_devices(MidiEngine *engine) {
if (midiInGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
MidiDeviceInfo *dev = &engine->devices[engine->device_count++];
strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE);
dev->id = (int32_t)i;
dev->id = (S32)i;
dev->is_input = true;
dev->active = false;
}
@@ -209,7 +209,7 @@ void midi_refresh_devices(MidiEngine *engine) {
if (midiOutGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
MidiDeviceInfo *dev = &engine->devices[engine->device_count++];
strncpy_s(dev->name, sizeof(dev->name), caps.szPname, _TRUNCATE);
dev->id = (int32_t)i;
dev->id = (S32)i;
dev->is_input = false;
dev->active = false;
}
@@ -218,11 +218,11 @@ void midi_refresh_devices(MidiEngine *engine) {
midi_open_all_inputs(engine);
}
int32_t midi_get_device_count(MidiEngine *engine) {
S32 midi_get_device_count(MidiEngine *engine) {
return engine->device_count;
}
MidiDeviceInfo *midi_get_device(MidiEngine *engine, int32_t index) {
MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index) {
if (index < 0 || index >= engine->device_count)
return nullptr;
return &engine->devices[index];

View File

@@ -1,7 +1,5 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "base/base_core.h"
#include "base/base_math.h"
@@ -35,16 +33,16 @@ enum {
struct PlatformInput {
// Typed characters (UTF-16 code units, printable only)
uint16_t chars[PLATFORM_MAX_CHARS_PER_FRAME];
int32_t char_count;
U16 chars[PLATFORM_MAX_CHARS_PER_FRAME];
S32 char_count;
// Key-down events (virtual key codes)
uint8_t keys[PLATFORM_MAX_KEYS_PER_FRAME];
int32_t key_count;
U8 keys[PLATFORM_MAX_KEYS_PER_FRAME];
S32 key_count;
// Modifier state at time of last key event
bool ctrl_held;
bool shift_held;
B32 ctrl_held;
B32 shift_held;
// Mouse state (polled per frame)
Vec2F32 mouse_pos;
@@ -57,19 +55,19 @@ struct PlatformWindow;
struct PlatformWindowDesc {
const char *title = "autosample";
int32_t width = 1280;
int32_t height = 720;
S32 width = 1280;
S32 height = 720;
};
struct PlatformMenuItem {
const char *label; // nullptr = separator
int32_t id; // command ID (ignored for separators)
S32 id; // command ID (ignored for separators)
};
struct PlatformMenu {
const char *label;
PlatformMenuItem *items;
int32_t item_count;
S32 item_count;
};
// Called by the platform layer when the window needs a frame rendered
@@ -79,13 +77,13 @@ typedef void (*PlatformFrameCallback)(void *user_data);
PlatformWindow *platform_create_window(PlatformWindowDesc *desc);
void platform_destroy_window(PlatformWindow *window);
bool platform_poll_events(PlatformWindow *window);
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h);
B32 platform_poll_events(PlatformWindow *window);
void platform_get_size(PlatformWindow *window, S32 *w, S32 *h);
void *platform_get_native_handle(PlatformWindow *window);
void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data);
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count);
int32_t platform_poll_menu_command(PlatformWindow *window);
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_count);
S32 platform_poll_menu_command(PlatformWindow *window);
// Returns accumulated input since last call (keyboard events + polled mouse state), then clears the buffer.
PlatformInput platform_get_input(PlatformWindow *window);

View File

@@ -18,7 +18,7 @@ enum {
kVK_ANSI_KeypadEnter = 0x4C,
};
static uint8_t macos_keycode_to_pkey(uint16_t keycode) {
static U8 macos_keycode_to_pkey(U16 keycode) {
switch (keycode) {
case kVK_ANSI_A: return PKEY_A;
case kVK_ANSI_C: return PKEY_C;
@@ -114,7 +114,7 @@ static PlatformWindow *g_current_window = nullptr;
}
- (void)keyDown:(NSEvent *)event {
extern void platform_macos_key_down(uint16_t keycode, NSEventModifierFlags mods);
extern void platform_macos_key_down(U16 keycode, NSEventModifierFlags mods);
platform_macos_key_down([event keyCode], [event modifierFlags]);
// Feed into text input system for character generation
@@ -142,8 +142,8 @@ static PlatformWindow *g_current_window = nullptr;
- (void)mouseDragged:(NSEvent *)event { (void)event; }
- (void)scrollWheel:(NSEvent *)event {
extern void platform_macos_scroll(float dx, float dy);
float dy = (float)[event scrollingDeltaY];
extern void platform_macos_scroll(F32 dx, F32 dy);
F32 dy = (F32)[event scrollingDeltaY];
if ([event hasPreciseScrollingDeltas])
dy /= 40.0f; // Normalize trackpad deltas to match discrete wheel steps
platform_macos_scroll(0, dy);
@@ -160,10 +160,10 @@ struct PlatformWindow {
NSWindow *ns_window;
ASmplView *view;
ASmplWindowDelegate *delegate;
bool should_close;
int32_t width;
int32_t height;
int32_t pending_menu_cmd;
B32 should_close;
S32 width;
S32 height;
S32 pending_menu_cmd;
PlatformFrameCallback frame_callback;
void *frame_callback_user_data;
PlatformInput input;
@@ -183,8 +183,8 @@ void platform_macos_handle_resize() {
if (!g_current_window) return;
NSRect frame = [g_current_window->view bounds];
F32 scale = g_current_window->backing_scale;
g_current_window->width = (int32_t)(frame.size.width * scale);
g_current_window->height = (int32_t)(frame.size.height * scale);
g_current_window->width = (S32)(frame.size.width * scale);
g_current_window->height = (S32)(frame.size.height * scale);
if (g_current_window->frame_callback)
g_current_window->frame_callback(g_current_window->frame_callback_user_data);
}
@@ -193,11 +193,11 @@ void platform_macos_insert_text(const char *utf8) {
if (!g_current_window || !utf8) return;
PlatformInput *ev = &g_current_window->input;
while (*utf8 && ev->char_count < PLATFORM_MAX_CHARS_PER_FRAME) {
uint8_t c = (uint8_t)*utf8;
U8 c = (U8)*utf8;
if (c < 32) { utf8++; continue; }
// Handle ASCII printable range (single-byte UTF-8)
if (c < 0x80) {
ev->chars[ev->char_count++] = (uint16_t)c;
ev->chars[ev->char_count++] = (U16)c;
utf8++;
} else {
// Skip multi-byte UTF-8 sequences for now (UI only handles ASCII)
@@ -208,11 +208,11 @@ void platform_macos_insert_text(const char *utf8) {
}
}
void platform_macos_key_down(uint16_t keycode, NSEventModifierFlags mods) {
void platform_macos_key_down(U16 keycode, NSEventModifierFlags mods) {
if (!g_current_window) return;
PlatformInput *ev = &g_current_window->input;
uint8_t pkey = macos_keycode_to_pkey(keycode);
U8 pkey = macos_keycode_to_pkey(keycode);
if (pkey && ev->key_count < PLATFORM_MAX_KEYS_PER_FRAME)
ev->keys[ev->key_count++] = pkey;
@@ -229,7 +229,7 @@ void platform_macos_mouse_up() {
if (g_current_window) g_current_window->mouse_down_state = 0;
}
void platform_macos_scroll(float dx, float dy) {
void platform_macos_scroll(F32 dx, F32 dy) {
(void)dx;
if (g_current_window) g_current_window->input.scroll_delta.y += dy;
}
@@ -245,7 +245,7 @@ void platform_macos_scroll(float dx, float dy) {
- (void)menuAction:(id)sender {
if (!g_current_window) return;
NSMenuItem *item = (NSMenuItem *)sender;
g_current_window->pending_menu_cmd = (int32_t)[item tag];
g_current_window->pending_menu_cmd = (S32)[item tag];
}
@end
@@ -288,8 +288,8 @@ PlatformWindow *platform_create_window(PlatformWindowDesc *desc) {
window->delegate = delegate;
window->should_close = false;
window->backing_scale = (F32)[ns_window backingScaleFactor];
window->width = (int32_t)(desc->width * window->backing_scale);
window->height = (int32_t)(desc->height * window->backing_scale);
window->width = (S32)(desc->width * window->backing_scale);
window->height = (S32)(desc->height * window->backing_scale);
g_current_window = window;
@@ -308,7 +308,7 @@ void platform_destroy_window(PlatformWindow *window) {
delete window;
}
bool platform_poll_events(PlatformWindow *window) {
B32 platform_poll_events(PlatformWindow *window) {
@autoreleasepool {
NSEvent *event;
while ((event = [NSApp nextEventMatchingMask:NSEventMaskAny
@@ -321,7 +321,7 @@ bool platform_poll_events(PlatformWindow *window) {
return !window->should_close;
}
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h) {
void platform_get_size(PlatformWindow *window, S32 *w, S32 *h) {
if (w) *w = window->width;
if (h) *h = window->height;
}
@@ -335,7 +335,7 @@ void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback c
window->frame_callback_user_data = user_data;
}
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count) {
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_count) {
(void)window;
if (!g_menu_target)
@@ -352,12 +352,12 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu
[app_menu_item setSubmenu:app_menu];
[menu_bar addItem:app_menu_item];
for (int32_t i = 0; i < menu_count; i++) {
for (S32 i = 0; i < menu_count; i++) {
NSMenuItem *top_item = [[NSMenuItem alloc] init];
NSMenu *submenu = [[NSMenu alloc] initWithTitle:
[NSString stringWithUTF8String:menus[i].label]];
for (int32_t j = 0; j < menus[i].item_count; j++) {
for (S32 j = 0; j < menus[i].item_count; j++) {
PlatformMenuItem *item = &menus[i].items[j];
if (!item->label) {
[submenu addItem:[NSMenuItem separatorItem]];
@@ -379,8 +379,8 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu
[NSApp setMainMenu:menu_bar];
}
int32_t platform_poll_menu_command(PlatformWindow *window) {
int32_t cmd = window->pending_menu_cmd;
S32 platform_poll_menu_command(PlatformWindow *window) {
S32 cmd = window->pending_menu_cmd;
window->pending_menu_cmd = 0;
return cmd;
}

View File

@@ -8,10 +8,10 @@
struct PlatformWindow {
HWND hwnd;
bool should_close;
int32_t width;
int32_t height;
int32_t pending_menu_cmd;
B32 should_close;
S32 width;
S32 height;
S32 pending_menu_cmd;
PlatformFrameCallback frame_callback;
void *frame_callback_user_data;
PlatformInput input;
@@ -25,8 +25,8 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
switch (msg) {
case WM_SIZE:
if (g_current_window && wparam != SIZE_MINIMIZED) {
g_current_window->width = (int32_t)LOWORD(lparam);
g_current_window->height = (int32_t)HIWORD(lparam);
g_current_window->width = (S32)LOWORD(lparam);
g_current_window->height = (S32)HIWORD(lparam);
// Render a frame during the modal resize loop so the UI
// stays responsive instead of showing a stretched image.
if (g_current_window->frame_callback) {
@@ -38,7 +38,7 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
if (g_current_window && wparam >= 32 && wparam < 0xFFFF) {
PlatformInput *ev = &g_current_window->input;
if (ev->char_count < PLATFORM_MAX_CHARS_PER_FRAME)
ev->chars[ev->char_count++] = (uint16_t)wparam;
ev->chars[ev->char_count++] = (U16)wparam;
}
return 0;
case WM_KEYDOWN:
@@ -46,20 +46,20 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
if (g_current_window) {
PlatformInput *ev = &g_current_window->input;
if (ev->key_count < PLATFORM_MAX_KEYS_PER_FRAME)
ev->keys[ev->key_count++] = (uint8_t)wparam;
ev->keys[ev->key_count++] = (U8)wparam;
ev->ctrl_held = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
ev->shift_held = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
}
break; // fall through to DefWindowProc for system keys
case WM_MOUSEWHEEL:
if (g_current_window) {
int16_t wheel_delta = (int16_t)HIWORD(wparam);
S16 wheel_delta = (S16)HIWORD(wparam);
g_current_window->input.scroll_delta.y += (F32)wheel_delta / (F32)WHEEL_DELTA * 6.0f;
}
return 0;
case WM_COMMAND:
if (g_current_window && HIWORD(wparam) == 0)
g_current_window->pending_menu_cmd = (int32_t)LOWORD(wparam);
g_current_window->pending_menu_cmd = (S32)LOWORD(wparam);
return 0;
case WM_SETCURSOR:
// When the cursor is in our client area, use the app-set cursor.
@@ -146,7 +146,7 @@ void platform_destroy_window(PlatformWindow *window) {
delete window;
}
bool platform_poll_events(PlatformWindow *window) {
B32 platform_poll_events(PlatformWindow *window) {
MSG msg;
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
@@ -158,7 +158,7 @@ bool platform_poll_events(PlatformWindow *window) {
return !window->should_close;
}
void platform_get_size(PlatformWindow *window, int32_t *w, int32_t *h) {
void platform_get_size(PlatformWindow *window, S32 *w, S32 *h) {
if (w) *w = window->width;
if (h) *h = window->height;
}
@@ -172,13 +172,13 @@ void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback c
window->frame_callback_user_data = user_data;
}
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu_count) {
void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, S32 menu_count) {
HMENU menu_bar = CreateMenu();
for (int32_t i = 0; i < menu_count; i++) {
for (S32 i = 0; i < menu_count; i++) {
HMENU submenu = CreatePopupMenu();
for (int32_t j = 0; j < menus[i].item_count; j++) {
for (S32 j = 0; j < menus[i].item_count; j++) {
PlatformMenuItem *item = &menus[i].items[j];
if (!item->label) {
AppendMenuW(submenu, MF_SEPARATOR, 0, nullptr);
@@ -201,8 +201,8 @@ void platform_set_menu(PlatformWindow *window, PlatformMenu *menus, int32_t menu
SetMenu(window->hwnd, menu_bar);
}
int32_t platform_poll_menu_command(PlatformWindow *window) {
int32_t cmd = window->pending_menu_cmd;
S32 platform_poll_menu_command(PlatformWindow *window) {
S32 cmd = window->pending_menu_cmd;
window->pending_menu_cmd = 0;
return cmd;
}
@@ -236,7 +236,7 @@ void platform_set_cursor(PlatformCursor cursor) {
void platform_clipboard_set(const char *text) {
if (!text) return;
int len = (int)strlen(text);
int len = (S32)strlen(text);
if (len == 0) return;
// Convert UTF-8 to wide string for Windows clipboard

View File

@@ -1,31 +1,30 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "base/base_core.h"
struct Renderer;
struct Clay_RenderCommandArray;
struct RendererDesc {
void *window_handle = nullptr;
int32_t width = 1280;
int32_t height = 720;
int32_t frame_count = 2;
S32 width = 1280;
S32 height = 720;
S32 frame_count = 2;
};
Renderer *renderer_create(RendererDesc *desc);
void renderer_destroy(Renderer *renderer);
bool renderer_begin_frame(Renderer *renderer);
B32 renderer_begin_frame(Renderer *renderer);
void renderer_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands);
void renderer_resize(Renderer *renderer, int32_t width, int32_t height);
void renderer_set_font_scale(Renderer *renderer, float scale);
void renderer_set_clear_color(Renderer *renderer, float r, float g, float b);
void renderer_resize(Renderer *renderer, S32 width, S32 height);
void renderer_set_font_scale(Renderer *renderer, F32 scale);
void renderer_set_clear_color(Renderer *renderer, F32 r, F32 g, F32 b);
// Text measurement callback compatible with UI_MeasureTextFn
// Measures text of given length (NOT necessarily null-terminated) at font_size pixels.
// user_data should be the Renderer pointer.
struct Vec2F32;
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data);
Vec2F32 renderer_measure_text(const char *text, S32 length, F32 font_size, void *user_data);
// Upload an RGBA8 icon atlas texture for icon rendering (4 bytes per pixel)
void renderer_create_icon_atlas(Renderer *renderer, const uint8_t *data, int32_t w, int32_t h);
void renderer_create_icon_atlas(Renderer *renderer, const U8 *data, S32 w, S32 h);

View File

@@ -170,17 +170,17 @@ struct FrameContext {
struct Renderer {
HWND hwnd;
int32_t width;
int32_t height;
int32_t frame_count;
S32 width;
S32 height;
S32 frame_count;
UINT frame_index;
ID3D12Device *device;
ID3D12CommandQueue *command_queue;
IDXGISwapChain3 *swap_chain;
HANDLE swap_chain_waitable;
bool swap_chain_occluded;
bool tearing_support;
B32 swap_chain_occluded;
B32 tearing_support;
ID3D12DescriptorHeap *rtv_heap;
ID3D12DescriptorHeap *srv_heap;
@@ -198,7 +198,7 @@ struct Renderer {
ID3D12RootSignature *root_signature;
ID3D12PipelineState *pipeline_state;
// Per-frame vertex/index buffers (double-buffered)
// Per-frame vertex/index buffers (F64-buffered)
ID3D12Resource *vertex_buffers[NUM_BACK_BUFFERS];
ID3D12Resource *index_buffers[NUM_BACK_BUFFERS];
void *vb_mapped[NUM_BACK_BUFFERS];
@@ -220,15 +220,15 @@ struct Renderer {
F32 measure_font_size;
// Clear color
float clear_r = 0.12f;
float clear_g = 0.12f;
float clear_b = 0.13f;
F32 clear_r = 0.12f;
F32 clear_g = 0.12f;
F32 clear_b = 0.13f;
};
////////////////////////////////
// DX12 infrastructure
static bool create_device(Renderer *r) {
static B32 create_device(Renderer *r) {
#ifdef DX12_ENABLE_DEBUG_LAYER
ID3D12Debug *debug = nullptr;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
@@ -254,7 +254,7 @@ static bool create_device(Renderer *r) {
return true;
}
static bool create_command_queue(Renderer *r) {
static B32 create_command_queue(Renderer *r) {
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
@@ -262,7 +262,7 @@ static bool create_command_queue(Renderer *r) {
return r->device->CreateCommandQueue(&desc, IID_PPV_ARGS(&r->command_queue)) == S_OK;
}
static bool create_descriptor_heaps(Renderer *r) {
static B32 create_descriptor_heaps(Renderer *r) {
// RTV heap
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
@@ -275,7 +275,7 @@ static bool create_descriptor_heaps(Renderer *r) {
SIZE_T rtv_size = r->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE handle = r->rtv_heap->GetCPUDescriptorHandleForHeapStart();
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
r->rtv_descriptors[i] = handle;
handle.ptr += rtv_size;
}
@@ -294,8 +294,8 @@ static bool create_descriptor_heaps(Renderer *r) {
return true;
}
static bool create_frame_resources(Renderer *r) {
for (int i = 0; i < r->frame_count; i++) {
static B32 create_frame_resources(Renderer *r) {
for (S32 i = 0; i < r->frame_count; i++) {
if (r->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&r->frames[i].command_allocator)) != S_OK)
return false;
@@ -315,7 +315,7 @@ static bool create_frame_resources(Renderer *r) {
return r->fence_event != nullptr;
}
static bool create_swap_chain(Renderer *r) {
static B32 create_swap_chain(Renderer *r) {
DXGI_SWAP_CHAIN_DESC1 sd = {};
sd.BufferCount = NUM_BACK_BUFFERS;
sd.Width = 0;
@@ -413,7 +413,7 @@ static void ensure_measure_font(Renderer *r, F32 font_size) {
if (r->measure_font) DeleteObject(r->measure_font);
r->measure_font = CreateFontW(
-(int)(font_size + 0.5f), 0, 0, 0,
-(S32)(font_size + 0.5f), 0, 0, 0,
FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
@@ -423,18 +423,18 @@ static void ensure_measure_font(Renderer *r, F32 font_size) {
SelectObject(r->measure_dc, r->measure_font);
}
static bool create_font_atlas(Renderer *r, F32 font_size) {
const int SS = 2; // supersample factor
static B32 create_font_atlas(Renderer *r, F32 font_size) {
const S32 SS = 2; // supersample factor
F32 render_size = font_size * SS;
int render_w = FONT_ATLAS_W * SS;
int render_h = FONT_ATLAS_H * SS;
S32 render_w = FONT_ATLAS_W * SS;
S32 render_h = FONT_ATLAS_H * SS;
r->font_atlas_size = font_size;
// Create a GDI bitmap to render glyphs at supersampled resolution
HDC dc = CreateCompatibleDC(nullptr);
HFONT font = CreateFontW(
-(int)(render_size + 0.5f), 0, 0, 0,
-(S32)(render_size + 0.5f), 0, 0, 0,
FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
@@ -468,16 +468,16 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
SetBkMode(dc, TRANSPARENT);
// Render each glyph at supersampled resolution
int pen_x = SS, pen_y = SS;
int row_height = 0;
S32 pen_x = SS, pen_y = SS;
S32 row_height = 0;
for (int i = 0; i < GLYPH_COUNT; i++) {
for (S32 i = 0; i < GLYPH_COUNT; i++) {
char ch = (char)(GLYPH_FIRST + i);
SIZE ch_size = {};
GetTextExtentPoint32A(dc, &ch, 1, &ch_size);
int gw = ch_size.cx + 2 * SS; // padding scaled by SS
int gh = ch_size.cy + 2 * SS;
S32 gw = ch_size.cx + 2 * SS; // padding scaled by SS
S32 gh = ch_size.cy + 2 * SS;
if (pen_x + gw >= render_w) {
pen_x = SS;
@@ -507,16 +507,16 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
// Box-filter downsample from supersampled resolution to atlas resolution
U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
U8 *src = (U8 *)dib_bits;
for (int y = 0; y < FONT_ATLAS_H; y++) {
for (int x = 0; x < FONT_ATLAS_W; x++) {
int sum = 0;
for (int sy = 0; sy < SS; sy++) {
for (int sx = 0; sx < SS; sx++) {
int src_idx = ((y * SS + sy) * render_w + (x * SS + sx)) * 4;
for (S32 y = 0; y < FONT_ATLAS_H; y++) {
for (S32 x = 0; x < FONT_ATLAS_W; x++) {
S32 sum = 0;
for (S32 sy = 0; sy < SS; sy++) {
for (S32 sx = 0; sx < SS; sx++) {
S32 src_idx = ((y * SS + sy) * render_w + (x * SS + sx)) * 4;
sum += src[src_idx + 2]; // R channel from BGRA
}
}
float a = (float)sum / (float)(SS * SS * 255);
F32 a = (F32)sum / (F32)(SS * SS * 255);
a = powf(a, 0.55f);
atlas_data[y * FONT_ATLAS_W + x] = (U8)(a * 255.0f + 0.5f);
}
@@ -573,7 +573,7 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
D3D12_RANGE read_range = {0, 0};
upload_buf->Map(0, &read_range, &mapped);
U8 *dst = (U8 *)mapped;
for (int y = 0; y < FONT_ATLAS_H; y++) {
for (S32 y = 0; y < FONT_ATLAS_H; y++) {
memcpy(dst + y * footprint.Footprint.RowPitch,
atlas_data + y * FONT_ATLAS_W,
FONT_ATLAS_W);
@@ -631,7 +631,7 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
////////////////////////////////
// UI rendering pipeline setup
static bool create_ui_pipeline(Renderer *r) {
static B32 create_ui_pipeline(Renderer *r) {
ID3DBlob *vs_blob = nullptr;
ID3DBlob *ps_blob = nullptr;
ID3DBlob *error_blob = nullptr;
@@ -769,11 +769,11 @@ static bool create_ui_pipeline(Renderer *r) {
return SUCCEEDED(hr);
}
static bool create_ui_buffers(Renderer *r) {
static B32 create_ui_buffers(Renderer *r) {
D3D12_HEAP_PROPERTIES heap_props = {};
heap_props.Type = D3D12_HEAP_TYPE_UPLOAD;
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
D3D12_RESOURCE_DESC buf_desc = {};
buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buf_desc.Width = MAX_VERTICES * sizeof(UIVertex);
@@ -792,7 +792,7 @@ static bool create_ui_buffers(Renderer *r) {
r->vertex_buffers[i]->Map(0, &read_range, &r->vb_mapped[i]);
}
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
D3D12_RESOURCE_DESC buf_desc = {};
buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buf_desc.Width = MAX_INDICES * sizeof(U32);
@@ -817,7 +817,7 @@ static bool create_ui_buffers(Renderer *r) {
////////////////////////////////
// Text measurement callback for UI system
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data) {
Vec2F32 renderer_measure_text(const char *text, S32 length, F32 font_size, void *user_data) {
Renderer *r = (Renderer *)user_data;
if (!r || length == 0) return v2f32(0, font_size);
@@ -839,12 +839,12 @@ struct DrawBatch {
};
static void emit_quad(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float u0, float v0, float u1, float v1,
float cr, float cg, float cb, float ca,
float rmin_x, float rmin_y, float rmax_x, float rmax_y,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float border_thickness, float softness, float mode)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 u0, F32 v0, F32 u1, F32 v1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 border_thickness, F32 softness, F32 mode)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -853,9 +853,9 @@ static void emit_quad(DrawBatch *batch,
UIVertex *v = &batch->vertices[base];
// For SDF mode, expand quad slightly for anti-aliasing
float px0 = x0, py0 = y0, px1 = x1, py1 = y1;
F32 px0 = x0, py0 = y0, px1 = x1, py1 = y1;
if (mode < 0.5f) {
float pad = softness + 1.0f;
F32 pad = softness + 1.0f;
px0 -= pad; py0 -= pad; px1 += pad; py1 += pad;
}
@@ -864,7 +864,7 @@ static void emit_quad(DrawBatch *batch,
v[2].pos[0] = px1; v[2].pos[1] = py1; v[2].uv[0] = u1; v[2].uv[1] = v1;
v[3].pos[0] = px0; v[3].pos[1] = py1; v[3].uv[0] = u0; v[3].uv[1] = v1;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
v[i].rect_min[0] = rmin_x; v[i].rect_min[1] = rmin_y;
v[i].rect_max[0] = rmax_x; v[i].rect_max[1] = rmax_y;
@@ -884,10 +884,10 @@ static void emit_quad(DrawBatch *batch,
}
static void emit_quad_rotated(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float u0, float v0, float u1, float v1,
float cr, float cg, float cb, float ca,
float angle_rad)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 u0, F32 v0, F32 u1, F32 v1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 angle_rad)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -895,13 +895,13 @@ static void emit_quad_rotated(DrawBatch *batch,
U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base];
float cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f;
float cosA = cosf(angle_rad);
float sinA = sinf(angle_rad);
F32 cx = (x0 + x1) * 0.5f;
F32 cy = (y0 + y1) * 0.5f;
F32 cosA = cosf(angle_rad);
F32 sinA = sinf(angle_rad);
float dx0 = x0 - cx, dy0 = y0 - cy;
float dx1 = x1 - cx, dy1 = y1 - cy;
F32 dx0 = x0 - cx, dy0 = y0 - cy;
F32 dx1 = x1 - cx, dy1 = y1 - cy;
v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA;
v[0].pos[1] = cy + dx0 * sinA + dy0 * cosA;
@@ -919,7 +919,7 @@ static void emit_quad_rotated(DrawBatch *batch,
v[3].pos[1] = cy + dx0 * sinA + dy1 * cosA;
v[3].uv[0] = u0; v[3].uv[1] = v1;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
v[i].rect_min[0] = 0; v[i].rect_min[1] = 0;
v[i].rect_max[0] = 0; v[i].rect_max[1] = 0;
@@ -939,10 +939,10 @@ static void emit_quad_rotated(DrawBatch *batch,
}
static void emit_rect(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float cr, float cg, float cb, float ca,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float border_thickness, float softness)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 border_thickness, F32 softness)
{
emit_quad(batch, x0, y0, x1, y1,
0, 0, 0, 0,
@@ -953,11 +953,11 @@ static void emit_rect(DrawBatch *batch,
}
static void emit_rect_vgradient(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float tr, float tg, float tb, float ta,
float br, float bg, float bb_, float ba,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float softness)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 tr, F32 tg, F32 tb, F32 ta,
F32 br, F32 bg, F32 bb_, F32 ba,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 softness)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -965,8 +965,8 @@ static void emit_rect_vgradient(DrawBatch *batch,
U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base];
float pad = softness + 1.0f;
float px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad;
F32 pad = softness + 1.0f;
F32 px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad;
v[0].pos[0] = px0; v[0].pos[1] = py0; v[0].uv[0] = 0; v[0].uv[1] = 0;
v[1].pos[0] = px1; v[1].pos[1] = py0; v[1].uv[0] = 0; v[1].uv[1] = 0;
@@ -979,7 +979,7 @@ static void emit_rect_vgradient(DrawBatch *batch,
v[2].col[0] = br; v[2].col[1] = bg; v[2].col[2] = bb_; v[2].col[3] = ba;
v[3].col[0] = br; v[3].col[1] = bg; v[3].col[2] = bb_; v[3].col[3] = ba;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].rect_min[0] = x0; v[i].rect_min[1] = y0;
v[i].rect_max[0] = x1; v[i].rect_max[1] = y1;
v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr;
@@ -998,16 +998,16 @@ static void emit_rect_vgradient(DrawBatch *batch,
}
static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
Clay_BoundingBox bbox, Clay_Color color, const char *text, int32_t text_len,
uint16_t font_size)
Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len,
U16 font_size)
{
if (text_len == 0 || color.a < 0.1f) return;
// Color is 0-255 in Clay convention, normalize to 0-1
float cr = color.r / 255.f;
float cg = color.g / 255.f;
float cb = color.b / 255.f;
float ca = color.a / 255.f;
F32 cr = color.r / 255.f;
F32 cg = color.g / 255.f;
F32 cb = color.b / 255.f;
F32 ca = color.a / 255.f;
F32 scale = (F32)font_size / r->font_atlas_size;
F32 text_h = r->font_line_height * scale;
@@ -1016,18 +1016,18 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
F32 x = floorf(bbox.x + 0.5f);
F32 y = floorf(bbox.y + (bbox.height - text_h) * 0.5f + 0.5f);
for (int32_t i = 0; i < text_len; i++) {
for (S32 i = 0; i < text_len; i++) {
char ch = text[i];
if (ch < GLYPH_FIRST || ch > GLYPH_LAST) {
if (ch == ' ') {
int gi = ' ' - GLYPH_FIRST;
S32 gi = ' ' - GLYPH_FIRST;
if (gi >= 0 && gi < GLYPH_COUNT)
x += r->glyphs[gi].x_advance * scale;
continue;
}
ch = '?';
}
int gi = ch - GLYPH_FIRST;
S32 gi = ch - GLYPH_FIRST;
if (gi < 0 || gi >= GLYPH_COUNT) continue;
GlyphInfo *g = &r->glyphs[gi];
@@ -1056,7 +1056,7 @@ static void flush_batch(Renderer *r, DrawBatch *batch, UINT buf_idx, U32 *flush_
r->command_list->SetPipelineState(r->pipeline_state);
r->command_list->SetGraphicsRootSignature(r->root_signature);
float constants[4] = { (float)r->width, (float)r->height, 0, 0 };
F32 constants[4] = { (F32)r->width, (F32)r->height, 0, 0 };
r->command_list->SetGraphicsRoot32BitConstants(0, 4, constants, 0);
// Bind texture (font or icon)
@@ -1122,7 +1122,7 @@ void renderer_destroy(Renderer *r) {
wait_for_pending(r);
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
if (r->vertex_buffers[i]) r->vertex_buffers[i]->Release();
if (r->index_buffers[i]) r->index_buffers[i]->Release();
}
@@ -1139,7 +1139,7 @@ void renderer_destroy(Renderer *r) {
if (r->swap_chain) { r->swap_chain->SetFullscreenState(false, nullptr); r->swap_chain->Release(); }
if (r->swap_chain_waitable) CloseHandle(r->swap_chain_waitable);
for (int i = 0; i < r->frame_count; i++)
for (S32 i = 0; i < r->frame_count; i++)
if (r->frames[i].command_allocator) r->frames[i].command_allocator->Release();
if (r->command_queue) r->command_queue->Release();
if (r->command_list) r->command_list->Release();
@@ -1160,7 +1160,7 @@ void renderer_destroy(Renderer *r) {
delete r;
}
bool renderer_begin_frame(Renderer *r) {
B32 renderer_begin_frame(Renderer *r) {
if ((r->swap_chain_occluded && r->swap_chain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
|| IsIconic(r->hwnd))
{
@@ -1188,7 +1188,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
r->command_list->ResourceBarrier(1, &barrier);
const float clear_color[4] = { r->clear_r, r->clear_g, r->clear_b, 1.0f };
const F32 clear_color[4] = { r->clear_r, r->clear_g, r->clear_b, 1.0f };
r->command_list->ClearRenderTargetView(r->rtv_descriptors[back_buffer_idx], clear_color, 0, nullptr);
r->command_list->OMSetRenderTargets(1, &r->rtv_descriptors[back_buffer_idx], FALSE, nullptr);
@@ -1210,7 +1210,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
batch.index_count = 0;
// Track which texture is currently bound (0 = font, 1 = icon)
int bound_texture = 0;
S32 bound_texture = 0;
U32 flush_index_start = 0;
auto bind_font = [&]() {
@@ -1228,7 +1228,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
}
};
for (int32_t i = 0; i < render_commands.length; i++) {
for (S32 i = 0; i < render_commands.length; i++) {
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
Clay_BoundingBox bb = cmd->boundingBox;
@@ -1247,15 +1247,15 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *border = &cmd->renderData.border;
Clay_Color c = border->color;
float cr_norm = c.r / 255.f;
float cg_norm = c.g / 255.f;
float cb_norm = c.b / 255.f;
float ca_norm = c.a / 255.f;
F32 cr_norm = c.r / 255.f;
F32 cg_norm = c.g / 255.f;
F32 cb_norm = c.b / 255.f;
F32 ca_norm = c.a / 255.f;
// Use SDF rounded border when corner radius is present and widths are uniform
Clay_CornerRadius cr = border->cornerRadius;
bool has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
bool uniform = border->width.top == border->width.bottom &&
B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
B32 uniform = border->width.top == border->width.bottom &&
border->width.top == border->width.left &&
border->width.top == border->width.right &&
border->width.top > 0;
@@ -1265,7 +1265,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
cr_norm, cg_norm, cb_norm, ca_norm,
cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft,
(float)border->width.top, 1.0f);
(F32)border->width.top, 1.0f);
} else {
if (border->width.top > 0) {
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top,
@@ -1335,8 +1335,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bind_icon();
CustomIconData *icon = (CustomIconData *)custom->customData;
Clay_Color c = icon->color;
float cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f;
F32 cr = c.r / 255.f, cg = c.g / 255.f;
F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[icon->icon_id];
emit_quad(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
@@ -1349,8 +1349,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bind_icon();
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
Clay_Color c = ri->color;
float cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f;
F32 cr = c.r / 255.f, cg = c.g / 255.f;
F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[ri->icon_id];
emit_quad_rotated(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
@@ -1386,7 +1386,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
r->frame_index++;
}
void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int32_t h) {
void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
// Create texture resource
D3D12_HEAP_PROPERTIES heap_props = {};
heap_props.Type = D3D12_HEAP_TYPE_DEFAULT;
@@ -1431,7 +1431,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
D3D12_RANGE read_range = {0, 0};
upload_buf->Map(0, &read_range, &mapped);
U8 *dst = (U8 *)mapped;
for (int y = 0; y < h; y++) {
for (S32 y = 0; y < h; y++) {
memcpy(dst + y * footprint.Footprint.RowPitch, data + y * w * 4, w * 4);
}
upload_buf->Unmap(0, nullptr);
@@ -1481,7 +1481,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
&srv_view, r->icon_srv_heap->GetCPUDescriptorHandleForHeapStart());
}
void renderer_resize(Renderer *r, int32_t width, int32_t height) {
void renderer_resize(Renderer *r, S32 width, S32 height) {
if (width <= 0 || height <= 0) return;
wait_for_pending(r);
@@ -1497,14 +1497,14 @@ void renderer_resize(Renderer *r, int32_t width, int32_t height) {
r->height = height;
}
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
void renderer_set_clear_color(Renderer *r, F32 cr, F32 cg, F32 cb) {
r->clear_r = cr;
r->clear_g = cg;
r->clear_b = cb;
}
void renderer_set_font_scale(Renderer *r, float scale) {
float target_size = 15.0f * scale;
void renderer_set_font_scale(Renderer *r, F32 scale) {
F32 target_size = 15.0f * scale;
if (fabsf(target_size - r->font_atlas_size) < 0.1f) return;
wait_for_pending(r);
if (r->font_texture) { r->font_texture->Release(); r->font_texture = nullptr; }

View File

@@ -146,10 +146,10 @@ fragment float4 fragment_main(Fragment in [[stage_in]],
// Renderer struct
struct Renderer {
int32_t width;
int32_t height;
int32_t frame_count;
uint32_t frame_index;
S32 width;
S32 height;
S32 frame_count;
U32 frame_index;
F32 backing_scale;
id<MTLDevice> device;
@@ -187,11 +187,11 @@ struct Renderer {
////////////////////////////////
// Font atlas (Core Text + CoreGraphics)
static bool create_font_atlas(Renderer *r, F32 font_size) {
const int SS = 2;
static B32 create_font_atlas(Renderer *r, F32 font_size) {
const S32 SS = 2;
F32 render_size = font_size * SS;
int render_w = FONT_ATLAS_W * SS;
int render_h = FONT_ATLAS_H * SS;
S32 render_w = FONT_ATLAS_W * SS;
S32 render_h = FONT_ATLAS_H * SS;
r->font_atlas_size = font_size;
@@ -228,23 +228,23 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
CGContextSetTextMatrix(ctx, CGAffineTransformMake(1, 0, 0, -1, 0, 0));
// Render each glyph
int pen_x = SS, pen_y = SS;
int row_height = 0;
S32 pen_x = SS, pen_y = SS;
S32 row_height = 0;
NSDictionary *attrs = @{
(id)kCTFontAttributeName: (__bridge id)font,
(id)kCTForegroundColorFromContextAttributeName: @YES
};
for (int i = 0; i < GLYPH_COUNT; i++) {
for (S32 i = 0; i < GLYPH_COUNT; i++) {
char ch = (char)(GLYPH_FIRST + i);
NSString *str = [[NSString alloc] initWithBytes:&ch length:1 encoding:NSASCIIStringEncoding];
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)astr);
CGRect bounds = CTLineGetBoundsWithOptions(line, 0);
int gw = (int)ceilf((float)bounds.size.width) + 2 * SS;
int gh = (int)ceilf((float)(ascent + descent)) + 2 * SS;
S32 gw = (S32)ceilf((F32)bounds.size.width) + 2 * SS;
S32 gh = (S32)ceilf((F32)(ascent + descent)) + 2 * SS;
if (pen_x + gw >= render_w) {
pen_x = SS;
@@ -252,7 +252,7 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
row_height = 0;
}
if (pen_y + gh >= render_h) { CFRelease(line); break; }
if (pen_y + gh >= render_h) { CFRelease(line); [astr release]; [str release]; break; }
// Context is flipped to top-left origin. Position baseline:
// pen_y + SS is the top of the glyph cell, baseline is ascent below that.
@@ -275,24 +275,26 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
pen_x += gw + SS;
CFRelease(line);
[astr release];
[str release];
}
// Box-filter downsample (context is flipped, so bitmap is already top-down)
uint8_t *src = (uint8_t *)CGBitmapContextGetData(ctx);
uint8_t *atlas_data = (uint8_t *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
U8 *src = (U8 *)CGBitmapContextGetData(ctx);
U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
for (int y = 0; y < FONT_ATLAS_H; y++) {
for (int x = 0; x < FONT_ATLAS_W; x++) {
int sum = 0;
for (int sy = 0; sy < SS; sy++) {
for (int sx = 0; sx < SS; sx++) {
int src_idx = (y * SS + sy) * render_w + (x * SS + sx);
for (S32 y = 0; y < FONT_ATLAS_H; y++) {
for (S32 x = 0; x < FONT_ATLAS_W; x++) {
S32 sum = 0;
for (S32 sy = 0; sy < SS; sy++) {
for (S32 sx = 0; sx < SS; sx++) {
S32 src_idx = (y * SS + sy) * render_w + (x * SS + sx);
sum += src[src_idx];
}
}
float a = (float)sum / (float)(SS * SS * 255);
F32 a = (F32)sum / (F32)(SS * SS * 255);
a = powf(a, 0.55f);
atlas_data[y * FONT_ATLAS_W + x] = (uint8_t)(a * 255.0f + 0.5f);
atlas_data[y * FONT_ATLAS_W + x] = (U8)(a * 255.0f + 0.5f);
}
}
@@ -335,23 +337,29 @@ static void ensure_measure_font(Renderer *r, F32 font_size) {
r->measure_font_size = font_size;
}
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data) {
Vec2F32 renderer_measure_text(const char *text, S32 length, F32 font_size, void *user_data) {
Renderer *r = (Renderer *)user_data;
if (!r || length == 0) return v2f32(0, font_size);
ensure_measure_font(r, font_size);
NSString *str = [[NSString alloc] initWithBytes:text length:length encoding:NSUTF8StringEncoding];
if (!str) return v2f32(0, font_size);
Vec2F32 result = v2f32(0, font_size);
@autoreleasepool {
NSString *str = [[NSString alloc] initWithBytes:text length:length encoding:NSUTF8StringEncoding];
if (!str) return result;
NSDictionary *attrs = @{ (id)kCTFontAttributeName: (__bridge id)r->measure_font };
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)astr);
NSDictionary *attrs = @{ (id)kCTFontAttributeName: (__bridge id)r->measure_font };
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)astr);
CGRect bounds = CTLineGetBoundsWithOptions(line, 0);
CFRelease(line);
CGRect bounds = CTLineGetBoundsWithOptions(line, 0);
CFRelease(line);
[astr release];
[str release];
return v2f32((F32)bounds.size.width, (F32)bounds.size.height);
result = v2f32((F32)bounds.size.width, (F32)bounds.size.height);
}
return result;
}
////////////////////////////////
@@ -365,12 +373,12 @@ struct DrawBatch {
};
static void emit_quad(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float u0, float v0, float u1, float v1,
float cr, float cg, float cb, float ca,
float rmin_x, float rmin_y, float rmax_x, float rmax_y,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float border_thickness, float softness, float mode)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 u0, F32 v0, F32 u1, F32 v1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 border_thickness, F32 softness, F32 mode)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -378,9 +386,9 @@ static void emit_quad(DrawBatch *batch,
U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base];
float px0 = x0, py0 = y0, px1 = x1, py1 = y1;
F32 px0 = x0, py0 = y0, px1 = x1, py1 = y1;
if (mode < 0.5f) {
float pad = softness + 1.0f;
F32 pad = softness + 1.0f;
px0 -= pad; py0 -= pad; px1 += pad; py1 += pad;
}
@@ -389,7 +397,7 @@ static void emit_quad(DrawBatch *batch,
v[2].pos[0] = px1; v[2].pos[1] = py1; v[2].uv[0] = u1; v[2].uv[1] = v1;
v[3].pos[0] = px0; v[3].pos[1] = py1; v[3].uv[0] = u0; v[3].uv[1] = v1;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
v[i].rect_min[0] = rmin_x; v[i].rect_min[1] = rmin_y;
v[i].rect_max[0] = rmax_x; v[i].rect_max[1] = rmax_y;
@@ -409,10 +417,10 @@ static void emit_quad(DrawBatch *batch,
}
static void emit_quad_rotated(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float u0, float v0, float u1, float v1,
float cr, float cg, float cb, float ca,
float angle_rad)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 u0, F32 v0, F32 u1, F32 v1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 angle_rad)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -420,14 +428,14 @@ static void emit_quad_rotated(DrawBatch *batch,
U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base];
float cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f;
float cosA = cosf(angle_rad);
float sinA = sinf(angle_rad);
F32 cx = (x0 + x1) * 0.5f;
F32 cy = (y0 + y1) * 0.5f;
F32 cosA = cosf(angle_rad);
F32 sinA = sinf(angle_rad);
// Corner offsets from center
float dx0 = x0 - cx, dy0 = y0 - cy;
float dx1 = x1 - cx, dy1 = y1 - cy;
F32 dx0 = x0 - cx, dy0 = y0 - cy;
F32 dx1 = x1 - cx, dy1 = y1 - cy;
// Rotate each corner around center
v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA;
@@ -446,7 +454,7 @@ static void emit_quad_rotated(DrawBatch *batch,
v[3].pos[1] = cy + dx0 * sinA + dy1 * cosA;
v[3].uv[0] = u0; v[3].uv[1] = v1;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].col[0] = cr; v[i].col[1] = cg; v[i].col[2] = cb; v[i].col[3] = ca;
v[i].rect_min[0] = 0; v[i].rect_min[1] = 0;
v[i].rect_max[0] = 0; v[i].rect_max[1] = 0;
@@ -466,10 +474,10 @@ static void emit_quad_rotated(DrawBatch *batch,
}
static void emit_rect(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float cr, float cg, float cb, float ca,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float border_thickness, float softness)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 cr, F32 cg, F32 cb, F32 ca,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 border_thickness, F32 softness)
{
emit_quad(batch, x0, y0, x1, y1,
0, 0, 0, 0,
@@ -480,11 +488,11 @@ static void emit_rect(DrawBatch *batch,
}
static void emit_rect_vgradient(DrawBatch *batch,
float x0, float y0, float x1, float y1,
float tr, float tg, float tb, float ta,
float br, float bg, float bb_, float ba,
float cr_tl, float cr_tr, float cr_br, float cr_bl,
float softness)
F32 x0, F32 y0, F32 x1, F32 y1,
F32 tr, F32 tg, F32 tb, F32 ta,
F32 br, F32 bg, F32 bb_, F32 ba,
F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
F32 softness)
{
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return;
@@ -492,8 +500,8 @@ static void emit_rect_vgradient(DrawBatch *batch,
U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base];
float pad = softness + 1.0f;
float px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad;
F32 pad = softness + 1.0f;
F32 px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad;
v[0].pos[0] = px0; v[0].pos[1] = py0; v[0].uv[0] = 0; v[0].uv[1] = 0;
v[1].pos[0] = px1; v[1].pos[1] = py0; v[1].uv[0] = 0; v[1].uv[1] = 0;
@@ -505,7 +513,7 @@ static void emit_rect_vgradient(DrawBatch *batch,
v[2].col[0] = br; v[2].col[1] = bg; v[2].col[2] = bb_; v[2].col[3] = ba;
v[3].col[0] = br; v[3].col[1] = bg; v[3].col[2] = bb_; v[3].col[3] = ba;
for (int i = 0; i < 4; i++) {
for (S32 i = 0; i < 4; i++) {
v[i].rect_min[0] = x0; v[i].rect_min[1] = y0;
v[i].rect_max[0] = x1; v[i].rect_max[1] = y1;
v[i].corner_radii[0] = cr_tl; v[i].corner_radii[1] = cr_tr;
@@ -524,15 +532,15 @@ static void emit_rect_vgradient(DrawBatch *batch,
}
static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
Clay_BoundingBox bbox, Clay_Color color, const char *text, int32_t text_len,
uint16_t font_size)
Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len,
U16 font_size)
{
if (text_len == 0 || color.a < 0.1f) return;
float cr = color.r / 255.f;
float cg = color.g / 255.f;
float cb = color.b / 255.f;
float ca = color.a / 255.f;
F32 cr = color.r / 255.f;
F32 cg = color.g / 255.f;
F32 cb = color.b / 255.f;
F32 ca = color.a / 255.f;
F32 scale = (F32)font_size / r->font_atlas_size;
F32 text_h = r->font_line_height * scale;
@@ -540,18 +548,18 @@ static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
F32 x = floorf(bbox.x + 0.5f);
F32 y = floorf(bbox.y + (bbox.height - text_h) * 0.5f + 0.5f);
for (int32_t i = 0; i < text_len; i++) {
for (S32 i = 0; i < text_len; i++) {
char ch = text[i];
if (ch < GLYPH_FIRST || ch > GLYPH_LAST) {
if (ch == ' ') {
int gi = ' ' - GLYPH_FIRST;
S32 gi = ' ' - GLYPH_FIRST;
if (gi >= 0 && gi < GLYPH_COUNT)
x += r->glyphs[gi].x_advance * scale;
continue;
}
ch = '?';
}
int gi = ch - GLYPH_FIRST;
S32 gi = ch - GLYPH_FIRST;
if (gi < 0 || gi >= GLYPH_COUNT) continue;
GlyphInfo *g = &r->glyphs[gi];
@@ -595,6 +603,7 @@ Renderer *renderer_create(RendererDesc *desc) {
layer.device = r->device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.framebufferOnly = YES;
layer.maximumDrawableCount = NUM_BACK_BUFFERS;
NSWindow *window = [view window];
r->backing_scale = (F32)[window backingScaleFactor];
@@ -672,8 +681,8 @@ Renderer *renderer_create(RendererDesc *desc) {
return nullptr;
}
// Create double-buffered vertex/index buffers
for (int i = 0; i < NUM_BACK_BUFFERS; i++) {
// Create F64-buffered vertex/index buffers
for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
r->vertex_buffers[i] = [r->device newBufferWithLength:MAX_VERTICES * sizeof(UIVertex)
options:MTLResourceStorageModeShared];
r->index_buffers[i] = [r->device newBufferWithLength:MAX_INDICES * sizeof(U32)
@@ -704,14 +713,16 @@ void renderer_destroy(Renderer *r) {
delete r;
}
bool renderer_begin_frame(Renderer *r) {
B32 renderer_begin_frame(Renderer *r) {
if (r->width <= 0 || r->height <= 0) return false;
// Wait for an in-flight frame to finish, then acquire a drawable.
// Doing this BEFORE input sampling ensures the freshest mouse position
// is used for rendering, reducing perceived drag latency.
dispatch_semaphore_wait(r->frame_semaphore, DISPATCH_TIME_FOREVER);
r->current_drawable = [r->metal_layer nextDrawable];
@autoreleasepool {
r->current_drawable = [[r->metal_layer nextDrawable] retain];
}
if (!r->current_drawable) {
dispatch_semaphore_signal(r->frame_semaphore);
return false;
@@ -724,7 +735,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
id<CAMetalDrawable> drawable = r->current_drawable;
r->current_drawable = nil;
uint32_t buf_idx = r->frame_index % NUM_BACK_BUFFERS;
U32 buf_idx = r->frame_index % NUM_BACK_BUFFERS;
MTLRenderPassDescriptor *pass = [MTLRenderPassDescriptor renderPassDescriptor];
pass.colorAttachments[0].texture = drawable.texture;
@@ -741,8 +752,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
// Viewport
MTLViewport viewport = {};
viewport.width = (double)r->width;
viewport.height = (double)r->height;
viewport.width = (F64)r->width;
viewport.height = (F64)r->height;
viewport.zfar = 1.0;
[encoder setViewport:viewport];
@@ -751,7 +762,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
[encoder setScissorRect:full_scissor];
// Constants
float constants[2] = { (float)r->width, (float)r->height };
F32 constants[2] = { (F32)r->width, (F32)r->height };
[encoder setVertexBytes:constants length:sizeof(constants) atIndex:1];
// Process Clay render commands
@@ -763,7 +774,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
batch.index_count = 0;
// Track which texture is currently bound (0 = font, 1 = icon)
int bound_texture = 0;
S32 bound_texture = 0;
U32 flush_index_start = 0;
auto flush_batch = [&]() {
@@ -796,7 +807,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
}
};
for (int32_t i = 0; i < render_commands.length; i++) {
for (S32 i = 0; i < render_commands.length; i++) {
Clay_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
Clay_BoundingBox bb = cmd->boundingBox;
@@ -815,15 +826,15 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *border = &cmd->renderData.border;
Clay_Color c = border->color;
float cr_norm = c.r / 255.f;
float cg_norm = c.g / 255.f;
float cb_norm = c.b / 255.f;
float ca_norm = c.a / 255.f;
F32 cr_norm = c.r / 255.f;
F32 cg_norm = c.g / 255.f;
F32 cb_norm = c.b / 255.f;
F32 ca_norm = c.a / 255.f;
// Use SDF rounded border when corner radius is present and widths are uniform
Clay_CornerRadius cr = border->cornerRadius;
bool has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
bool uniform = border->width.top == border->width.bottom &&
B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
B32 uniform = border->width.top == border->width.bottom &&
border->width.top == border->width.left &&
border->width.top == border->width.right &&
border->width.top > 0;
@@ -833,7 +844,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
cr_norm, cg_norm, cb_norm, ca_norm,
cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft,
(float)border->width.top, 1.0f);
(F32)border->width.top, 1.0f);
} else {
if (border->width.top > 0) {
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top,
@@ -899,8 +910,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bind_icon_texture();
CustomIconData *icon = (CustomIconData *)custom->customData;
Clay_Color c = icon->color;
float cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f;
F32 cr = c.r / 255.f, cg = c.g / 255.f;
F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[icon->icon_id];
emit_quad(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
@@ -913,8 +924,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
bind_icon_texture();
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
Clay_Color c = ri->color;
float cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f;
F32 cr = c.r / 255.f, cg = c.g / 255.f;
F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[ri->icon_id];
emit_quad_rotated(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
@@ -943,12 +954,13 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
}];
[cmd_buf commit];
[drawable release];
}
r->frame_index++;
}
void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int32_t h) {
void renderer_create_icon_atlas(Renderer *r, const U8 *data, S32 w, S32 h) {
MTLTextureDescriptor *tex_desc = [[MTLTextureDescriptor alloc] init];
tex_desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
tex_desc.width = w;
@@ -962,21 +974,23 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
bytesPerRow:w * 4];
}
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
void renderer_set_clear_color(Renderer *r, F32 cr, F32 cg, F32 cb) {
r->clear_r = cr;
r->clear_g = cg;
r->clear_b = cb;
}
void renderer_set_font_scale(Renderer *r, float scale) {
float target_size = 15.0f * scale;
void renderer_set_font_scale(Renderer *r, F32 scale) {
F32 target_size = 15.0f * scale;
if (fabsf(target_size - r->font_atlas_size) < 0.1f) return;
[r->font_texture release];
[r->font_sampler release];
r->font_texture = nil;
r->font_sampler = nil;
create_font_atlas(r, target_size);
}
void renderer_resize(Renderer *r, int32_t width, int32_t height) {
void renderer_resize(Renderer *r, S32 width, S32 height) {
if (width <= 0 || height <= 0) return;
r->width = width;
r->height = height;

View File

@@ -146,7 +146,7 @@ void ui_set_accent(S32 accent_id) {
static void clay_error_handler(Clay_ErrorData error) {
char buf[512];
int len = error.errorText.length < 511 ? error.errorText.length : 511;
S32 len = error.errorText.length < 511 ? error.errorText.length : 511;
memcpy(buf, error.errorText.chars, len);
buf[len] = '\0';
fprintf(stderr, "[Clay Error] %s\n", buf);
@@ -161,7 +161,7 @@ static UI_Context *g_measure_ctx = nullptr;
static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *user_data) {
UI_Context *ctx = (UI_Context *)user_data;
if (!ctx || !ctx->measure_text_fn || text.length == 0) {
return Clay_Dimensions{0, (float)config->fontSize};
return Clay_Dimensions{0, (F32)config->fontSize};
}
Vec2F32 result = ctx->measure_text_fn(text.chars, text.length, (F32)config->fontSize, ctx->measure_text_user_data);
return Clay_Dimensions{result.x, result.y};
@@ -173,7 +173,7 @@ static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElement
UI_Context *ui_create(F32 viewport_w, F32 viewport_h) {
UI_Context *ctx = (UI_Context *)calloc(1, sizeof(UI_Context));
uint32_t min_memory = Clay_MinMemorySize();
U32 min_memory = Clay_MinMemorySize();
ctx->clay_memory = malloc(min_memory);
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(min_memory, ctx->clay_memory);

View File

@@ -92,14 +92,14 @@ void ui_set_accent(S32 accent_id);
extern F32 g_ui_scale;
// Scale a float value (for CLAY_SIZING_FIXED, corner radii, etc.)
static inline float uis(float x) { return x * g_ui_scale; }
// Scale a F32 value (for CLAY_SIZING_FIXED, corner radii, etc.)
static inline F32 uis(F32 x) { return x * g_ui_scale; }
// Scale to uint16_t (for Clay_Padding, childGap, etc.)
static inline uint16_t uip(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); }
// Scale to U16 (for Clay_Padding, childGap, etc.)
static inline U16 uip(F32 x) { return (U16)(x * g_ui_scale + 0.5f); }
// Scale to uint16_t font size
static inline uint16_t uifs(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); }
// Scale to U16 font size
static inline U16 uifs(F32 x) { return (U16)(x * g_ui_scale + 0.5f); }
////////////////////////////////
// Tab styling

View File

@@ -44,7 +44,7 @@ static CustomGradientData *alloc_gradient(Clay_Color top, Clay_Color bottom) {
// Per-frame shadow layer ID counter (each shadow uses N unique IDs)
static S32 g_shadow_id_counter = 0;
// Frame counter for double-click detection
// Frame counter for F64-click detection
static S32 g_frame_number = 0;
// Emit a smooth multi-layer drop shadow as floating rects.
@@ -59,16 +59,16 @@ static S32 g_frame_number = 0;
#define SHADOW_LAYERS 7
static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
float peak_alpha, int16_t z,
B32 attach_to_root, uint32_t parent_id,
F32 peak_alpha, S16 z,
B32 attach_to_root, U32 parent_id,
Clay_FloatingAttachPointType parent_attach) {
if (bb.width <= 0) return;
float per_layer = peak_alpha / (float)SHADOW_LAYERS;
F32 per_layer = peak_alpha / (F32)SHADOW_LAYERS;
// Draw outermost first (largest, lowest alpha contribution), innermost last
for (S32 i = SHADOW_LAYERS - 1; i >= 0; i--) {
float t = (float)(i + 1) / (float)SHADOW_LAYERS; // 1/N .. 1.0
F32 t = (F32)(i + 1) / (F32)SHADOW_LAYERS; // 1/N .. 1.0
F32 expand = radius * t;
S32 sid = g_shadow_id_counter++;
@@ -268,8 +268,8 @@ B32 ui_button(const char *id, const char *text) {
B32 hovered = Clay_PointerOver(eid);
Clay_Color base = hovered ? g_theme.accent_hover : g_theme.accent;
Clay_Color top = {(float)Min((int)base.r+12,255), (float)Min((int)base.g+12,255), (float)Min((int)base.b+12,255), base.a};
Clay_Color bot = {(float)Max((int)base.r-15,0), (float)Max((int)base.g-15,0), (float)Max((int)base.b-15,0), base.a};
Clay_Color top = {(F32)Min((S32)base.r+12,255), (F32)Min((S32)base.g+12,255), (F32)Min((S32)base.b+12,255), base.a};
Clay_Color bot = {(F32)Max((S32)base.r-15,0), (F32)Max((S32)base.g-15,0), (F32)Max((S32)base.b-15,0), base.a};
CustomGradientData *grad = alloc_gradient(top, bot);
CLAY(eid,
@@ -526,7 +526,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
// Key events
for (S32 k = 0; k < g_wstate.input.key_count; k++) {
uint8_t key = g_wstate.input.keys[k];
U8 key = g_wstate.input.keys[k];
// Skip Tab — handled via tab cycling
if (key == PKEY_TAB) continue;
@@ -656,7 +656,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
// Character input (printable only, skip control chars)
if (is_focused) {
for (S32 c = 0; c < g_wstate.input.char_count; c++) {
uint16_t ch = g_wstate.input.chars[c];
U16 ch = g_wstate.input.chars[c];
if (ch >= 32 && ch < 127) {
// Delete selection first if any
if (text_input_has_sel()) {
@@ -726,8 +726,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
Clay_Color border_color = is_focused ? g_theme.accent : g_theme.border;
// Inset effect: darker top, lighter bottom (recessed look)
Clay_Color inset_top = {(float)Max((int)bg.r-8,0), (float)Max((int)bg.g-8,0), (float)Max((int)bg.b-8,0), bg.a};
Clay_Color inset_bot = {(float)Min((int)bg.r+3,255), (float)Min((int)bg.g+3,255), (float)Min((int)bg.b+3,255), bg.a};
Clay_Color inset_top = {(F32)Max((S32)bg.r-8,0), (F32)Max((S32)bg.g-8,0), (F32)Max((S32)bg.b-8,0), bg.a};
Clay_Color inset_bot = {(F32)Min((S32)bg.r+3,255), (F32)Min((S32)bg.g+3,255), (F32)Min((S32)bg.b+3,255), bg.a};
CustomGradientData *inset_grad = alloc_gradient(inset_top, inset_bot);
CLAY(eid,
@@ -820,13 +820,13 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
static char dd_trunc_buf[256];
const char *display_label = current_label;
Clay_ElementId text_eid = WIDI(id, 500);
float avail_w = Clay_GetElementData(text_eid).boundingBox.width;
F32 avail_w = Clay_GetElementData(text_eid).boundingBox.width;
if (avail_w > 0) {
S32 label_len = (S32)strlen(current_label);
Vec2F32 text_size = ui_measure_text(current_label, label_len, FONT_SIZE_NORMAL);
if (text_size.x > avail_w) {
Vec2F32 dots = ui_measure_text("...", 3, FONT_SIZE_NORMAL);
float target_w = avail_w - dots.x;
F32 target_w = avail_w - dots.x;
S32 lo = 0, hi = label_len;
while (lo < hi) {
S32 mid = (lo + hi + 1) / 2;
@@ -845,7 +845,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
Clay_Color bg = is_open ? g_theme.bg_dark : g_theme.bg_medium;
if (header_hovered && !is_open) bg = g_theme.bg_lighter;
Clay_Color dd_top = {(float)Min((int)bg.r+6,255), (float)Min((int)bg.g+6,255), (float)Min((int)bg.b+6,255), bg.a};
Clay_Color dd_top = {(F32)Min((S32)bg.r+6,255), (F32)Min((S32)bg.g+6,255), (F32)Min((S32)bg.b+6,255), bg.a};
CustomGradientData *dd_grad = alloc_gradient(dd_top, bg);
CLAY(eid,
@@ -892,7 +892,7 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
// Draw dropdown list if open (floating so it escapes modal/container clipping)
if (is_open) {
Clay_ElementId list_id = WIDI(id, 502);
float header_width = Clay_GetElementData(eid).boundingBox.width;
F32 header_width = Clay_GetElementData(eid).boundingBox.width;
// Dropdown list shadow
{
@@ -1052,7 +1052,7 @@ S32 ui_modal(const char *id, const char *title, const char *message,
// Title bar (gradient: lighter top)
{
Clay_Color mtb = g_theme.title_bar;
Clay_Color mtb_top = {(float)Min((int)mtb.r+12,255), (float)Min((int)mtb.g+12,255), (float)Min((int)mtb.b+12,255), mtb.a};
Clay_Color mtb_top = {(F32)Min((S32)mtb.r+12,255), (F32)Min((S32)mtb.g+12,255), (F32)Min((S32)mtb.b+12,255), mtb.a};
CustomGradientData *mtb_grad = alloc_gradient(mtb_top, mtb);
CLAY(WIDI(id, 2002),
@@ -1099,8 +1099,8 @@ S32 ui_modal(const char *id, const char *title, const char *message,
B32 btn_hovered = Clay_PointerOver(btn_id);
Clay_Color mbtn_base = btn_hovered ? g_theme.accent_hover : g_theme.accent;
Clay_Color mbtn_top = {(float)Min((int)mbtn_base.r+12,255), (float)Min((int)mbtn_base.g+12,255), (float)Min((int)mbtn_base.b+12,255), mbtn_base.a};
Clay_Color mbtn_bot = {(float)Max((int)mbtn_base.r-15,0), (float)Max((int)mbtn_base.g-15,0), (float)Max((int)mbtn_base.b-15,0), mbtn_base.a};
Clay_Color mbtn_top = {(F32)Min((S32)mbtn_base.r+12,255), (F32)Min((S32)mbtn_base.g+12,255), (F32)Min((S32)mbtn_base.b+12,255), mbtn_base.a};
Clay_Color mbtn_bot = {(F32)Max((S32)mbtn_base.r-15,0), (F32)Max((S32)mbtn_base.g-15,0), (F32)Max((S32)mbtn_base.b-15,0), mbtn_base.a};
CustomGradientData *mbtn_grad = alloc_gradient(mbtn_top, mbtn_bot);
CLAY(btn_id,
@@ -1136,7 +1136,7 @@ S32 ui_modal(const char *id, const char *title, const char *message,
////////////////////////////////
// Draggable window
static UI_WindowSlot *find_or_create_window_slot(uint32_t id, Vec2F32 initial_pos, Vec2F32 initial_size) {
static UI_WindowSlot *find_or_create_window_slot(U32 id, Vec2F32 initial_pos, Vec2F32 initial_size) {
// Look for existing slot
for (S32 i = 0; i < g_wstate.window_count; i++) {
if (g_wstate.windows[i].id == id) {
@@ -1157,25 +1157,25 @@ static UI_WindowSlot *find_or_create_window_slot(uint32_t id, Vec2F32 initial_po
static void bring_window_to_front(UI_WindowSlot *slot) {
// Renormalize if approaching modal z-range
if (g_wstate.next_z > 800) {
int16_t sorted[UI_WIDGET_MAX_WINDOWS];
S16 sorted[UI_WIDGET_MAX_WINDOWS];
S32 count = g_wstate.window_count;
for (S32 i = 0; i < count; i++) sorted[i] = g_wstate.windows[i].z_order;
// Bubble sort (tiny array)
for (S32 i = 0; i < count - 1; i++) {
for (S32 j = i + 1; j < count; j++) {
if (sorted[j] < sorted[i]) {
int16_t tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp;
S16 tmp = sorted[i]; sorted[i] = sorted[j]; sorted[j] = tmp;
}
}
}
for (S32 i = 0; i < count; i++) {
for (S32 j = 0; j < count; j++) {
if (g_wstate.windows[j].z_order == sorted[i]) {
g_wstate.windows[j].z_order = (int16_t)i;
g_wstate.windows[j].z_order = (S16)i;
}
}
}
g_wstate.next_z = (int16_t)count;
g_wstate.next_z = (S16)count;
}
slot->z_order = g_wstate.next_z++;
}
@@ -1233,7 +1233,7 @@ B32 ui_window(const char *id, const char *title, B32 *open,
// Use absolute position since window uses offset-based floating
Clay_BoundingBox shadow_bb = { slot->position.x, slot->position.y, win_bb.width, win_bb.height };
emit_shadow(shadow_bb, uis(3), uis(3), uis(8),
g_theme.shadow.a, (int16_t)(100 + slot->z_order - 1),
g_theme.shadow.a, (S16)(100 + slot->z_order - 1),
1, 0, CLAY_ATTACH_POINT_LEFT_TOP);
}
@@ -1247,7 +1247,7 @@ B32 ui_window(const char *id, const char *title, B32 *open,
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
.floating = {
.offset = { slot->position.x, slot->position.y },
.zIndex = (int16_t)(100 + slot->z_order),
.zIndex = (S16)(100 + slot->z_order),
.attachPoints = {
.element = CLAY_ATTACH_POINT_LEFT_TOP,
.parent = CLAY_ATTACH_POINT_LEFT_TOP,
@@ -1259,7 +1259,7 @@ B32 ui_window(const char *id, const char *title, B32 *open,
) {
// Title bar (gradient: lighter top)
Clay_Color tb = g_theme.title_bar;
Clay_Color tb_top = {(float)Min((int)tb.r+12,255), (float)Min((int)tb.g+12,255), (float)Min((int)tb.b+12,255), tb.a};
Clay_Color tb_top = {(F32)Min((S32)tb.r+12,255), (F32)Min((S32)tb.g+12,255), (F32)Min((S32)tb.b+12,255), tb.a};
CustomGradientData *tb_grad = alloc_gradient(tb_top, tb);
CLAY(title_bar_id,
@@ -1346,7 +1346,7 @@ S32 ui_tab_bar(const char *id, const char **labels, S32 count, S32 *selected) {
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
) {
for (S32 i = 0; i < count; i++) {
Clay_ElementId tab_eid = Clay__HashStringWithOffset(id_str, (uint32_t)i, 0);
Clay_ElementId tab_eid = Clay__HashStringWithOffset(id_str, (U32)i, 0);
B32 is_active = (i == *selected);
B32 hovered = Clay_PointerOver(tab_eid);
@@ -1421,7 +1421,7 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
};
for (S32 k = 0; k < g_wstate.input.key_count; k++) {
uint8_t key = g_wstate.input.keys[k];
U8 key = g_wstate.input.keys[k];
if (ctrl) {
if (key == PKEY_A) {
@@ -1500,7 +1500,7 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
if (!commit && !cancel) {
for (S32 c = 0; c < g_wstate.input.char_count; c++) {
uint16_t ch = g_wstate.input.chars[c];
U16 ch = g_wstate.input.chars[c];
B32 valid = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-';
if (valid) {
if (KE_HAS_SEL()) elen = ke_delete_sel();
@@ -1537,8 +1537,8 @@ static S32 value_edit_process_keys(F32 *value, F32 max_val, B32 is_signed, B32 *
}
// Render the text edit box with selection/cursor display.
static void value_edit_render(uint32_t hash, F32 width) {
Clay_ElementId edit_eid = CLAY_IDI("ValEdit", (int)hash);
static void value_edit_render(U32 hash, F32 width) {
Clay_ElementId edit_eid = CLAY_IDI("ValEdit", (S32)hash);
char *ebuf = g_wstate.knob_edit_buf;
S32 elen = (S32)strlen(ebuf);
@@ -1588,7 +1588,7 @@ static void value_edit_render(uint32_t hash, F32 width) {
S32 n = sel_hi - sel_lo;
memcpy(ke_dbuf_sel, &ebuf[sel_lo], n); ke_dbuf_sel[n] = '\0';
Clay_String s_sel = { .length = n, .chars = ke_dbuf_sel };
CLAY(CLAY_IDI("ValEditSel", (int)hash),
CLAY(CLAY_IDI("ValEditSel", (S32)hash),
.layout = { .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() } },
.backgroundColor = g_theme.accent
) {
@@ -1616,8 +1616,8 @@ static void value_edit_render(uint32_t hash, F32 width) {
}
// Get the Clay element ID of the current edit box (for click-away detection).
static Clay_ElementId value_edit_eid(uint32_t hash) {
return CLAY_IDI("ValEdit", (int)hash);
static Clay_ElementId value_edit_eid(U32 hash) {
return CLAY_IDI("ValEdit", (S32)hash);
}
// Handle click-outside to commit the edit.
@@ -1636,7 +1636,7 @@ static void value_edit_click_away(Clay_ElementId eid, F32 *value, F32 max_val, B
}
// Enter text edit mode for a widget.
static void value_edit_enter(uint32_t hash, F32 value, B32 is_signed) {
static void value_edit_enter(U32 hash, F32 value, B32 is_signed) {
g_wstate.knob_edit_id = hash;
g_wstate.focused_id = 0;
if (is_signed) {
@@ -1684,7 +1684,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
F32 deg_to_rad = 3.14159265f / 180.0f;
F32 angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad;
uint32_t knob_hash = Clay__HashString(clay_str(id), 0).id;
U32 knob_hash = Clay__HashString(clay_str(id), 0).id;
B32 is_editing = (editable && g_wstate.knob_edit_id == knob_hash);
UI_KnobDragState *kd = &g_wstate.knob_drag;
@@ -1732,7 +1732,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
.layoutDirection = CLAY_TOP_TO_BOTTOM,
}
) {
Clay_ElementId knob_eid = CLAY_IDI("KnobBg", (int)knob_hash);
Clay_ElementId knob_eid = CLAY_IDI("KnobBg", (S32)knob_hash);
B32 hovered = Clay_PointerOver(knob_eid);
CLAY(knob_eid,
@@ -1761,7 +1761,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
}
}
// Click: double-click resets, single click starts drag
// Click: F64-click resets, single click starts drag
if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == knob_hash &&
(g_frame_number - kd->last_click_frame) < 20);
@@ -1785,7 +1785,7 @@ B32 ui_knob(const char *id, const char *label, F32 *value, F32 max_val, B32 is_s
value_edit_render(knob_hash, knob_size);
value_edit_click_away(value_edit_eid(knob_hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) {
Clay_ElementId val_eid = CLAY_IDI("KnobVal", (int)knob_hash);
Clay_ElementId val_eid = CLAY_IDI("KnobVal", (S32)knob_hash);
B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text };
@@ -1824,7 +1824,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed);
uint32_t hash = Clay__HashString(clay_str(id), 0).id;
U32 hash = Clay__HashString(clay_str(id), 0).id;
B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag;
@@ -1883,7 +1883,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass thumb travel)
Clay_ElementId hit_eid = CLAY_IDI("SlHHit", (int)hash);
Clay_ElementId hit_eid = CLAY_IDI("SlHHit", (S32)hash);
B32 hovered = Clay_PointerOver(hit_eid);
CLAY(hit_eid,
@@ -1893,7 +1893,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
}
) {
// Visible track (centered inside hit area)
CLAY(CLAY_IDI("SlHTrack", (int)hash),
CLAY(CLAY_IDI("SlHTrack", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) },
},
@@ -1903,7 +1903,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
// Fill bar
F32 fill_w = normalized * track_w;
if (fill_w < 1.0f) fill_w = 1.0f;
CLAY(CLAY_IDI("SlHFill", (int)hash),
CLAY(CLAY_IDI("SlHFill", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(fill_w), .height = CLAY_SIZING_GROW() },
},
@@ -1919,7 +1919,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
idata->icon_id = (S32)UI_ICON_SLIDER_THUMB;
idata->color = g_theme.accent;
CLAY(CLAY_IDI("SlHThumb", (int)hash),
CLAY(CLAY_IDI("SlHThumb", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) },
},
@@ -1941,7 +1941,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
}
}
// Click: double-click resets, single click starts drag
// Click: F64-click resets, single click starts drag
if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20);
@@ -1964,7 +1964,7 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
value_edit_render(hash, track_w);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) {
Clay_ElementId val_eid = CLAY_IDI("SlHVal", (int)hash);
Clay_ElementId val_eid = CLAY_IDI("SlHVal", (S32)hash);
B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text };
@@ -2000,7 +2000,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed);
uint32_t hash = Clay__HashString(clay_str(id), 0).id;
U32 hash = Clay__HashString(clay_str(id), 0).id;
B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag;
@@ -2059,7 +2059,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass thumb travel)
Clay_ElementId hit_eid = CLAY_IDI("SlVHit", (int)hash);
Clay_ElementId hit_eid = CLAY_IDI("SlVHit", (S32)hash);
B32 hovered = Clay_PointerOver(hit_eid);
CLAY(hit_eid,
@@ -2069,7 +2069,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
}
) {
// Visible track (centered inside hit area)
CLAY(CLAY_IDI("SlVTrack", (int)hash),
CLAY(CLAY_IDI("SlVTrack", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) },
.childAlignment = { .y = CLAY_ALIGN_Y_BOTTOM },
@@ -2080,7 +2080,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
// Fill bar (from bottom)
F32 fill_h = normalized * track_h;
if (fill_h < 1.0f) fill_h = 1.0f;
CLAY(CLAY_IDI("SlVFill", (int)hash),
CLAY(CLAY_IDI("SlVFill", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) },
},
@@ -2096,7 +2096,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
idata->icon_id = (S32)UI_ICON_SLIDER_THUMB;
idata->color = g_theme.accent;
CLAY(CLAY_IDI("SlVThumb", (int)hash),
CLAY(CLAY_IDI("SlVThumb", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) },
},
@@ -2118,7 +2118,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
}
}
// Click: double-click resets, single click starts drag
// Click: F64-click resets, single click starts drag
if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20);
@@ -2141,7 +2141,7 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
value_edit_render(hash, thumb_w * 2.0f);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) {
Clay_ElementId val_eid = CLAY_IDI("SlVVal", (int)hash);
Clay_ElementId val_eid = CLAY_IDI("SlVVal", (S32)hash);
B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text };
@@ -2177,7 +2177,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed);
uint32_t hash = Clay__HashString(clay_str(id), 0).id;
U32 hash = Clay__HashString(clay_str(id), 0).id;
B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag;
@@ -2239,7 +2239,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass fader cap travel)
Clay_ElementId hit_eid = CLAY_IDI("FdrHit", (int)hash);
Clay_ElementId hit_eid = CLAY_IDI("FdrHit", (S32)hash);
B32 hovered = Clay_PointerOver(hit_eid);
CLAY(hit_eid,
@@ -2249,7 +2249,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
}
) {
// Visible track (centered inside hit area, empty)
CLAY(CLAY_IDI("FdrTrack", (int)hash),
CLAY(CLAY_IDI("FdrTrack", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) },
},
@@ -2264,7 +2264,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
F32 tw = (i % 5 == 0) ? tick_major_w : tick_minor_w;
// Left tick
CLAY(CLAY_IDI("FdrTkL", (int)(hash * 100 + i)),
CLAY(CLAY_IDI("FdrTkL", (S32)(hash * 100 + i)),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) },
},
@@ -2285,7 +2285,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
) {}
// Right tick
CLAY(CLAY_IDI("FdrTkR", (int)(hash * 100 + i)),
CLAY(CLAY_IDI("FdrTkR", (S32)(hash * 100 + i)),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) },
},
@@ -2315,7 +2315,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
F32 cap_y = (1.0f - normalized) * (track_h - cap_h);
CLAY(CLAY_IDI("FdrCap", (int)hash),
CLAY(CLAY_IDI("FdrCap", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
},
@@ -2336,7 +2336,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
Clay_Color tint = g_theme.accent;
tint.a = 80;
F32 cap_corner = cap_w * (3.0f / 62.2f);
CLAY(CLAY_IDI("FdrTint", (int)hash),
CLAY(CLAY_IDI("FdrTint", (S32)hash),
.layout = {
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
},
@@ -2356,7 +2356,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
}
}
// Click: double-click resets, single click starts drag
// Click: F64-click resets, single click starts drag
if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20);
@@ -2379,7 +2379,7 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
value_edit_render(hash, cap_w * 2.0f);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) {
Clay_ElementId val_eid = CLAY_IDI("FdrVal", (int)hash);
Clay_ElementId val_eid = CLAY_IDI("FdrVal", (S32)hash);
B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text };

View File

@@ -19,51 +19,51 @@
struct UI_ModalState {
B32 active;
uint32_t id; // Hash of the modal's string ID
U32 id; // Hash of the modal's string ID
S32 result; // Button index pressed, -1 = pending
};
struct UI_WindowSlot {
uint32_t id; // Hash of the window's string ID (0 = unused)
U32 id; // Hash of the window's string ID (0 = unused)
Vec2F32 position;
Vec2F32 size;
B32 open;
int16_t z_order;
S16 z_order;
};
struct UI_KnobDragState {
uint32_t dragging_id; // Hash of the knob being dragged (0 = none)
U32 dragging_id; // Hash of the knob being dragged (0 = none)
F32 drag_start_y; // Mouse Y when drag started
F32 drag_start_x; // Mouse X when drag started (for h-slider)
F32 value_at_start; // Value when drag started
B32 was_shift; // Shift state last frame (to re-anchor on change)
uint32_t last_click_id; // Knob hash of last click (for double-click detection)
U32 last_click_id; // Knob hash of last click (for F64-click detection)
S32 last_click_frame; // Frame number of last click
};
struct UI_DragState {
uint32_t dragging_id; // Window ID currently being dragged (0 = none)
U32 dragging_id; // Window ID currently being dragged (0 = none)
Vec2F32 drag_anchor; // Mouse position when drag started
Vec2F32 pos_anchor; // Window position when drag started
};
struct UI_WidgetState {
// Text input focus
uint32_t focused_id; // Clay element ID hash of the focused text input (0 = none)
int32_t cursor_pos; // Cursor position in focused text input
U32 focused_id; // Clay element ID hash of the focused text input (0 = none)
S32 cursor_pos; // Cursor position in focused text input
F32 cursor_blink; // Blink timer (seconds)
// Text selection (sel_start == sel_end means no selection)
int32_t sel_start; // Selection anchor (where selection began)
int32_t sel_end; // Selection extent (moves with cursor)
S32 sel_start; // Selection anchor (where selection began)
S32 sel_end; // Selection extent (moves with cursor)
// Tab cycling: registered text input IDs in order of declaration
uint32_t text_input_ids[UI_WIDGET_MAX_TEXT_INPUTS];
int32_t text_input_count;
U32 text_input_ids[UI_WIDGET_MAX_TEXT_INPUTS];
S32 text_input_count;
B32 tab_pressed; // True on the frame Tab was pressed
// Dropdown
uint32_t open_dropdown_id; // Clay element ID hash of the open dropdown (0 = none)
U32 open_dropdown_id; // Clay element ID hash of the open dropdown (0 = none)
// Input for this frame
PlatformInput input;
@@ -77,14 +77,14 @@ struct UI_WidgetState {
// Window state
UI_WindowSlot windows[UI_WIDGET_MAX_WINDOWS];
S32 window_count;
int16_t next_z;
S16 next_z;
UI_DragState drag;
// Knob drag state
UI_KnobDragState knob_drag;
// Knob text edit state
uint32_t knob_edit_id; // Hash of knob in text edit mode (0 = none)
U32 knob_edit_id; // Hash of knob in text edit mode (0 = none)
char knob_edit_buf[32]; // Text buffer for numeric entry
S32 knob_edit_cursor; // Cursor position in edit buffer
S32 knob_edit_sel_start; // Selection anchor
@@ -154,7 +154,7 @@ B32 ui_window(const char *id, const char *title, B32 *open,
// Knob / potentiometer. Vertical drag to change value.
// unsigned (is_signed=0): value in [0, max_val]
// signed (is_signed=1): value in [-max_val, +max_val]
// default_val: value restored on double-click.
// default_val: value restored on F64-click.
// editable: if true, clicking the value text opens a text input for direct entry.
// Hold Shift while dragging for fine control.
// Returns true if value changed this frame.