[event system] allocate events and additional payload (e.g. file paths) directly in ring buffer, and copy them to user-supplied arena in mp_next_event()

This commit is contained in:
Martin Fouilleul 2023-04-19 16:16:36 +02:00
parent ab150f94f2
commit bb6b68ad73
9 changed files with 178 additions and 84 deletions

View File

@ -116,51 +116,51 @@ void mp_input_next_frame(mp_input_state* state)
state->frameCounter++; state->frameCounter++;
} }
void mp_input_process_event(mp_input_state* state, mp_event event) void mp_input_process_event(mp_input_state* state, mp_event* event)
{ {
switch(event.type) switch(event->type)
{ {
case MP_EVENT_KEYBOARD_KEY: case MP_EVENT_KEYBOARD_KEY:
{ {
mp_key_state* key = &state->keyboard.keys[event.key.code]; mp_key_state* key = &state->keyboard.keys[event->key.code];
mp_update_key_state(state, key, event.key.action); mp_update_key_state(state, key, event->key.action);
mp_update_key_mods(state, event.key.mods); mp_update_key_mods(state, event->key.mods);
} break; } break;
case MP_EVENT_KEYBOARD_CHAR: case MP_EVENT_KEYBOARD_CHAR:
mp_update_text(state, event.character.codepoint); mp_update_text(state, event->character.codepoint);
break; break;
case MP_EVENT_KEYBOARD_MODS: case MP_EVENT_KEYBOARD_MODS:
mp_update_key_mods(state, event.key.mods); mp_update_key_mods(state, event->key.mods);
break; break;
case MP_EVENT_MOUSE_MOVE: case MP_EVENT_MOUSE_MOVE:
mp_update_mouse_move(state, event.move.x, event.move.y, event.move.deltaX, event.move.deltaY); mp_update_mouse_move(state, event->move.x, event->move.y, event->move.deltaX, event->move.deltaY);
break; break;
case MP_EVENT_MOUSE_WHEEL: case MP_EVENT_MOUSE_WHEEL:
mp_update_mouse_wheel(state, event.move.deltaX, event.move.deltaY); mp_update_mouse_wheel(state, event->move.deltaX, event->move.deltaY);
break; break;
case MP_EVENT_MOUSE_BUTTON: case MP_EVENT_MOUSE_BUTTON:
{ {
mp_key_state* key = &state->mouse.buttons[event.key.code]; mp_key_state* key = &state->mouse.buttons[event->key.code];
mp_update_key_state(state, key, event.key.action); mp_update_key_state(state, key, event->key.action);
if(event.key.action == MP_KEY_PRESS) if(event->key.action == MP_KEY_PRESS)
{ {
if(event.key.clickCount >= 1) if(event->key.clickCount >= 1)
{ {
key->sysClicked = true; key->sysClicked = true;
} }
if(event.key.clickCount >= 2) if(event->key.clickCount >= 2)
{ {
key->sysDoubleClicked = true; key->sysDoubleClicked = true;
} }
} }
mp_update_key_mods(state, event.key.mods); mp_update_key_mods(state, event->key.mods);
} break; } break;
default: default:

View File

@ -70,7 +70,7 @@ typedef struct mp_input_state
mp_text_state text; mp_text_state text;
} mp_input_state; } mp_input_state;
MP_API void mp_input_process_event(mp_input_state* state, mp_event event); MP_API void mp_input_process_event(mp_input_state* state, mp_event* event);
MP_API void mp_input_next_frame(mp_input_state* state); MP_API void mp_input_next_frame(mp_input_state* state);
MP_API bool mp_key_down(mp_input_state* state, mp_key_code key); MP_API bool mp_key_down(mp_input_state* state, mp_key_code key);

View File

@ -97,28 +97,85 @@ static void mp_terminate_common()
void mp_queue_event(mp_event* event) void mp_queue_event(mp_event* event)
{ {
if(ringbuffer_write_available(&__mpApp.eventQueue) < sizeof(mp_event)) ringbuffer* queue = &__mpApp.eventQueue;
if(ringbuffer_write_available(queue) < sizeof(mp_event))
{ {
log_error("event queue full\n"); log_error("event queue full\n");
} }
else else
{ {
u32 written = ringbuffer_write(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event); bool error = false;
DEBUG_ASSERT(written == sizeof(mp_event)); ringbuffer_reserve(queue, sizeof(mp_event), (u8*)event);
}
}
bool mp_next_event(mp_event* event) if(event->type == MP_EVENT_PATHDROP)
{ {
//NOTE pop and return event from queue for_list(&event->paths.list, elt, str8_elt, listElt)
if(ringbuffer_read_available(&__mpApp.eventQueue) >= sizeof(mp_event))
{ {
u64 read = ringbuffer_read(&__mpApp.eventQueue, sizeof(mp_event), (u8*)event); str8* path = &elt->string;
DEBUG_ASSERT(read == sizeof(mp_event)); if(ringbuffer_write_available(queue) < (sizeof(u64) + path->len))
return(true); {
log_error("event queue full\n");
error = true;
break;
} }
else else
{ {
return(false); ringbuffer_reserve(queue, sizeof(u64), (u8*)&path->len);
ringbuffer_reserve(queue, path->len, (u8*)path->ptr);
} }
} }
}
if(error)
{
ringbuffer_rewind(queue);
}
else
{
ringbuffer_commit(queue);
}
}
}
mp_event* mp_next_event(mem_arena* arena)
{
//NOTE: pop and return event from queue
mp_event* event = 0;
ringbuffer* queue = &__mpApp.eventQueue;
if(ringbuffer_read_available(queue) >= sizeof(mp_event))
{
event = mem_arena_alloc_type(arena, mp_event);
u64 read = ringbuffer_read(queue, sizeof(mp_event), (u8*)event);
DEBUG_ASSERT(read == sizeof(mp_event));
if(event->type == MP_EVENT_PATHDROP)
{
u64 pathCount = event->paths.eltCount;
event->paths = (str8_list){0};
for(int i=0; i<pathCount; i++)
{
if(ringbuffer_read_available(queue) < sizeof(u64))
{
log_error("malformed path payload: no string size\n");
break;
}
u64 len = 0;
ringbuffer_read(queue, sizeof(u64), (u8*)&len);
if(ringbuffer_read_available(queue) < len)
{
log_error("malformed path payload: string shorter than expected\n");
break;
}
char* buffer = mem_arena_alloc_array(arena, char, len);
ringbuffer_read(queue, len, (u8*)buffer);
str8_list_push(arena, &event->paths, str8_from_buffer(len, buffer));
}
}
}
return(event);
}

View File

@ -12,6 +12,7 @@
#include"typedefs.h" #include"typedefs.h"
#include"utf8.h" #include"utf8.h"
#include"lists.h" #include"lists.h"
#include"memory.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -237,7 +238,6 @@ typedef struct mp_frame_event // window resize / move
typedef struct mp_event typedef struct mp_event
{ {
//TODO clipboard and path drop //TODO clipboard and path drop
mp_window window; mp_window window;
mp_event_type type; mp_event_type type;
@ -247,11 +247,9 @@ typedef struct mp_event
mp_char_event character; mp_char_event character;
mp_move_event move; mp_move_event move;
mp_frame_event frame; mp_frame_event frame;
str8 path; str8_list paths;
}; };
//TODO(martin): chain externally ?
list_elt list;
} mp_event; } mp_event;
//-------------------------------------------------------------------- //--------------------------------------------------------------------
@ -272,7 +270,7 @@ MP_API void mp_set_cursor(mp_mouse_cursor cursor);
//-------------------------------------------------------------------- //--------------------------------------------------------------------
MP_API void mp_pump_events(f64 timeout); MP_API void mp_pump_events(f64 timeout);
MP_API bool mp_next_event(mp_event* event); MP_API mp_event* mp_next_event(mem_arena* arena);
typedef void(*mp_live_resize_callback)(mp_event event, void* data); typedef void(*mp_live_resize_callback)(mp_event event, void* data);
MP_API void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data); MP_API void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data);

