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 #pragma once
#include <stdint.h> #include "base/base_core.h"
#include <stdbool.h>
struct AudioEngine; struct AudioEngine;
struct AudioDeviceInfo { struct AudioDeviceInfo {
char name[128]; 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); AudioEngine *audio_create(void *hwnd);
void audio_destroy(AudioEngine *engine); void audio_destroy(AudioEngine *engine);
void audio_refresh_devices(AudioEngine *engine); void audio_refresh_devices(AudioEngine *engine);
int32_t audio_get_device_count(AudioEngine *engine); S32 audio_get_device_count(AudioEngine *engine);
AudioDeviceInfo*audio_get_device(AudioEngine *engine, int32_t index); 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_close_device(AudioEngine *engine);
void audio_play_test_tone(AudioEngine *engine); void audio_play_test_tone(AudioEngine *engine);
bool audio_is_test_tone_playing(AudioEngine *engine); B32 audio_is_test_tone_playing(AudioEngine *engine);
void audio_update(AudioEngine *engine, float dt); void audio_update(AudioEngine *engine, F32 dt);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,32 +1,31 @@
#pragma once #pragma once
#include <stdint.h> #include "base/base_core.h"
#include <stdbool.h>
struct MidiEngine; struct MidiEngine;
struct MidiDeviceInfo { struct MidiDeviceInfo {
char name[64]; char name[64];
int32_t id; S32 id;
bool is_input; B32 is_input;
bool active; // true when note(s) currently held B32 active; // true when note(s) currently held
bool releasing; // true during release flash B32 releasing; // true during release flash
int32_t velocity; // last note-on velocity (0-127) S32 velocity; // last note-on velocity (0-127)
int32_t note; // last MIDI note number (0-127) S32 note; // last MIDI note number (0-127)
}; };
MidiEngine *midi_create(); MidiEngine *midi_create();
void midi_destroy(MidiEngine *engine); void midi_destroy(MidiEngine *engine);
void midi_refresh_devices(MidiEngine *engine); void midi_refresh_devices(MidiEngine *engine);
int32_t midi_get_device_count(MidiEngine *engine); S32 midi_get_device_count(MidiEngine *engine);
MidiDeviceInfo *midi_get_device(MidiEngine *engine, int32_t index); MidiDeviceInfo *midi_get_device(MidiEngine *engine, S32 index);
void midi_open_all_inputs(MidiEngine *engine); void midi_open_all_inputs(MidiEngine *engine);
void midi_close_all_inputs(MidiEngine *engine); void midi_close_all_inputs(MidiEngine *engine);
void midi_update(MidiEngine *engine, float dt); void midi_update(MidiEngine *engine, F32 dt);
bool midi_is_input_active(MidiEngine *engine, int32_t device_index); 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 // 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 // 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 { struct MidiEngine {
MidiDeviceInfo devices[MIDI_MAX_DEVICES]; MidiDeviceInfo devices[MIDI_MAX_DEVICES];
int32_t device_count; S32 device_count;
MIDIClientRef client; MIDIClientRef client;
MIDIPortRef input_port; MIDIPortRef input_port;
// Map: source endpoint index -> our device array index // 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 // Set atomically from callback thread
_Atomic int32_t pending_note_on_vel[MIDI_MAX_DEVICES]; _Atomic S32 pending_note_on_vel[MIDI_MAX_DEVICES];
_Atomic int32_t pending_note_num[MIDI_MAX_DEVICES]; _Atomic S32 pending_note_num[MIDI_MAX_DEVICES];
_Atomic int32_t pending_note_off[MIDI_MAX_DEVICES]; _Atomic S32 pending_note_off[MIDI_MAX_DEVICES];
_Atomic int32_t held_note_count[MIDI_MAX_DEVICES]; _Atomic S32 held_note_count[MIDI_MAX_DEVICES];
// Per-note state (across all devices), set atomically from callback // Per-note state (across all devices), set atomically from callback
_Atomic int32_t note_states[128]; // held count _Atomic S32 note_states[128]; // held count
_Atomic int32_t note_velocities[128]; // last note-on velocity _Atomic S32 note_velocities[128]; // last note-on velocity
// Main thread only // Main thread only
int32_t display_velocity[MIDI_MAX_DEVICES]; S32 display_velocity[MIDI_MAX_DEVICES];
int32_t display_note[MIDI_MAX_DEVICES]; S32 display_note[MIDI_MAX_DEVICES];
float release_timers[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; (void)readProcRefCon;
MidiEngine *engine = (MidiEngine *)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; if (!engine || device_idx < 0 || device_idx >= MIDI_MAX_DEVICES) return;
const MIDIPacket *packet = &pktlist->packet[0]; const MIDIPacket *packet = &pktlist->packet[0];
for (UInt32 i = 0; i < pktlist->numPackets; i++) { for (UInt32 i = 0; i < pktlist->numPackets; i++) {
// Parse MIDI bytes // Parse MIDI bytes
for (UInt16 j = 0; j < packet->length; ) { 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) // Skip non-status bytes (running status not handled for simplicity)
if (status < 0x80) { j++; continue; } if (status < 0x80) { j++; continue; }
uint8_t kind = status & 0xF0; U8 kind = status & 0xF0;
if (kind == 0x90 && j + 2 < packet->length) { if (kind == 0x90 && j + 2 < packet->length) {
uint8_t note = packet->data[j + 1]; U8 note = packet->data[j + 1];
uint8_t velocity = packet->data[j + 2]; U8 velocity = packet->data[j + 2];
j += 3; j += 3;
if (velocity > 0) { if (velocity > 0) {
atomic_store(&engine->pending_note_on_vel[device_idx], (int32_t)velocity); atomic_store(&engine->pending_note_on_vel[device_idx], (S32)velocity);
atomic_store(&engine->pending_note_num[device_idx], (int32_t)(note + 1)); atomic_store(&engine->pending_note_num[device_idx], (S32)(note + 1));
atomic_fetch_add(&engine->held_note_count[device_idx], 1); atomic_fetch_add(&engine->held_note_count[device_idx], 1);
if (note < 128) { if (note < 128) {
atomic_fetch_add(&engine->note_states[note], 1); 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 { } else {
// Note-on with velocity 0 = note-off // Note-on with velocity 0 = note-off
atomic_store(&engine->pending_note_off[device_idx], 1); 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 (count <= 1) atomic_store(&engine->held_note_count[device_idx], 0);
if (note < 128) { 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); if (c <= 1) atomic_store(&engine->note_states[note], 0);
} }
} }
} else if (kind == 0x80 && j + 2 < packet->length) { } 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; j += 3;
atomic_store(&engine->pending_note_off[device_idx], 1); 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 (count <= 1) atomic_store(&engine->held_note_count[device_idx], 0);
if (note_off < 128) { 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); if (c <= 1) atomic_store(&engine->note_states[note_off], 0);
} }
} else if (kind == 0xC0 || kind == 0xD0) { } 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); CFStringGetCString(name_ref, dev->name, sizeof(dev->name), kCFStringEncodingUTF8);
CFRelease(name_ref); CFRelease(name_ref);
} else { } 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; 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); CFStringGetCString(name_ref, dev->name, sizeof(dev->name), kCFStringEncodingUTF8);
CFRelease(name_ref); CFRelease(name_ref);
} else { } 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; dev->id = engine->device_count;
@@ -183,9 +183,9 @@ void midi_destroy(MidiEngine *engine) {
void midi_open_all_inputs(MidiEngine *engine) { void midi_open_all_inputs(MidiEngine *engine) {
ItemCount num_sources = MIDIGetNumberOfSources(); 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); 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); 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); 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_on_vel[i], 0);
atomic_store(&engine->pending_note_num[i], 0); atomic_store(&engine->pending_note_num[i], 0);
atomic_store(&engine->pending_note_off[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->display_note[i] = 0;
engine->release_timers[i] = 0.0f; 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_states[i], 0);
atomic_store(&engine->note_velocities[i], 0); atomic_store(&engine->note_velocities[i], 0);
} }
} }
void midi_update(MidiEngine *engine, float dt) { void midi_update(MidiEngine *engine, F32 dt) {
for (int32_t i = 0; i < engine->device_count; i++) { for (S32 i = 0; i < engine->device_count; i++) {
if (!engine->devices[i].is_input) continue; if (!engine->devices[i].is_input) continue;
int32_t vel = atomic_exchange(&engine->pending_note_on_vel[i], 0); S32 vel = atomic_exchange(&engine->pending_note_on_vel[i], 0);
int32_t note_p1 = atomic_exchange(&engine->pending_note_num[i], 0); S32 note_p1 = atomic_exchange(&engine->pending_note_num[i], 0);
if (vel > 0) engine->display_velocity[i] = vel; if (vel > 0) engine->display_velocity[i] = vel;
if (note_p1 > 0) engine->display_note[i] = note_p1 - 1; if (note_p1 > 0) engine->display_note[i] = note_p1 - 1;
int32_t off = atomic_exchange(&engine->pending_note_off[i], 0); S32 off = atomic_exchange(&engine->pending_note_off[i], 0);
int32_t held = atomic_load(&engine->held_note_count[i]); S32 held = atomic_load(&engine->held_note_count[i]);
if (held > 0) { if (held > 0) {
engine->devices[i].active = true; 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; if (device_index < 0 || device_index >= engine->device_count) return false;
return engine->devices[device_index].active; 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; if (note < 0 || note > 127) return false;
return atomic_load(&engine->note_states[note]) > 0; 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 (note < 0 || note > 127) return 0;
if (atomic_load(&engine->note_states[note]) <= 0) return 0; if (atomic_load(&engine->note_states[note]) <= 0) return 0;
return atomic_load(&engine->note_velocities[note]); return atomic_load(&engine->note_velocities[note]);
@@ -271,11 +271,11 @@ void midi_refresh_devices(MidiEngine *engine) {
midi_open_all_inputs(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; 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; if (index < 0 || index >= engine->device_count) return nullptr;
return &engine->devices[index]; return &engine->devices[index];
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,31 +1,30 @@
#pragma once #pragma once
#include <stdint.h> #include "base/base_core.h"
#include <stdbool.h>
struct Renderer; struct Renderer;
struct Clay_RenderCommandArray; struct Clay_RenderCommandArray;
struct RendererDesc { struct RendererDesc {
void *window_handle = nullptr; void *window_handle = nullptr;
int32_t width = 1280; S32 width = 1280;
int32_t height = 720; S32 height = 720;
int32_t frame_count = 2; S32 frame_count = 2;
}; };
Renderer *renderer_create(RendererDesc *desc); Renderer *renderer_create(RendererDesc *desc);
void renderer_destroy(Renderer *renderer); 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_end_frame(Renderer *renderer, Clay_RenderCommandArray render_commands);
void renderer_resize(Renderer *renderer, int32_t width, int32_t height); void renderer_resize(Renderer *renderer, S32 width, S32 height);
void renderer_set_font_scale(Renderer *renderer, float scale); void renderer_set_font_scale(Renderer *renderer, F32 scale);
void renderer_set_clear_color(Renderer *renderer, float r, float g, float b); void renderer_set_clear_color(Renderer *renderer, F32 r, F32 g, F32 b);
// Text measurement callback compatible with UI_MeasureTextFn // Text measurement callback compatible with UI_MeasureTextFn
// Measures text of given length (NOT necessarily null-terminated) at font_size pixels. // Measures text of given length (NOT necessarily null-terminated) at font_size pixels.
// user_data should be the Renderer pointer. // user_data should be the Renderer pointer.
struct Vec2F32; 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) // 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 { struct Renderer {
HWND hwnd; HWND hwnd;
int32_t width; S32 width;
int32_t height; S32 height;
int32_t frame_count; S32 frame_count;
UINT frame_index; UINT frame_index;
ID3D12Device *device; ID3D12Device *device;
ID3D12CommandQueue *command_queue; ID3D12CommandQueue *command_queue;
IDXGISwapChain3 *swap_chain; IDXGISwapChain3 *swap_chain;
HANDLE swap_chain_waitable; HANDLE swap_chain_waitable;
bool swap_chain_occluded; B32 swap_chain_occluded;
bool tearing_support; B32 tearing_support;
ID3D12DescriptorHeap *rtv_heap; ID3D12DescriptorHeap *rtv_heap;
ID3D12DescriptorHeap *srv_heap; ID3D12DescriptorHeap *srv_heap;
@@ -198,7 +198,7 @@ struct Renderer {
ID3D12RootSignature *root_signature; ID3D12RootSignature *root_signature;
ID3D12PipelineState *pipeline_state; ID3D12PipelineState *pipeline_state;
// Per-frame vertex/index buffers (double-buffered) // Per-frame vertex/index buffers (F64-buffered)
ID3D12Resource *vertex_buffers[NUM_BACK_BUFFERS]; ID3D12Resource *vertex_buffers[NUM_BACK_BUFFERS];
ID3D12Resource *index_buffers[NUM_BACK_BUFFERS]; ID3D12Resource *index_buffers[NUM_BACK_BUFFERS];
void *vb_mapped[NUM_BACK_BUFFERS]; void *vb_mapped[NUM_BACK_BUFFERS];
@@ -220,15 +220,15 @@ struct Renderer {
F32 measure_font_size; F32 measure_font_size;
// Clear color // Clear color
float clear_r = 0.12f; F32 clear_r = 0.12f;
float clear_g = 0.12f; F32 clear_g = 0.12f;
float clear_b = 0.13f; F32 clear_b = 0.13f;
}; };
//////////////////////////////// ////////////////////////////////
// DX12 infrastructure // DX12 infrastructure
static bool create_device(Renderer *r) { static B32 create_device(Renderer *r) {
#ifdef DX12_ENABLE_DEBUG_LAYER #ifdef DX12_ENABLE_DEBUG_LAYER
ID3D12Debug *debug = nullptr; ID3D12Debug *debug = nullptr;
if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) { if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) {
@@ -254,7 +254,7 @@ static bool create_device(Renderer *r) {
return true; return true;
} }
static bool create_command_queue(Renderer *r) { static B32 create_command_queue(Renderer *r) {
D3D12_COMMAND_QUEUE_DESC desc = {}; D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; 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; 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 // RTV heap
{ {
D3D12_DESCRIPTOR_HEAP_DESC desc = {}; 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); SIZE_T rtv_size = r->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE handle = r->rtv_heap->GetCPUDescriptorHandleForHeapStart(); 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; r->rtv_descriptors[i] = handle;
handle.ptr += rtv_size; handle.ptr += rtv_size;
} }
@@ -294,8 +294,8 @@ static bool create_descriptor_heaps(Renderer *r) {
return true; return true;
} }
static bool create_frame_resources(Renderer *r) { static B32 create_frame_resources(Renderer *r) {
for (int i = 0; i < r->frame_count; i++) { for (S32 i = 0; i < r->frame_count; i++) {
if (r->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, if (r->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&r->frames[i].command_allocator)) != S_OK) IID_PPV_ARGS(&r->frames[i].command_allocator)) != S_OK)
return false; return false;
@@ -315,7 +315,7 @@ static bool create_frame_resources(Renderer *r) {
return r->fence_event != nullptr; return r->fence_event != nullptr;
} }
static bool create_swap_chain(Renderer *r) { static B32 create_swap_chain(Renderer *r) {
DXGI_SWAP_CHAIN_DESC1 sd = {}; DXGI_SWAP_CHAIN_DESC1 sd = {};
sd.BufferCount = NUM_BACK_BUFFERS; sd.BufferCount = NUM_BACK_BUFFERS;
sd.Width = 0; 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); if (r->measure_font) DeleteObject(r->measure_font);
r->measure_font = CreateFontW( 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, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 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); SelectObject(r->measure_dc, r->measure_font);
} }
static bool create_font_atlas(Renderer *r, F32 font_size) { static B32 create_font_atlas(Renderer *r, F32 font_size) {
const int SS = 2; // supersample factor const S32 SS = 2; // supersample factor
F32 render_size = font_size * SS; F32 render_size = font_size * SS;
int render_w = FONT_ATLAS_W * SS; S32 render_w = FONT_ATLAS_W * SS;
int render_h = FONT_ATLAS_H * SS; S32 render_h = FONT_ATLAS_H * SS;
r->font_atlas_size = font_size; r->font_atlas_size = font_size;
// Create a GDI bitmap to render glyphs at supersampled resolution // Create a GDI bitmap to render glyphs at supersampled resolution
HDC dc = CreateCompatibleDC(nullptr); HDC dc = CreateCompatibleDC(nullptr);
HFONT font = CreateFontW( HFONT font = CreateFontW(
-(int)(render_size + 0.5f), 0, 0, 0, -(S32)(render_size + 0.5f), 0, 0, 0,
FW_NORMAL, FALSE, FALSE, FALSE, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE, ANTIALIASED_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
@@ -468,16 +468,16 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
SetBkMode(dc, TRANSPARENT); SetBkMode(dc, TRANSPARENT);
// Render each glyph at supersampled resolution // Render each glyph at supersampled resolution
int pen_x = SS, pen_y = SS; S32 pen_x = SS, pen_y = SS;
int row_height = 0; 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); char ch = (char)(GLYPH_FIRST + i);
SIZE ch_size = {}; SIZE ch_size = {};
GetTextExtentPoint32A(dc, &ch, 1, &ch_size); GetTextExtentPoint32A(dc, &ch, 1, &ch_size);
int gw = ch_size.cx + 2 * SS; // padding scaled by SS S32 gw = ch_size.cx + 2 * SS; // padding scaled by SS
int gh = ch_size.cy + 2 * SS; S32 gh = ch_size.cy + 2 * SS;
if (pen_x + gw >= render_w) { if (pen_x + gw >= render_w) {
pen_x = SS; 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 // Box-filter downsample from supersampled resolution to atlas resolution
U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H); U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
U8 *src = (U8 *)dib_bits; U8 *src = (U8 *)dib_bits;
for (int y = 0; y < FONT_ATLAS_H; y++) { for (S32 y = 0; y < FONT_ATLAS_H; y++) {
for (int x = 0; x < FONT_ATLAS_W; x++) { for (S32 x = 0; x < FONT_ATLAS_W; x++) {
int sum = 0; S32 sum = 0;
for (int sy = 0; sy < SS; sy++) { for (S32 sy = 0; sy < SS; sy++) {
for (int sx = 0; sx < SS; sx++) { for (S32 sx = 0; sx < SS; sx++) {
int src_idx = ((y * SS + sy) * render_w + (x * SS + sx)) * 4; S32 src_idx = ((y * SS + sy) * render_w + (x * SS + sx)) * 4;
sum += src[src_idx + 2]; // R channel from BGRA 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); a = powf(a, 0.55f);
atlas_data[y * FONT_ATLAS_W + x] = (U8)(a * 255.0f + 0.5f); 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}; D3D12_RANGE read_range = {0, 0};
upload_buf->Map(0, &read_range, &mapped); upload_buf->Map(0, &read_range, &mapped);
U8 *dst = (U8 *)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, memcpy(dst + y * footprint.Footprint.RowPitch,
atlas_data + y * FONT_ATLAS_W, atlas_data + y * FONT_ATLAS_W,
FONT_ATLAS_W); FONT_ATLAS_W);
@@ -631,7 +631,7 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
//////////////////////////////// ////////////////////////////////
// UI rendering pipeline setup // UI rendering pipeline setup
static bool create_ui_pipeline(Renderer *r) { static B32 create_ui_pipeline(Renderer *r) {
ID3DBlob *vs_blob = nullptr; ID3DBlob *vs_blob = nullptr;
ID3DBlob *ps_blob = nullptr; ID3DBlob *ps_blob = nullptr;
ID3DBlob *error_blob = nullptr; ID3DBlob *error_blob = nullptr;
@@ -769,11 +769,11 @@ static bool create_ui_pipeline(Renderer *r) {
return SUCCEEDED(hr); return SUCCEEDED(hr);
} }
static bool create_ui_buffers(Renderer *r) { static B32 create_ui_buffers(Renderer *r) {
D3D12_HEAP_PROPERTIES heap_props = {}; D3D12_HEAP_PROPERTIES heap_props = {};
heap_props.Type = D3D12_HEAP_TYPE_UPLOAD; 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 = {}; D3D12_RESOURCE_DESC buf_desc = {};
buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buf_desc.Width = MAX_VERTICES * sizeof(UIVertex); 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]); 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 = {}; D3D12_RESOURCE_DESC buf_desc = {};
buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; buf_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buf_desc.Width = MAX_INDICES * sizeof(U32); buf_desc.Width = MAX_INDICES * sizeof(U32);
@@ -817,7 +817,7 @@ static bool create_ui_buffers(Renderer *r) {
//////////////////////////////// ////////////////////////////////
// Text measurement callback for UI system // 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; Renderer *r = (Renderer *)user_data;
if (!r || length == 0) return v2f32(0, font_size); if (!r || length == 0) return v2f32(0, font_size);
@@ -839,12 +839,12 @@ struct DrawBatch {
}; };
static void emit_quad(DrawBatch *batch, static void emit_quad(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float u0, float v0, float u1, float v1, F32 u0, F32 v0, F32 u1, F32 v1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float rmin_x, float rmin_y, float rmax_x, float rmax_y, F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float border_thickness, float softness, float mode) F32 border_thickness, F32 softness, F32 mode)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -853,9 +853,9 @@ static void emit_quad(DrawBatch *batch,
UIVertex *v = &batch->vertices[base]; UIVertex *v = &batch->vertices[base];
// For SDF mode, expand quad slightly for anti-aliasing // 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) { if (mode < 0.5f) {
float pad = softness + 1.0f; F32 pad = softness + 1.0f;
px0 -= pad; py0 -= pad; px1 += pad; py1 += pad; 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[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; 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].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_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; 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, static void emit_quad_rotated(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float u0, float v0, float u1, float v1, F32 u0, F32 v0, F32 u1, F32 v1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float angle_rad) F32 angle_rad)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -895,13 +895,13 @@ static void emit_quad_rotated(DrawBatch *batch,
U32 base = batch->vertex_count; U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base]; UIVertex *v = &batch->vertices[base];
float cx = (x0 + x1) * 0.5f; F32 cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f; F32 cy = (y0 + y1) * 0.5f;
float cosA = cosf(angle_rad); F32 cosA = cosf(angle_rad);
float sinA = sinf(angle_rad); F32 sinA = sinf(angle_rad);
float dx0 = x0 - cx, dy0 = y0 - cy; F32 dx0 = x0 - cx, dy0 = y0 - cy;
float dx1 = x1 - cx, dy1 = y1 - cy; F32 dx1 = x1 - cx, dy1 = y1 - cy;
v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA; v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA;
v[0].pos[1] = cy + dx0 * sinA + dy0 * cosA; 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].pos[1] = cy + dx0 * sinA + dy1 * cosA;
v[3].uv[0] = u0; v[3].uv[1] = v1; 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].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_min[0] = 0; v[i].rect_min[1] = 0;
v[i].rect_max[0] = 0; v[i].rect_max[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, static void emit_rect(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float border_thickness, float softness) F32 border_thickness, F32 softness)
{ {
emit_quad(batch, x0, y0, x1, y1, emit_quad(batch, x0, y0, x1, y1,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -953,11 +953,11 @@ static void emit_rect(DrawBatch *batch,
} }
static void emit_rect_vgradient(DrawBatch *batch, static void emit_rect_vgradient(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float tr, float tg, float tb, float ta, F32 tr, F32 tg, F32 tb, F32 ta,
float br, float bg, float bb_, float ba, F32 br, F32 bg, F32 bb_, F32 ba,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float softness) F32 softness)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -965,8 +965,8 @@ static void emit_rect_vgradient(DrawBatch *batch,
U32 base = batch->vertex_count; U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base]; UIVertex *v = &batch->vertices[base];
float pad = softness + 1.0f; F32 pad = softness + 1.0f;
float px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad; 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[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; 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[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; 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_min[0] = x0; v[i].rect_min[1] = y0;
v[i].rect_max[0] = x1; v[i].rect_max[1] = y1; 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; 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, static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
Clay_BoundingBox bbox, Clay_Color color, const char *text, int32_t text_len, Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len,
uint16_t font_size) U16 font_size)
{ {
if (text_len == 0 || color.a < 0.1f) return; if (text_len == 0 || color.a < 0.1f) return;
// Color is 0-255 in Clay convention, normalize to 0-1 // Color is 0-255 in Clay convention, normalize to 0-1
float cr = color.r / 255.f; F32 cr = color.r / 255.f;
float cg = color.g / 255.f; F32 cg = color.g / 255.f;
float cb = color.b / 255.f; F32 cb = color.b / 255.f;
float ca = color.a / 255.f; F32 ca = color.a / 255.f;
F32 scale = (F32)font_size / r->font_atlas_size; F32 scale = (F32)font_size / r->font_atlas_size;
F32 text_h = r->font_line_height * scale; 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 x = floorf(bbox.x + 0.5f);
F32 y = floorf(bbox.y + (bbox.height - text_h) * 0.5f + 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]; char ch = text[i];
if (ch < GLYPH_FIRST || ch > GLYPH_LAST) { if (ch < GLYPH_FIRST || ch > GLYPH_LAST) {
if (ch == ' ') { if (ch == ' ') {
int gi = ' ' - GLYPH_FIRST; S32 gi = ' ' - GLYPH_FIRST;
if (gi >= 0 && gi < GLYPH_COUNT) if (gi >= 0 && gi < GLYPH_COUNT)
x += r->glyphs[gi].x_advance * scale; x += r->glyphs[gi].x_advance * scale;
continue; continue;
} }
ch = '?'; ch = '?';
} }
int gi = ch - GLYPH_FIRST; S32 gi = ch - GLYPH_FIRST;
if (gi < 0 || gi >= GLYPH_COUNT) continue; if (gi < 0 || gi >= GLYPH_COUNT) continue;
GlyphInfo *g = &r->glyphs[gi]; 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->SetPipelineState(r->pipeline_state);
r->command_list->SetGraphicsRootSignature(r->root_signature); 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); r->command_list->SetGraphicsRoot32BitConstants(0, 4, constants, 0);
// Bind texture (font or icon) // Bind texture (font or icon)
@@ -1122,7 +1122,7 @@ void renderer_destroy(Renderer *r) {
wait_for_pending(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->vertex_buffers[i]) r->vertex_buffers[i]->Release();
if (r->index_buffers[i]) r->index_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) { r->swap_chain->SetFullscreenState(false, nullptr); r->swap_chain->Release(); }
if (r->swap_chain_waitable) CloseHandle(r->swap_chain_waitable); 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->frames[i].command_allocator) r->frames[i].command_allocator->Release();
if (r->command_queue) r->command_queue->Release(); if (r->command_queue) r->command_queue->Release();
if (r->command_list) r->command_list->Release(); if (r->command_list) r->command_list->Release();
@@ -1160,7 +1160,7 @@ void renderer_destroy(Renderer *r) {
delete 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) if ((r->swap_chain_occluded && r->swap_chain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
|| IsIconic(r->hwnd)) || 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; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
r->command_list->ResourceBarrier(1, &barrier); 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->ClearRenderTargetView(r->rtv_descriptors[back_buffer_idx], clear_color, 0, nullptr);
r->command_list->OMSetRenderTargets(1, &r->rtv_descriptors[back_buffer_idx], FALSE, 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; batch.index_count = 0;
// Track which texture is currently bound (0 = font, 1 = icon) // Track which texture is currently bound (0 = font, 1 = icon)
int bound_texture = 0; S32 bound_texture = 0;
U32 flush_index_start = 0; U32 flush_index_start = 0;
auto bind_font = [&]() { 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_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
Clay_BoundingBox bb = cmd->boundingBox; 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: { case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *border = &cmd->renderData.border; Clay_BorderRenderData *border = &cmd->renderData.border;
Clay_Color c = border->color; Clay_Color c = border->color;
float cr_norm = c.r / 255.f; F32 cr_norm = c.r / 255.f;
float cg_norm = c.g / 255.f; F32 cg_norm = c.g / 255.f;
float cb_norm = c.b / 255.f; F32 cb_norm = c.b / 255.f;
float ca_norm = c.a / 255.f; F32 ca_norm = c.a / 255.f;
// Use SDF rounded border when corner radius is present and widths are uniform // Use SDF rounded border when corner radius is present and widths are uniform
Clay_CornerRadius cr = border->cornerRadius; Clay_CornerRadius cr = border->cornerRadius;
bool has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0; B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
bool uniform = border->width.top == border->width.bottom && B32 uniform = border->width.top == border->width.bottom &&
border->width.top == border->width.left && border->width.top == border->width.left &&
border->width.top == border->width.right && border->width.top == border->width.right &&
border->width.top > 0; 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, bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
cr_norm, cg_norm, cb_norm, ca_norm, cr_norm, cg_norm, cb_norm, ca_norm,
cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft, cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft,
(float)border->width.top, 1.0f); (F32)border->width.top, 1.0f);
} else { } else {
if (border->width.top > 0) { if (border->width.top > 0) {
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top, 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(); bind_icon();
CustomIconData *icon = (CustomIconData *)custom->customData; CustomIconData *icon = (CustomIconData *)custom->customData;
Clay_Color c = icon->color; Clay_Color c = icon->color;
float cr = c.r / 255.f, cg = c.g / 255.f; F32 cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f; F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[icon->icon_id]; UI_IconInfo *info = &g_icons[icon->icon_id];
emit_quad(&batch, emit_quad(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, 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(); bind_icon();
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData; CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
Clay_Color c = ri->color; Clay_Color c = ri->color;
float cr = c.r / 255.f, cg = c.g / 255.f; F32 cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f; F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[ri->icon_id]; UI_IconInfo *info = &g_icons[ri->icon_id];
emit_quad_rotated(&batch, emit_quad_rotated(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, 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++; 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 // Create texture resource
D3D12_HEAP_PROPERTIES heap_props = {}; D3D12_HEAP_PROPERTIES heap_props = {};
heap_props.Type = D3D12_HEAP_TYPE_DEFAULT; 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}; D3D12_RANGE read_range = {0, 0};
upload_buf->Map(0, &read_range, &mapped); upload_buf->Map(0, &read_range, &mapped);
U8 *dst = (U8 *)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); memcpy(dst + y * footprint.Footprint.RowPitch, data + y * w * 4, w * 4);
} }
upload_buf->Unmap(0, nullptr); 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()); &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; if (width <= 0 || height <= 0) return;
wait_for_pending(r); wait_for_pending(r);
@@ -1497,14 +1497,14 @@ void renderer_resize(Renderer *r, int32_t width, int32_t height) {
r->height = 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_r = cr;
r->clear_g = cg; r->clear_g = cg;
r->clear_b = cb; r->clear_b = cb;
} }
void renderer_set_font_scale(Renderer *r, float scale) { void renderer_set_font_scale(Renderer *r, F32 scale) {
float target_size = 15.0f * scale; F32 target_size = 15.0f * scale;
if (fabsf(target_size - r->font_atlas_size) < 0.1f) return; if (fabsf(target_size - r->font_atlas_size) < 0.1f) return;
wait_for_pending(r); wait_for_pending(r);
if (r->font_texture) { r->font_texture->Release(); r->font_texture = nullptr; } 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 // Renderer struct
struct Renderer { struct Renderer {
int32_t width; S32 width;
int32_t height; S32 height;
int32_t frame_count; S32 frame_count;
uint32_t frame_index; U32 frame_index;
F32 backing_scale; F32 backing_scale;
id<MTLDevice> device; id<MTLDevice> device;
@@ -187,11 +187,11 @@ struct Renderer {
//////////////////////////////// ////////////////////////////////
// Font atlas (Core Text + CoreGraphics) // Font atlas (Core Text + CoreGraphics)
static bool create_font_atlas(Renderer *r, F32 font_size) { static B32 create_font_atlas(Renderer *r, F32 font_size) {
const int SS = 2; const S32 SS = 2;
F32 render_size = font_size * SS; F32 render_size = font_size * SS;
int render_w = FONT_ATLAS_W * SS; S32 render_w = FONT_ATLAS_W * SS;
int render_h = FONT_ATLAS_H * SS; S32 render_h = FONT_ATLAS_H * SS;
r->font_atlas_size = font_size; 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)); CGContextSetTextMatrix(ctx, CGAffineTransformMake(1, 0, 0, -1, 0, 0));
// Render each glyph // Render each glyph
int pen_x = SS, pen_y = SS; S32 pen_x = SS, pen_y = SS;
int row_height = 0; S32 row_height = 0;
NSDictionary *attrs = @{ NSDictionary *attrs = @{
(id)kCTFontAttributeName: (__bridge id)font, (id)kCTFontAttributeName: (__bridge id)font,
(id)kCTForegroundColorFromContextAttributeName: @YES (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); char ch = (char)(GLYPH_FIRST + i);
NSString *str = [[NSString alloc] initWithBytes:&ch length:1 encoding:NSASCIIStringEncoding]; NSString *str = [[NSString alloc] initWithBytes:&ch length:1 encoding:NSASCIIStringEncoding];
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs]; NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)astr); CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)astr);
CGRect bounds = CTLineGetBoundsWithOptions(line, 0); CGRect bounds = CTLineGetBoundsWithOptions(line, 0);
int gw = (int)ceilf((float)bounds.size.width) + 2 * SS; S32 gw = (S32)ceilf((F32)bounds.size.width) + 2 * SS;
int gh = (int)ceilf((float)(ascent + descent)) + 2 * SS; S32 gh = (S32)ceilf((F32)(ascent + descent)) + 2 * SS;
if (pen_x + gw >= render_w) { if (pen_x + gw >= render_w) {
pen_x = SS; pen_x = SS;
@@ -252,7 +252,7 @@ static bool create_font_atlas(Renderer *r, F32 font_size) {
row_height = 0; 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: // Context is flipped to top-left origin. Position baseline:
// pen_y + SS is the top of the glyph cell, baseline is ascent below that. // 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; pen_x += gw + SS;
CFRelease(line); CFRelease(line);
[astr release];
[str release];
} }
// Box-filter downsample (context is flipped, so bitmap is already top-down) // Box-filter downsample (context is flipped, so bitmap is already top-down)
uint8_t *src = (uint8_t *)CGBitmapContextGetData(ctx); U8 *src = (U8 *)CGBitmapContextGetData(ctx);
uint8_t *atlas_data = (uint8_t *)malloc(FONT_ATLAS_W * FONT_ATLAS_H); U8 *atlas_data = (U8 *)malloc(FONT_ATLAS_W * FONT_ATLAS_H);
for (int y = 0; y < FONT_ATLAS_H; y++) { for (S32 y = 0; y < FONT_ATLAS_H; y++) {
for (int x = 0; x < FONT_ATLAS_W; x++) { for (S32 x = 0; x < FONT_ATLAS_W; x++) {
int sum = 0; S32 sum = 0;
for (int sy = 0; sy < SS; sy++) { for (S32 sy = 0; sy < SS; sy++) {
for (int sx = 0; sx < SS; sx++) { for (S32 sx = 0; sx < SS; sx++) {
int src_idx = (y * SS + sy) * render_w + (x * SS + sx); S32 src_idx = (y * SS + sy) * render_w + (x * SS + sx);
sum += src[src_idx]; 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); 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,14 +337,16 @@ static void ensure_measure_font(Renderer *r, F32 font_size) {
r->measure_font_size = 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; Renderer *r = (Renderer *)user_data;
if (!r || length == 0) return v2f32(0, font_size); if (!r || length == 0) return v2f32(0, font_size);
ensure_measure_font(r, font_size); ensure_measure_font(r, font_size);
Vec2F32 result = v2f32(0, font_size);
@autoreleasepool {
NSString *str = [[NSString alloc] initWithBytes:text length:length encoding:NSUTF8StringEncoding]; NSString *str = [[NSString alloc] initWithBytes:text length:length encoding:NSUTF8StringEncoding];
if (!str) return v2f32(0, font_size); if (!str) return result;
NSDictionary *attrs = @{ (id)kCTFontAttributeName: (__bridge id)r->measure_font }; NSDictionary *attrs = @{ (id)kCTFontAttributeName: (__bridge id)r->measure_font };
NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs]; NSAttributedString *astr = [[NSAttributedString alloc] initWithString:str attributes:attrs];
@@ -350,8 +354,12 @@ Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size,
CGRect bounds = CTLineGetBoundsWithOptions(line, 0); CGRect bounds = CTLineGetBoundsWithOptions(line, 0);
CFRelease(line); 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, static void emit_quad(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float u0, float v0, float u1, float v1, F32 u0, F32 v0, F32 u1, F32 v1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float rmin_x, float rmin_y, float rmax_x, float rmax_y, F32 rmin_x, F32 rmin_y, F32 rmax_x, F32 rmax_y,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float border_thickness, float softness, float mode) F32 border_thickness, F32 softness, F32 mode)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -378,9 +386,9 @@ static void emit_quad(DrawBatch *batch,
U32 base = batch->vertex_count; U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base]; 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) { if (mode < 0.5f) {
float pad = softness + 1.0f; F32 pad = softness + 1.0f;
px0 -= pad; py0 -= pad; px1 += pad; py1 += pad; 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[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; 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].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_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; 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, static void emit_quad_rotated(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float u0, float v0, float u1, float v1, F32 u0, F32 v0, F32 u1, F32 v1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float angle_rad) F32 angle_rad)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -420,14 +428,14 @@ static void emit_quad_rotated(DrawBatch *batch,
U32 base = batch->vertex_count; U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base]; UIVertex *v = &batch->vertices[base];
float cx = (x0 + x1) * 0.5f; F32 cx = (x0 + x1) * 0.5f;
float cy = (y0 + y1) * 0.5f; F32 cy = (y0 + y1) * 0.5f;
float cosA = cosf(angle_rad); F32 cosA = cosf(angle_rad);
float sinA = sinf(angle_rad); F32 sinA = sinf(angle_rad);
// Corner offsets from center // Corner offsets from center
float dx0 = x0 - cx, dy0 = y0 - cy; F32 dx0 = x0 - cx, dy0 = y0 - cy;
float dx1 = x1 - cx, dy1 = y1 - cy; F32 dx1 = x1 - cx, dy1 = y1 - cy;
// Rotate each corner around center // Rotate each corner around center
v[0].pos[0] = cx + dx0 * cosA - dy0 * sinA; 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].pos[1] = cy + dx0 * sinA + dy1 * cosA;
v[3].uv[0] = u0; v[3].uv[1] = v1; 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].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_min[0] = 0; v[i].rect_min[1] = 0;
v[i].rect_max[0] = 0; v[i].rect_max[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, static void emit_rect(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float cr, float cg, float cb, float ca, F32 cr, F32 cg, F32 cb, F32 ca,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float border_thickness, float softness) F32 border_thickness, F32 softness)
{ {
emit_quad(batch, x0, y0, x1, y1, emit_quad(batch, x0, y0, x1, y1,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -480,11 +488,11 @@ static void emit_rect(DrawBatch *batch,
} }
static void emit_rect_vgradient(DrawBatch *batch, static void emit_rect_vgradient(DrawBatch *batch,
float x0, float y0, float x1, float y1, F32 x0, F32 y0, F32 x1, F32 y1,
float tr, float tg, float tb, float ta, F32 tr, F32 tg, F32 tb, F32 ta,
float br, float bg, float bb_, float ba, F32 br, F32 bg, F32 bb_, F32 ba,
float cr_tl, float cr_tr, float cr_br, float cr_bl, F32 cr_tl, F32 cr_tr, F32 cr_br, F32 cr_bl,
float softness) F32 softness)
{ {
if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES) if (batch->vertex_count + 4 > MAX_VERTICES || batch->index_count + 6 > MAX_INDICES)
return; return;
@@ -492,8 +500,8 @@ static void emit_rect_vgradient(DrawBatch *batch,
U32 base = batch->vertex_count; U32 base = batch->vertex_count;
UIVertex *v = &batch->vertices[base]; UIVertex *v = &batch->vertices[base];
float pad = softness + 1.0f; F32 pad = softness + 1.0f;
float px0 = x0 - pad, py0 = y0 - pad, px1 = x1 + pad, py1 = y1 + pad; 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[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; 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[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; 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_min[0] = x0; v[i].rect_min[1] = y0;
v[i].rect_max[0] = x1; v[i].rect_max[1] = y1; 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; 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, static void emit_text_glyphs(DrawBatch *batch, Renderer *r,
Clay_BoundingBox bbox, Clay_Color color, const char *text, int32_t text_len, Clay_BoundingBox bbox, Clay_Color color, const char *text, S32 text_len,
uint16_t font_size) U16 font_size)
{ {
if (text_len == 0 || color.a < 0.1f) return; if (text_len == 0 || color.a < 0.1f) return;
float cr = color.r / 255.f; F32 cr = color.r / 255.f;
float cg = color.g / 255.f; F32 cg = color.g / 255.f;
float cb = color.b / 255.f; F32 cb = color.b / 255.f;
float ca = color.a / 255.f; F32 ca = color.a / 255.f;
F32 scale = (F32)font_size / r->font_atlas_size; F32 scale = (F32)font_size / r->font_atlas_size;
F32 text_h = r->font_line_height * scale; 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 x = floorf(bbox.x + 0.5f);
F32 y = floorf(bbox.y + (bbox.height - text_h) * 0.5f + 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]; char ch = text[i];
if (ch < GLYPH_FIRST || ch > GLYPH_LAST) { if (ch < GLYPH_FIRST || ch > GLYPH_LAST) {
if (ch == ' ') { if (ch == ' ') {
int gi = ' ' - GLYPH_FIRST; S32 gi = ' ' - GLYPH_FIRST;
if (gi >= 0 && gi < GLYPH_COUNT) if (gi >= 0 && gi < GLYPH_COUNT)
x += r->glyphs[gi].x_advance * scale; x += r->glyphs[gi].x_advance * scale;
continue; continue;
} }
ch = '?'; ch = '?';
} }
int gi = ch - GLYPH_FIRST; S32 gi = ch - GLYPH_FIRST;
if (gi < 0 || gi >= GLYPH_COUNT) continue; if (gi < 0 || gi >= GLYPH_COUNT) continue;
GlyphInfo *g = &r->glyphs[gi]; GlyphInfo *g = &r->glyphs[gi];
@@ -595,6 +603,7 @@ Renderer *renderer_create(RendererDesc *desc) {
layer.device = r->device; layer.device = r->device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm; layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.framebufferOnly = YES; layer.framebufferOnly = YES;
layer.maximumDrawableCount = NUM_BACK_BUFFERS;
NSWindow *window = [view window]; NSWindow *window = [view window];
r->backing_scale = (F32)[window backingScaleFactor]; r->backing_scale = (F32)[window backingScaleFactor];
@@ -672,8 +681,8 @@ Renderer *renderer_create(RendererDesc *desc) {
return nullptr; return nullptr;
} }
// Create double-buffered vertex/index buffers // Create F64-buffered vertex/index buffers
for (int i = 0; i < NUM_BACK_BUFFERS; i++) { for (S32 i = 0; i < NUM_BACK_BUFFERS; i++) {
r->vertex_buffers[i] = [r->device newBufferWithLength:MAX_VERTICES * sizeof(UIVertex) r->vertex_buffers[i] = [r->device newBufferWithLength:MAX_VERTICES * sizeof(UIVertex)
options:MTLResourceStorageModeShared]; options:MTLResourceStorageModeShared];
r->index_buffers[i] = [r->device newBufferWithLength:MAX_INDICES * sizeof(U32) r->index_buffers[i] = [r->device newBufferWithLength:MAX_INDICES * sizeof(U32)
@@ -704,14 +713,16 @@ void renderer_destroy(Renderer *r) {
delete r; delete r;
} }
bool renderer_begin_frame(Renderer *r) { B32 renderer_begin_frame(Renderer *r) {
if (r->width <= 0 || r->height <= 0) return false; if (r->width <= 0 || r->height <= 0) return false;
// Wait for an in-flight frame to finish, then acquire a drawable. // Wait for an in-flight frame to finish, then acquire a drawable.
// Doing this BEFORE input sampling ensures the freshest mouse position // Doing this BEFORE input sampling ensures the freshest mouse position
// is used for rendering, reducing perceived drag latency. // is used for rendering, reducing perceived drag latency.
dispatch_semaphore_wait(r->frame_semaphore, DISPATCH_TIME_FOREVER); 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) { if (!r->current_drawable) {
dispatch_semaphore_signal(r->frame_semaphore); dispatch_semaphore_signal(r->frame_semaphore);
return false; return false;
@@ -724,7 +735,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
id<CAMetalDrawable> drawable = r->current_drawable; id<CAMetalDrawable> drawable = r->current_drawable;
r->current_drawable = nil; 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]; MTLRenderPassDescriptor *pass = [MTLRenderPassDescriptor renderPassDescriptor];
pass.colorAttachments[0].texture = drawable.texture; pass.colorAttachments[0].texture = drawable.texture;
@@ -741,8 +752,8 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
// Viewport // Viewport
MTLViewport viewport = {}; MTLViewport viewport = {};
viewport.width = (double)r->width; viewport.width = (F64)r->width;
viewport.height = (double)r->height; viewport.height = (F64)r->height;
viewport.zfar = 1.0; viewport.zfar = 1.0;
[encoder setViewport:viewport]; [encoder setViewport:viewport];
@@ -751,7 +762,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
[encoder setScissorRect:full_scissor]; [encoder setScissorRect:full_scissor];
// Constants // 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]; [encoder setVertexBytes:constants length:sizeof(constants) atIndex:1];
// Process Clay render commands // Process Clay render commands
@@ -763,7 +774,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
batch.index_count = 0; batch.index_count = 0;
// Track which texture is currently bound (0 = font, 1 = icon) // Track which texture is currently bound (0 = font, 1 = icon)
int bound_texture = 0; S32 bound_texture = 0;
U32 flush_index_start = 0; U32 flush_index_start = 0;
auto flush_batch = [&]() { 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_RenderCommand *cmd = Clay_RenderCommandArray_Get(&render_commands, i);
Clay_BoundingBox bb = cmd->boundingBox; 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: { case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *border = &cmd->renderData.border; Clay_BorderRenderData *border = &cmd->renderData.border;
Clay_Color c = border->color; Clay_Color c = border->color;
float cr_norm = c.r / 255.f; F32 cr_norm = c.r / 255.f;
float cg_norm = c.g / 255.f; F32 cg_norm = c.g / 255.f;
float cb_norm = c.b / 255.f; F32 cb_norm = c.b / 255.f;
float ca_norm = c.a / 255.f; F32 ca_norm = c.a / 255.f;
// Use SDF rounded border when corner radius is present and widths are uniform // Use SDF rounded border when corner radius is present and widths are uniform
Clay_CornerRadius cr = border->cornerRadius; Clay_CornerRadius cr = border->cornerRadius;
bool has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0; B32 has_radius = cr.topLeft > 0 || cr.topRight > 0 || cr.bottomLeft > 0 || cr.bottomRight > 0;
bool uniform = border->width.top == border->width.bottom && B32 uniform = border->width.top == border->width.bottom &&
border->width.top == border->width.left && border->width.top == border->width.left &&
border->width.top == border->width.right && border->width.top == border->width.right &&
border->width.top > 0; 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, bb.x, bb.y, bb.x + bb.width, bb.y + bb.height,
cr_norm, cg_norm, cb_norm, ca_norm, cr_norm, cg_norm, cb_norm, ca_norm,
cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft, cr.topLeft, cr.topRight, cr.bottomRight, cr.bottomLeft,
(float)border->width.top, 1.0f); (F32)border->width.top, 1.0f);
} else { } else {
if (border->width.top > 0) { if (border->width.top > 0) {
emit_rect(&batch, bb.x, bb.y, bb.x + bb.width, bb.y + border->width.top, 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(); bind_icon_texture();
CustomIconData *icon = (CustomIconData *)custom->customData; CustomIconData *icon = (CustomIconData *)custom->customData;
Clay_Color c = icon->color; Clay_Color c = icon->color;
float cr = c.r / 255.f, cg = c.g / 255.f; F32 cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f; F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[icon->icon_id]; UI_IconInfo *info = &g_icons[icon->icon_id];
emit_quad(&batch, emit_quad(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, 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(); bind_icon_texture();
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData; CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
Clay_Color c = ri->color; Clay_Color c = ri->color;
float cr = c.r / 255.f, cg = c.g / 255.f; F32 cr = c.r / 255.f, cg = c.g / 255.f;
float cb = c.b / 255.f, ca = c.a / 255.f; F32 cb = c.b / 255.f, ca = c.a / 255.f;
UI_IconInfo *info = &g_icons[ri->icon_id]; UI_IconInfo *info = &g_icons[ri->icon_id];
emit_quad_rotated(&batch, emit_quad_rotated(&batch,
bb.x, bb.y, bb.x + bb.width, bb.y + bb.height, 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]; [cmd_buf commit];
[drawable release];
} }
r->frame_index++; 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]; MTLTextureDescriptor *tex_desc = [[MTLTextureDescriptor alloc] init];
tex_desc.pixelFormat = MTLPixelFormatRGBA8Unorm; tex_desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
tex_desc.width = w; 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]; 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_r = cr;
r->clear_g = cg; r->clear_g = cg;
r->clear_b = cb; r->clear_b = cb;
} }
void renderer_set_font_scale(Renderer *r, float scale) { void renderer_set_font_scale(Renderer *r, F32 scale) {
float target_size = 15.0f * scale; F32 target_size = 15.0f * scale;
if (fabsf(target_size - r->font_atlas_size) < 0.1f) return; 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_texture = nil;
r->font_sampler = nil; r->font_sampler = nil;
create_font_atlas(r, target_size); 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; if (width <= 0 || height <= 0) return;
r->width = width; r->width = width;
r->height = height; r->height = height;

View File

@@ -146,7 +146,7 @@ void ui_set_accent(S32 accent_id) {
static void clay_error_handler(Clay_ErrorData error) { static void clay_error_handler(Clay_ErrorData error) {
char buf[512]; 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); memcpy(buf, error.errorText.chars, len);
buf[len] = '\0'; buf[len] = '\0';
fprintf(stderr, "[Clay Error] %s\n", buf); 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) { static Clay_Dimensions clay_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *user_data) {
UI_Context *ctx = (UI_Context *)user_data; UI_Context *ctx = (UI_Context *)user_data;
if (!ctx || !ctx->measure_text_fn || text.length == 0) { 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); 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}; 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 *ui_create(F32 viewport_w, F32 viewport_h) {
UI_Context *ctx = (UI_Context *)calloc(1, sizeof(UI_Context)); 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); ctx->clay_memory = malloc(min_memory);
Clay_Arena clay_arena = Clay_CreateArenaWithCapacityAndMemory(min_memory, ctx->clay_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; extern F32 g_ui_scale;
// Scale a float value (for CLAY_SIZING_FIXED, corner radii, etc.) // Scale a F32 value (for CLAY_SIZING_FIXED, corner radii, etc.)
static inline float uis(float x) { return x * g_ui_scale; } static inline F32 uis(F32 x) { return x * g_ui_scale; }
// Scale to uint16_t (for Clay_Padding, childGap, etc.) // Scale to U16 (for Clay_Padding, childGap, etc.)
static inline uint16_t uip(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } static inline U16 uip(F32 x) { return (U16)(x * g_ui_scale + 0.5f); }
// Scale to uint16_t font size // Scale to U16 font size
static inline uint16_t uifs(float x) { return (uint16_t)(x * g_ui_scale + 0.5f); } static inline U16 uifs(F32 x) { return (U16)(x * g_ui_scale + 0.5f); }
//////////////////////////////// ////////////////////////////////
// Tab styling // 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) // Per-frame shadow layer ID counter (each shadow uses N unique IDs)
static S32 g_shadow_id_counter = 0; 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; static S32 g_frame_number = 0;
// Emit a smooth multi-layer drop shadow as floating rects. // Emit a smooth multi-layer drop shadow as floating rects.
@@ -59,16 +59,16 @@ static S32 g_frame_number = 0;
#define SHADOW_LAYERS 7 #define SHADOW_LAYERS 7
static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius, static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
float peak_alpha, int16_t z, F32 peak_alpha, S16 z,
B32 attach_to_root, uint32_t parent_id, B32 attach_to_root, U32 parent_id,
Clay_FloatingAttachPointType parent_attach) { Clay_FloatingAttachPointType parent_attach) {
if (bb.width <= 0) return; 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 // Draw outermost first (largest, lowest alpha contribution), innermost last
for (S32 i = SHADOW_LAYERS - 1; i >= 0; i--) { 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; F32 expand = radius * t;
S32 sid = g_shadow_id_counter++; S32 sid = g_shadow_id_counter++;
@@ -268,8 +268,8 @@ B32 ui_button(const char *id, const char *text) {
B32 hovered = Clay_PointerOver(eid); B32 hovered = Clay_PointerOver(eid);
Clay_Color base = hovered ? g_theme.accent_hover : g_theme.accent; 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 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 = {(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 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); CustomGradientData *grad = alloc_gradient(top, bot);
CLAY(eid, CLAY(eid,
@@ -526,7 +526,7 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
// Key events // Key events
for (S32 k = 0; k < g_wstate.input.key_count; k++) { 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 // Skip Tab — handled via tab cycling
if (key == PKEY_TAB) continue; 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) // Character input (printable only, skip control chars)
if (is_focused) { if (is_focused) {
for (S32 c = 0; c < g_wstate.input.char_count; c++) { 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) { if (ch >= 32 && ch < 127) {
// Delete selection first if any // Delete selection first if any
if (text_input_has_sel()) { 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; Clay_Color border_color = is_focused ? g_theme.accent : g_theme.border;
// Inset effect: darker top, lighter bottom (recessed look) // 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_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 = {(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_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); CustomGradientData *inset_grad = alloc_gradient(inset_top, inset_bot);
CLAY(eid, 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]; static char dd_trunc_buf[256];
const char *display_label = current_label; const char *display_label = current_label;
Clay_ElementId text_eid = WIDI(id, 500); 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) { if (avail_w > 0) {
S32 label_len = (S32)strlen(current_label); S32 label_len = (S32)strlen(current_label);
Vec2F32 text_size = ui_measure_text(current_label, label_len, FONT_SIZE_NORMAL); Vec2F32 text_size = ui_measure_text(current_label, label_len, FONT_SIZE_NORMAL);
if (text_size.x > avail_w) { if (text_size.x > avail_w) {
Vec2F32 dots = ui_measure_text("...", 3, FONT_SIZE_NORMAL); 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; S32 lo = 0, hi = label_len;
while (lo < hi) { while (lo < hi) {
S32 mid = (lo + hi + 1) / 2; 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; Clay_Color bg = is_open ? g_theme.bg_dark : g_theme.bg_medium;
if (header_hovered && !is_open) bg = g_theme.bg_lighter; 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); CustomGradientData *dd_grad = alloc_gradient(dd_top, bg);
CLAY(eid, 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) // Draw dropdown list if open (floating so it escapes modal/container clipping)
if (is_open) { if (is_open) {
Clay_ElementId list_id = WIDI(id, 502); 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 // Dropdown list shadow
{ {
@@ -1052,7 +1052,7 @@ S32 ui_modal(const char *id, const char *title, const char *message,
// Title bar (gradient: lighter top) // Title bar (gradient: lighter top)
{ {
Clay_Color mtb = g_theme.title_bar; 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); CustomGradientData *mtb_grad = alloc_gradient(mtb_top, mtb);
CLAY(WIDI(id, 2002), 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); B32 btn_hovered = Clay_PointerOver(btn_id);
Clay_Color mbtn_base = btn_hovered ? g_theme.accent_hover : g_theme.accent; 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_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 = {(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_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); CustomGradientData *mbtn_grad = alloc_gradient(mbtn_top, mbtn_bot);
CLAY(btn_id, CLAY(btn_id,
@@ -1136,7 +1136,7 @@ S32 ui_modal(const char *id, const char *title, const char *message,
//////////////////////////////// ////////////////////////////////
// Draggable window // 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 // Look for existing slot
for (S32 i = 0; i < g_wstate.window_count; i++) { for (S32 i = 0; i < g_wstate.window_count; i++) {
if (g_wstate.windows[i].id == id) { 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) { static void bring_window_to_front(UI_WindowSlot *slot) {
// Renormalize if approaching modal z-range // Renormalize if approaching modal z-range
if (g_wstate.next_z > 800) { 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; S32 count = g_wstate.window_count;
for (S32 i = 0; i < count; i++) sorted[i] = g_wstate.windows[i].z_order; for (S32 i = 0; i < count; i++) sorted[i] = g_wstate.windows[i].z_order;
// Bubble sort (tiny array) // Bubble sort (tiny array)
for (S32 i = 0; i < count - 1; i++) { for (S32 i = 0; i < count - 1; i++) {
for (S32 j = i + 1; j < count; j++) { for (S32 j = i + 1; j < count; j++) {
if (sorted[j] < sorted[i]) { 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 i = 0; i < count; i++) {
for (S32 j = 0; j < count; j++) { for (S32 j = 0; j < count; j++) {
if (g_wstate.windows[j].z_order == sorted[i]) { 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++; 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 // 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 }; 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), 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); 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), .cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
.floating = { .floating = {
.offset = { slot->position.x, slot->position.y }, .offset = { slot->position.x, slot->position.y },
.zIndex = (int16_t)(100 + slot->z_order), .zIndex = (S16)(100 + slot->z_order),
.attachPoints = { .attachPoints = {
.element = CLAY_ATTACH_POINT_LEFT_TOP, .element = CLAY_ATTACH_POINT_LEFT_TOP,
.parent = 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) // Title bar (gradient: lighter top)
Clay_Color tb = g_theme.title_bar; 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); CustomGradientData *tb_grad = alloc_gradient(tb_top, tb);
CLAY(title_bar_id, 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 } }, .border = { .color = g_theme.border, .width = { .bottom = 1 } },
) { ) {
for (S32 i = 0; i < count; i++) { 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 is_active = (i == *selected);
B32 hovered = Clay_PointerOver(tab_eid); 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++) { 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 (ctrl) {
if (key == PKEY_A) { 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) { if (!commit && !cancel) {
for (S32 c = 0; c < g_wstate.input.char_count; c++) { 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 == '-'; B32 valid = (ch >= '0' && ch <= '9') || ch == '.' || ch == '-';
if (valid) { if (valid) {
if (KE_HAS_SEL()) elen = ke_delete_sel(); 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. // Render the text edit box with selection/cursor display.
static void value_edit_render(uint32_t hash, F32 width) { static void value_edit_render(U32 hash, F32 width) {
Clay_ElementId edit_eid = CLAY_IDI("ValEdit", (int)hash); Clay_ElementId edit_eid = CLAY_IDI("ValEdit", (S32)hash);
char *ebuf = g_wstate.knob_edit_buf; char *ebuf = g_wstate.knob_edit_buf;
S32 elen = (S32)strlen(ebuf); 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; S32 n = sel_hi - sel_lo;
memcpy(ke_dbuf_sel, &ebuf[sel_lo], n); ke_dbuf_sel[n] = '\0'; memcpy(ke_dbuf_sel, &ebuf[sel_lo], n); ke_dbuf_sel[n] = '\0';
Clay_String s_sel = { .length = n, .chars = ke_dbuf_sel }; 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() } }, .layout = { .sizing = { .width = CLAY_SIZING_FIT(), .height = CLAY_SIZING_FIT() } },
.backgroundColor = g_theme.accent .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). // Get the Clay element ID of the current edit box (for click-away detection).
static Clay_ElementId value_edit_eid(uint32_t hash) { static Clay_ElementId value_edit_eid(U32 hash) {
return CLAY_IDI("ValEdit", (int)hash); return CLAY_IDI("ValEdit", (S32)hash);
} }
// Handle click-outside to commit the edit. // 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. // 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.knob_edit_id = hash;
g_wstate.focused_id = 0; g_wstate.focused_id = 0;
if (is_signed) { 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 deg_to_rad = 3.14159265f / 180.0f;
F32 angle_rad = (-135.0f + normalized * 270.0f) * deg_to_rad; 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); B32 is_editing = (editable && g_wstate.knob_edit_id == knob_hash);
UI_KnobDragState *kd = &g_wstate.knob_drag; 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, .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); B32 hovered = Clay_PointerOver(knob_eid);
CLAY(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) { if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == knob_hash && B32 is_double_click = (kd->last_click_id == knob_hash &&
(g_frame_number - kd->last_click_frame) < 20); (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_render(knob_hash, knob_size);
value_edit_click_away(value_edit_eid(knob_hash), value, max_val, is_signed, &changed); value_edit_click_away(value_edit_eid(knob_hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) { } 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); B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; 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; B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed); 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); B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag; 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); CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass thumb travel) // 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); B32 hovered = Clay_PointerOver(hit_eid);
CLAY(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) // Visible track (centered inside hit area)
CLAY(CLAY_IDI("SlHTrack", (int)hash), CLAY(CLAY_IDI("SlHTrack", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, .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 // Fill bar
F32 fill_w = normalized * track_w; F32 fill_w = normalized * track_w;
if (fill_w < 1.0f) fill_w = 1.0f; if (fill_w < 1.0f) fill_w = 1.0f;
CLAY(CLAY_IDI("SlHFill", (int)hash), CLAY(CLAY_IDI("SlHFill", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(fill_w), .height = CLAY_SIZING_GROW() }, .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->icon_id = (S32)UI_ICON_SLIDER_THUMB;
idata->color = g_theme.accent; idata->color = g_theme.accent;
CLAY(CLAY_IDI("SlHThumb", (int)hash), CLAY(CLAY_IDI("SlHThumb", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, .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) { if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash && B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20); (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_render(hash, track_w);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) { } 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); B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; 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; B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed); 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); B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag; 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); CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass thumb travel) // 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); B32 hovered = Clay_PointerOver(hit_eid);
CLAY(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) // Visible track (centered inside hit area)
CLAY(CLAY_IDI("SlVTrack", (int)hash), CLAY(CLAY_IDI("SlVTrack", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, .sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) },
.childAlignment = { .y = CLAY_ALIGN_Y_BOTTOM }, .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) // Fill bar (from bottom)
F32 fill_h = normalized * track_h; F32 fill_h = normalized * track_h;
if (fill_h < 1.0f) fill_h = 1.0f; if (fill_h < 1.0f) fill_h = 1.0f;
CLAY(CLAY_IDI("SlVFill", (int)hash), CLAY(CLAY_IDI("SlVFill", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) }, .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->icon_id = (S32)UI_ICON_SLIDER_THUMB;
idata->color = g_theme.accent; idata->color = g_theme.accent;
CLAY(CLAY_IDI("SlVThumb", (int)hash), CLAY(CLAY_IDI("SlVThumb", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) }, .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) { if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash && B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20); (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_render(hash, thumb_w * 2.0f);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) { } 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); B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; 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; B32 changed = 0;
F32 normalized = value_normalize(*value, max_val, is_signed); 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); B32 is_editing = (editable && g_wstate.knob_edit_id == hash);
UI_KnobDragState *kd = &g_wstate.knob_drag; 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); CLAY_TEXT(clay_str(label), &g_widget_text_config_dim);
// Hit area (transparent, sized to encompass fader cap travel) // 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); B32 hovered = Clay_PointerOver(hit_eid);
CLAY(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) // Visible track (centered inside hit area, empty)
CLAY(CLAY_IDI("FdrTrack", (int)hash), CLAY(CLAY_IDI("FdrTrack", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) }, .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; F32 tw = (i % 5 == 0) ? tick_major_w : tick_minor_w;
// Left tick // Left tick
CLAY(CLAY_IDI("FdrTkL", (int)(hash * 100 + i)), CLAY(CLAY_IDI("FdrTkL", (S32)(hash * 100 + i)),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, .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 // Right tick
CLAY(CLAY_IDI("FdrTkR", (int)(hash * 100 + i)), CLAY(CLAY_IDI("FdrTkR", (S32)(hash * 100 + i)),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) }, .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); F32 cap_y = (1.0f - normalized) * (track_h - cap_h);
CLAY(CLAY_IDI("FdrCap", (int)hash), CLAY(CLAY_IDI("FdrCap", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, .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; Clay_Color tint = g_theme.accent;
tint.a = 80; tint.a = 80;
F32 cap_corner = cap_w * (3.0f / 62.2f); F32 cap_corner = cap_w * (3.0f / 62.2f);
CLAY(CLAY_IDI("FdrTint", (int)hash), CLAY(CLAY_IDI("FdrTint", (S32)hash),
.layout = { .layout = {
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) }, .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) { if (!is_editing && hovered && g_wstate.mouse_clicked && kd->dragging_id == 0) {
B32 is_double_click = (kd->last_click_id == hash && B32 is_double_click = (kd->last_click_id == hash &&
(g_frame_number - kd->last_click_frame) < 20); (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_render(hash, cap_w * 2.0f);
value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed); value_edit_click_away(value_edit_eid(hash), value, max_val, is_signed, &changed);
} else if (val_text && val_len > 0) { } 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); B32 val_hovered = Clay_PointerOver(val_eid);
Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text }; Clay_String val_str = { .isStaticallyAllocated = false, .length = val_len, .chars = val_text };

View File

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