#define AL_LOG_SECTION "window" #include #include "window.h" #include "keys.h" static bool nop_pointer_pos_callback(void *userdata, f64 x, f64 y) { (void)userdata; (void)x; (void)y; return false; } static bool nop_touch_callback(void *userdata, s32 index, u8 phase, f64 x, f64 y) { (void)userdata; (void)index; (void)phase; (void)x; (void)y; return false; } static bool nop_scroll_callback(void *userdata, f64 y) { (void)userdata; (void)y; return false; } static bool nop_mouse_button_callback(void *userdata, u8 state, u8 button) { (void)userdata; (void)state; (void)button; return false; } static bool nop_key_callback(void *userdata, u8 state, u16 button) { (void)userdata; (void)state; (void)button; return false; } static bool nop_key_immediate_callback(void *userdata, u8 state, u16 button) { (void)userdata; (void)state; (void)button; return false; } static void window_default_close(struct stl_window *window) { window->closed = true; } void stl_window_init(struct stl_window *window) { window->pointer_pos_callback = nop_pointer_pos_callback; window->touch_callback = nop_touch_callback; window->scroll_callback = nop_scroll_callback; window->mouse_button_callback = nop_mouse_button_callback; window->key_callback = nop_key_callback; window->key_immediate_callback = nop_key_immediate_callback; window->close = window_default_close; #ifdef STELA_EVENT_BUFFER al_ring_buffer_init(&window->events, window->data, STELA_EVENTS_SIZE); #endif window->closed = false; window->held_modifiers = 0; #ifdef STELA_PAUSE window->pending_refresh = true; #endif } void stl_window_created(struct stl_window *window, u32 width, u32 height, u32 flags) { window->width = width; window->height = height; window->flags = flags; } void stl_window_send_render_event(struct stl_window *window) { if (window->render_callback) { window->render_callback(window->userdata); } } #ifdef STELA_EVENT_BUFFER #define GET_EVENT_CHUNK(op) \ ptrdiff_t size; \ u8 *ptr = al_ring_buffer_write_chunk(&window->events, &size); \ if (size < STELA_EVENT_SIZE) { \ log_warn("Buffer full, discarding event."); \ return; \ } \ ptr[0] = op #endif #define APPEND_EVENT_CHUNK() \ al_ring_buffer_append(&window->events, ptr, STELA_EVENT_SIZE) void stl_window_send_resize_event(struct stl_window *window, u32 width, u32 height) { al_assert(width == window->width && height == window->height); #ifdef STELA_EVENT_BUFFER window->pending_resize = true; #else window->resize_internal(window, width, height); if (window->resize_callback) { window->resize_callback(window->userdata, width, height); } #ifdef STELA_PAUSE window->pending_refresh = true; #endif #endif } void stl_window_send_pointer_pos_event(struct stl_window *window, f64 x, f64 y) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_POINTER_POS); ((f64 *)(&ptr[1]))[0] = x; ((f64 *)(&ptr[9]))[0] = y; APPEND_EVENT_CHUNK(); #else #ifdef STELA_PAUSE window->pending_refresh |= #endif window->pointer_pos_callback(window->userdata, x, y); #endif } void stl_window_send_touch_event(struct stl_window *window, s32 index, u8 phase, f64 x, f64 y) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_TOUCH); ((s32 *)(&ptr[1]))[0] = index; ptr[5] = phase; ((f64 *)(&ptr[6]))[0] = x; ((f64 *)(&ptr[14]))[0] = y; APPEND_EVENT_CHUNK(); #else #ifdef STELA_PAUSE window->pending_refresh |= #endif window->touch_callback(window->userdata, index, phase, x, y); #endif } void stl_window_send_scroll_event(struct stl_window *window, f64 y) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_SCROLL); ((f64 *)(&ptr[1]))[0] = y; APPEND_EVENT_CHUNK(); #else #ifdef STELA_PAUSE window->pending_refresh |= #endif window->scroll_callback(window->userdata, y); #endif } void stl_window_send_mouse_button_event(struct stl_window *window, u8 state, u8 button) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_MOUSE_BUTTON); ptr[1] = state; ptr[2] = button; APPEND_EVENT_CHUNK(); #else window->held_mouse_buttons = (window->held_mouse_buttons & ~button) | (button * (state == STELA_BUTTON_PRESSED)); #ifdef STELA_PAUSE window->pending_refresh |= #endif window->mouse_button_callback(window->userdata, state, button); #endif } void stl_window_drop_mouse_buttons(struct stl_window *window) { u8 *buttons = stl_all_mouse_buttons; while (window->held_mouse_buttons) { u8 button = *buttons++; if (window->held_mouse_buttons & button) { stl_window_send_mouse_button_event(window, STELA_BUTTON_RELEASED, button); } } al_assert(!window->held_mouse_buttons); } #ifdef STELA_WINDOW_WIN32 // It's possible for a window to behave in a way that disallows // consistancy between BUTTON_PRESSED and BUTTON_RELEASED events. #define STELA_CORRECT_MODIFIER_INCONSISTENCY #endif void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button) { u32 mod = stl_key_to_mod(button); #ifdef STELA_CORRECT_MODIFIER_INCONSISTENCY if (state == STELA_BUTTON_PRESSED) { if (window->held_modifiers & mod) { window->held_modifiers &= ~mod; window->key_callback(window->userdata, STELA_BUTTON_RELEASED, button); } } else { if (!(window->held_modifiers & mod)) return; } #endif window->held_modifiers = (window->held_modifiers & ~mod) | (mod * (state == STELA_BUTTON_PRESSED)); #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_KEY); ptr[1] = state; ((u16 *)(&ptr[2]))[0] = button; APPEND_EVENT_CHUNK(); #else #ifdef STELA_PAUSE window->pending_refresh |= #endif window->key_callback(window->userdata, state, button); #endif } bool stl_window_send_key_immediate_event(struct stl_window *window, u8 state, u16 button) { // Don't send modifiers on key immediate. return window->key_immediate_callback(window->userdata, state, button * (stl_key_to_mod(button) == STELA_MOD_NONE)); } void stl_window_drop_modifiers(struct stl_window *window) { u32 *mods = stl_all_modifiers; while (window->held_modifiers) { u32 mod = *mods++; if (window->held_modifiers & mod) { stl_window_send_key_event(window, STELA_BUTTON_RELEASED, stl_mod_to_key(mod)); } } al_assert(!window->held_modifiers); } void stl_window_send_should_close_event(struct stl_window *window) { #ifdef STELA_EVENT_BUFFER GET_EVENT_CHUNK(STELA_EVENT_SHOULD_CLOSE); APPEND_EVENT_CHUNK(); #else if (window->should_close_callback) { window->should_close_callback(window->userdata); } #endif } #ifdef STELA_EVENT_BUFFER bool stl_window_read_events(struct stl_window *window) { u8 op = STELA_EVENT_NONE; if (window->pending_resize) { op = STELA_EVENT_RESIZE; window->resize_internal(window, window->width, window->height); if (window->resize_callback) { window->resize_callback(window->userdata, window->width, window->height); } #ifdef STELA_PAUSE window->pending_refresh = true; #endif window->pending_resize = false; } ptrdiff_t size; u8 *ptr = al_ring_buffer_read_chunk(&window->events, &size); u8 *bc = ptr; while (bc - ptr < size) { op = *bc; switch (op) { case STELA_EVENT_POINTER_POS: { f64 x = ((f64 *)(&bc[1]))[0]; f64 y = ((f64 *)(&bc[9]))[0]; #ifdef STELA_PAUSE window->pending_refresh |= #endif window->pointer_pos_callback(window->userdata, x, y); break; } case STELA_EVENT_TOUCH: { s32 index = ((s32 *)(&bc[1]))[0]; u8 phase = bc[5]; f64 x = ((f64 *)(&bc[6]))[0]; f64 y = ((f64 *)(&bc[14]))[0]; #ifdef STELA_PAUSE window->pending_refresh |= #endif window->touch_callback(window->userdata, index, phase, x, y); break; } case STELA_EVENT_SCROLL: { f64 y = ((f64 *)(&bc[1]))[0]; #ifdef STELA_PAUSE window->pending_refresh |= #endif window->scroll_callback(window->userdata, y); break; } case STELA_EVENT_MOUSE_BUTTON: { u8 state = bc[1]; u8 button = bc[2]; #ifdef STELA_PAUSE window->pending_refresh |= #endif window->mouse_button_callback(window->userdata, state, button); break; } case STELA_EVENT_KEY: { u8 state = bc[1]; u16 button = ((u16 *)(&bc[2]))[0]; #ifdef STELA_PAUSE window->pending_refresh |= #endif window->key_callback(window->userdata, state, button); break; } case STELA_EVENT_SHOULD_CLOSE: { if (window->should_close_callback) { window->should_close_callback(window->userdata); } break; } default: break; } bc += STELA_EVENT_SIZE; } al_assert(bc - ptr == size); al_ring_buffer_consume(&window->events, ptr, size); return op != STELA_EVENT_NONE; } #endif