View File

@ -475,12 +475,20 @@ void mp_install_keyboard_layout_listener()
- (BOOL)application:(NSApplication *)application openFile:(NSString *)filename - (BOOL)application:(NSApplication *)application openFile:(NSString *)filename
{ {
mp_event event = {}; mp_event event = {0};
event.window = (mp_window){0}; event.window = (mp_window){0};
event.type = MP_EVENT_PATHDROP; event.type = MP_EVENT_PATHDROP;
event.path = str8_push_cstring(&__mpApp.eventArena, [filename UTF8String]);
mem_arena* scratch = mem_scratch();
mem_arena_marker mark = mem_arena_mark(scratch);
str8 path = str8_push_cstring(scratch, [filename UTF8String]);
str8_list_push(scratch, &event.paths, path);
mp_queue_event(&event); mp_queue_event(&event);
mem_arena_clear_to(scratch, mark);
return(YES); return(YES);
} }
@ -491,11 +499,20 @@ void mp_install_keyboard_layout_listener()
mp_event event = {}; mp_event event = {};
event.window = (mp_window){0}; event.window = (mp_window){0};
event.type = MP_EVENT_PATHDROP; event.type = MP_EVENT_PATHDROP;
event.path = str8_push_cstring(&__mpApp.eventArena, [nsPath UTF8String]);
mem_arena* scratch = mem_scratch();
mem_arena_marker mark = mem_arena_mark(scratch);
str8 path = str8_push_cstring(scratch, [nsPath UTF8String]);
str8_list_push(scratch, &event.paths, path);
mp_queue_event(&event); mp_queue_event(&event);
mem_arena_clear_to(scratch, mark);
} }
//TODO: drag and drop paths
@end // @implementation MPAppDelegate @end // @implementation MPAppDelegate
//--------------------------------------------------------------- //---------------------------------------------------------------

View File

@ -327,7 +327,7 @@ void ui_style_box_after(ui_box* box, ui_pattern pattern, ui_style* style, ui_sty
// input // input
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ui_process_event(mp_event event) void ui_process_event(mp_event* event)
{ {
ui_context* ui = ui_get_context(); ui_context* ui = ui_get_context();
mp_input_process_event(&ui->input, event); mp_input_process_event(&ui->input, event);

View File

@ -395,7 +395,7 @@ MP_API void ui_init(ui_context* context);
MP_API ui_context* ui_get_context(void); MP_API ui_context* ui_get_context(void);
MP_API void ui_set_context(ui_context* context); MP_API void ui_set_context(ui_context* context);
MP_API void ui_process_event(mp_event event); MP_API void ui_process_event(mp_event* event);
MP_API void ui_begin_frame(ui_style* defaultStyle, ui_style_mask mask); MP_API void ui_begin_frame(ui_style* defaultStyle, ui_style_mask mask);
MP_API void ui_end_frame(void); MP_API void ui_end_frame(void);
MP_API void ui_draw(void); MP_API void ui_draw(void);

View File

@ -11,9 +11,10 @@
void ringbuffer_init(ringbuffer* ring, u8 capExp) void ringbuffer_init(ringbuffer* ring, u8 capExp)
{ {
u32 cap = 1<<capExp; u64 cap = 1<<capExp;
ring->mask = cap - 1; ring->mask = cap - 1;
ring->readIndex = 0; ring->readIndex = 0;
ring->reserveIndex = 0;
ring->writeIndex = 0; ring->writeIndex = 0;
ring->buffer = (u8*)malloc(cap); ring->buffer = (u8*)malloc(cap);
} }
@ -23,54 +24,24 @@ void ringbuffer_cleanup(ringbuffer* ring)
free(ring->buffer); free(ring->buffer);
} }
u32 ringbuffer_read_available(ringbuffer* ring) u64 ringbuffer_read_available(ringbuffer* ring)
{ {
return((ring->writeIndex - ring->readIndex) & ring->mask); return((ring->writeIndex - ring->readIndex) & ring->mask);
} }
u32 ringbuffer_write_available(ringbuffer* ring) u64 ringbuffer_write_available(ringbuffer* ring)
{ {
//NOTE(martin): we keep one sentinel byte between write index and read index, //NOTE(martin): we keep one sentinel byte between write index and read index,
// when the buffer is full, to avoid overrunning read index. // when the buffer is full, to avoid overrunning read index.
// Hence, available write space is size - 1 - available read space. return(((ring->readIndex - ring->reserveIndex) & ring->mask) - 1);
return(ring->mask - ringbuffer_read_available(ring));
} }
u32 ringbuffer_write(ringbuffer* ring, u32 size, u8* data) u64 ringbuffer_read(ringbuffer* ring, u64 size, u8* data)
{ {
u32 read = ring->readIndex; u64 read = ring->readIndex;
u32 write = ring->writeIndex; u64 write = ring->writeIndex;
u32 writeAvailable = ringbuffer_write_available(ring); u64 readAvailable = ringbuffer_read_available(ring);
if(size > writeAvailable)
{
DEBUG_ASSERT("not enough space available");
size = writeAvailable;
}
if(read <= write)
{
u32 copyCount = minimum(size, ring->mask + 1 - write);
memcpy(ring->buffer + write, data, copyCount);
data += copyCount;
copyCount = size - copyCount;
memcpy(ring->buffer, data, copyCount);
}
else
{
memcpy(ring->buffer + write, data, size);
}
ring->writeIndex = (write + size) & ring->mask;
return(size);
}
u32 ringbuffer_read(ringbuffer* ring, u32 size, u8* data)
{
u32 read = ring->readIndex;
u32 write = ring->writeIndex;
u32 readAvailable = ringbuffer_read_available(ring);
if(size > readAvailable) if(size > readAvailable)
{ {
size = readAvailable; size = readAvailable;
@ -82,7 +53,7 @@ u32 ringbuffer_read(ringbuffer* ring, u32 size, u8* data)
} }
else else
{ {
u32 copyCount = minimum(size, ring->mask + 1 - read); u64 copyCount = minimum(size, ring->mask + 1 - read);
memcpy(data, ring->buffer + read, copyCount); memcpy(data, ring->buffer + read, copyCount);
data += copyCount; data += copyCount;
@ -92,3 +63,50 @@ u32 ringbuffer_read(ringbuffer* ring, u32 size, u8* data)
ring->readIndex = (read + size) & ring->mask; ring->readIndex = (read + size) & ring->mask;
return(size); return(size);
} }
u64 ringbuffer_reserve(ringbuffer* ring, u64 size, u8* data)
{
u64 read = ring->readIndex;
u64 reserve = ring->reserveIndex;
u64 writeAvailable = ringbuffer_write_available(ring);
if(size > writeAvailable)
{
DEBUG_ASSERT("not enough space available");
size = writeAvailable;
}
if(read <= reserve)
{
u64 copyCount = minimum(size, ring->mask + 1 - reserve);
memcpy(ring->buffer + reserve, data, copyCount);
data += copyCount;
copyCount = size - copyCount;
memcpy(ring->buffer, data, copyCount);
}
else
{
memcpy(ring->buffer + reserve, data, size);
}
ring->reserveIndex = (reserve + size) & ring->mask;
return(size);
}
u64 ringbuffer_write(ringbuffer* ring, u64 size, u8* data)
{
ringbuffer_commit(ring);
u64 res = ringbuffer_reserve(ring, size, data);
ringbuffer_commit(ring);
return(res);
}
void ringbuffer_commit(ringbuffer* ring)
{
ring->writeIndex = ring->reserveIndex;
}
void ringbuffer_rewind(ringbuffer* ring)
{
ring->reserveIndex = ring->writeIndex;
}

View File

@ -18,9 +18,10 @@ extern "C" {
typedef struct ringbuffer typedef struct ringbuffer
{ {
u32 mask; u64 mask;
_Atomic(u32) readIndex; _Atomic(u64) readIndex;
_Atomic(u32) writeIndex; _Atomic(u64) writeIndex;
u64 reserveIndex;
u8* buffer; u8* buffer;
@ -28,10 +29,13 @@ typedef struct ringbuffer
void ringbuffer_init(ringbuffer* ring, u8 capExp); void ringbuffer_init(ringbuffer* ring, u8 capExp);
void ringbuffer_cleanup(ringbuffer* ring); void ringbuffer_cleanup(ringbuffer* ring);
u32 ringbuffer_read_available(ringbuffer* ring); u64 ringbuffer_read_available(ringbuffer* ring);
u32 ringbuffer_write_available(ringbuffer* ring); u64 ringbuffer_write_available(ringbuffer* ring);
u32 ringbuffer_write(ringbuffer* ring, u32 size, u8* data); u64 ringbuffer_read(ringbuffer* ring, u64 size, u8* data);
u32 ringbuffer_read(ringbuffer* ring, u32 size, u8* data); u64 ringbuffer_write(ringbuffer* ring, u64 size, u8* data);
u64 ringbuffer_reserve(ringbuffer* ring, u64 size, u8* data);
void ringbuffer_commit(ringbuffer* ring);
void ringbuffer_rewind(ringbuffer* ring);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"